Bound and Determined
As part of a generic filtering mechanism I've been working on for Active Record, I came across a case where I needed to get the arity
of a Proc
, only the Proc
was stored inside of another Proc
. I settled upon a solution involving eval
and the outer Proc
's binding:
arity = eval('options.arity', @scope.binding) rescue nil
All was well and good, and I was proud of myself for being so clever. Then it came time to spec this out (shouldn't that have come first?), but I had no clue how to proceed. I first tried stubbing the eval
call.
@filter.stub!(:eval).and_return(-1)
This works to an extent, but the scope binding also has to be stubbed, otherwise the @scope.binding
call errors out and the rescue
kicks in and set arity
to nil
.
@scope.stub!(:binding).and_return(nil) @filter.stub!(:eval).and_return(-1)
But at this point, I felt it would be better to do things properly in order to really get to the heart of the code. This means properly stubbing the binding. Needless to say, I went through several iterations before stumbling upon one that seemed to do the trick.
@arity = lambda { |i| options = Struct.new(:arity).new(i) ; binding } @scope.stub!(:binding).and_return(@arity.call(-1))
This lets our eval
return the desired value, and the stub is easily overridden in any examples which require a value other than -1
.
I will say that my big stumbling block was thinking that the following are equivalent:
lambda { }.send(:binding) lambda { binding }.call
The former won't get us very far, as it's not the binding for which we're looking. The latter, however, gets us exactly where we need to be to perform our magic.
For more information, see Variable Bindings in Ruby.