Sean O'Halpin
5/3/2008 12:03:00 AM
On Fri, May 2, 2008 at 6:35 AM, John Conti <john@john-conti.com> wrote:
> Hi all,
Hi
>
> I am not sure if my question is relatively obvious or instead arcane.
> Being new to ruby, I am trying out some of the metaprogramming features
> to generate sets of orthogonal methods for decoding various string
> types. I want to provide these methods as mix-ins so many of my classes
> can decode these specialized strings. The methods have lots of
> repetitive code and are easily reduced using lambdas (think DRY).
>
> However, all has not gone as I expected. This works:
[snip code]
>
> This does not:
It does with the fix below.
>
> module Fix
> def self.doer()
> lambda do
> foo? ? "fix" : "leave"
> end
> end
>
> def self.manage()
> grunt = doer
> lambda do
# use instance_eval(&block) instead of block.call
"Let's " + instance_eval(&grunt) + " it"
> end
> end
>
> module_eval %{define_method :maintenance, manage}
> end
>
> class X
> include Fix
> def foo?; true; end
> end
>
> class Y
> include Fix
> def foo?; false; end
> end
>
> puts X.new.maintenance
> puts Y.new.maintenance
>
Output:
Let's fix it
Let's leave it
>
> Can anyone give me an explanation to help?
>
When you use block.call, the block is evaluated in the context in
which it was defined. In the case of the block defined in #doer this
is the singleton class of the module Fix, which does not have a #foo?
method. Using instance_eval tells Ruby to evaluate the block in the
context of an object instance. Without a specific receiver, this is
the current #self - in this case, the object of the class in which you
have included the module Fix. This does have a #foo? method, so the
block executes.
HTH
Regards,
Sean