Revisiting rebinding self on lambdas
In my previous post on the topic I determined the best method would be to pass the current self into the lambda:
fun = -> context {context.thing}
fun.call(self)
But in a recent project I revisited the problem and discovered you can do this:
class Deal
def emit_a_funky(funk)
#returns a lambda which takes a name argument and calls the lambda
#passed in with the name argument and self rebound to be an instance of Deal.
-> name do
self.instance_exec(name, &funk)
end
end
def doathing(name)
p "hey #{name} I'm doin a thing"
end
end
a = -> name do
doathing(name)
end
b = Deal.new
c = b.emit_a_funky(a)
c.call 'max'
# prints "hey max I'm doin a thing"
c.call 'Hulk Hogan'
# prints "hey Hulk Hogan I'm doin a thing"
The actual use case I had for it was writing an RSpec helper that takes a list of user authentication levels and a block and spits out a seperate example for each user level. The examples evaluate the block in the context of the RSpec example and make an assertion on the block’s return value. It came in handy for DRYing up my specs:
def assert_granted(*args, &blk)
if args.last.instance_of?(Hash)
options = args.pop
options = Hash[options.collect {|option,value| [option.to_sym, value]}]
else
options = {:granted => true}
end
if args.length == 0
args = [:user, :fulfillment_admin, :engine_admin, :admin]
end
message = options[:granted] ? 'is' : 'is not'
args.each do |name|
it "#{message} granted for #{name}" do
auth_level = User.auth_levels.invert[name.to_sym]
user = stub_grant_current_user(auth_level)
assert_grant_error(!options[:granted]) do
#rebind self for blk
self.instance_exec(user, &blk)
end
end
end
end
def assert_grant_error(raises = true, &blk)
assertion = raises ? 'should' : 'should_not'
blk.send(assertion, raise_error(Grant::Error))
end
There are still instances where passing the current self as an argument to a lambda may be useful, but this makes it unnecessary in cases where you don’t need access to both contexts.




