问题
Is there any way to get hold of the invoked object inside the block thats being called. For instance, is there any way for the blocks to get access to the scope of the method batman
or the class SuperHeros
class SuperHeros
attr_accessor :news
def initialize
@news = []
end
def batman task
puts "Batman: #{task} - done"
yield "feed cat"
@news << task
end
end
cat_woman = lambda do |task|
puts "Cat Woman: #{task} - done"
# invoker.news << task
end
robin = lambda do |task|
puts "Robin: #{task} - done"
# invoker.news << task
end
characters = SuperHeros.new
characters.batman("kick Joker's ass", &cat_woman)
characters.batman("break Bane's bones", &robin)
回答1:
You can use something similar to Instance eval with delegation pattern, used - for example - in Savon gem:
def batman(task, &block)
@original_self = eval('self', block.binding)
puts "Batman: #{task} - done"
instance_exec('feed cat', &block)
@news << task
end
private
def method_missing(method, *args, &block)
if @original_self
@original_self.send(method, *args, &block)
else
super
end
end
In this approach, when you call method (with implicit receiver) inside block passed into batman
method, it's called in the context of SuperHeros
instance. If there is no such method available, the call goes (through method_missing
) to original block self
.
回答2:
The simplest way to get the receiver object inside a block is assigning the object to an instance variable.
This example illustrate more clearly how the lambdas cat_woman and robin can access to attributes of the receiver objects of blocks:
class SuperHeros
attr_accessor :news, :name, :current_task
def initialize(a_name)
@name = a_name
@news = []
end
def batman(task)
puts "Inside the method batman of #{name}: #{task} in progress ..."
@current_task = task
yield
@news << task
end
end
cat_woman = lambda do |extra_task|
puts "cat_woman even #{extra_task} before doing #{@caller_obj.current_task}"
puts "Cat Woman: #{@caller_obj.current_task} - done by #{@caller_obj.name}"
# invoker.news << task
end
robin = lambda do |extra_task|
puts "robin even #{extra_task} before doing #{@caller_obj.current_task}"
puts "Robin: #{@caller_obj.current_task} - done by #{@caller_obj.name}"
end
character_1 = SuperHeros.new('batman_1')
(@caller_obj = character_1).batman("kick Joker's ass") { cat_woman['eats some burger'] }
puts
character_2 = SuperHeros.new('batman_2')
(@caller_obj = character_2).batman("break Bane's bones") { robin['drinks some beer'] }
The output will be:
Inside the method batman of batman_1: kick Joker's ass in progress ...
cat_woman even eats some burger before doing kick Joker's ass
Cat Woman: kick Joker's ass - done by batman_1
Inside the method batman of batman_2: break Bane's bones in progress ...
robin even drinks some beer before doing break Bane's bones
Robin: break Bane's bones - done by batman_2
来源:https://stackoverflow.com/questions/19834086/reference-the-invoking-object-in-the-passed-block-in-ruby