[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Block Trouble (Binding Issue?

Pieter V.

5/19/2007 7:39:00 PM

I'm presently trying to work on a little metaprogramming project, but
am finding my efforts a little frustrated by ... well, I'm not rightly
sure what by. It seems to be a scoping issue, from what I can tell.

Here's a sample case:

############
class BlockTester
def x
"Correct Method!"
end

def self.x
"WRONG METHOD!"
end

def self.meth(name, &b)
# ... do some stuff
define_method name, &b
end

def self.normal_meth(name, &b)
meth(name, &b)
end

def self.special_meth(name, &b)
meth(name) do
# do something special
yield # <= This is the problematic call
end
end
end

# Test Case

class Foo < BlockTester
meth "foo" do x end
normal_meth "bar" do x end
special_meth "baz" do x end
end

f = Foo.new
f.foo # => "Correct Method!"
f.bar # => "Correct Method!"
f.baz # => "WRONG METHOD!"
############

The goal, as may not be immediately apparent from the code, is for
"special_meth" to be able to create a new method that automatically
executes a few lines of code before executing the block. While this
is similar in principle to overriding a method in a subclass...

############
class X
def to_s
"green" + super
end
end
############

... I can't seem to make this very common pattern stick for a block.
Any suggestions?

3 Answers

Joel VanderWerf

5/19/2007 8:36:00 PM

0

Pieter V. wrote:
> I'm presently trying to work on a little metaprogramming project, but
> am finding my efforts a little frustrated by ... well, I'm not rightly
> sure what by. It seems to be a scoping issue, from what I can tell.

Yup, it sure is. define_method(name, &block) changes the scope of block
to the instance, but the yield still references a block in class Foo scope.

A fix might be to replace yield with instance_eval(&b), as below, but
that will preclude passing arguments to the block, in case you ever need
to do that. Google for instance_exec for a discussion, hacks for the
present, and hope for the future (1.9 has it)...

> def self.special_meth(name, &b)
> meth(name) do
> # do something special
> yield # <= This is the problematic call
instance_eval(&b)
> end
> end
> end

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Pieter V.

5/19/2007 9:02:00 PM

0

Arg. Thanks. I would have sworn I tried that at some point. Works
like a charm.

On 5/19/07, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:
> Pieter V. wrote:
> > I'm presently trying to work on a little metaprogramming project, but
> > am finding my efforts a little frustrated by ... well, I'm not rightly
> > sure what by. It seems to be a scoping issue, from what I can tell.
>
> Yup, it sure is. define_method(name, &block) changes the scope of block
> to the instance, but the yield still references a block in class Foo scope.
>
> A fix might be to replace yield with instance_eval(&b), as below, but
> that will preclude passing arguments to the block, in case you ever need
> to do that. Google for instance_exec for a discussion, hacks for the
> present, and hope for the future (1.9 has it)...
>
> > def self.special_meth(name, &b)
> > meth(name) do
> > # do something special
> > yield # <= This is the problematic call
> instance_eval(&b)
> > end
> > end
> > end
>
> --
> vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
>
>

Robert Dober

5/19/2007 9:23:00 PM

0

On 5/19/07, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:
> Pieter V. wrote:
> > I'm presently trying to work on a little metaprogramming project, but
> > am finding my efforts a little frustrated by ... well, I'm not rightly
> > sure what by. It seems to be a scoping issue, from what I can tell.
>
> Yup, it sure is. define_method(name, &block) changes the scope of block
> to the instance, but the yield still references a block in class Foo scope.
>
> A fix might be to replace yield with instance_eval(&b), as below, but
> that will preclude passing arguments to the block, in case you ever need
> to do that. Google for instance_exec for a discussion, hacks for the
> present, and hope for the future (1.9 has it)...

As we just had a discussion about it maybe you might be interested in
this, it comes from Mauricio Fernandez' Eigenclass. Hopefully he does
not mind that I post this slightly modified version. The original can
be found here http://eigenclass.org/hiki/bounded+space+ins...
there is very good stuff over there.
Especially as you are interested in metaprogramming.
All I added to Mauricio's code is to remove the temporary method
*before* calling it, thus avoiding a theoretical possibility of
endless recursion in the temporary method call.

class Object
include InstanceExecHelper = Module.new
def instance_exec(*args, &block)
begin
old_critical, Thread.critical = Thread.critical, true
n = 0
n += 1 while respond_to?(mname="__instance_exec#{n}")
InstanceExecHelper.module_eval{ define_method(mname, &block) }
mthd = method(mname)
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
ensure
Thread.critical = old_critical
end
mthd.call *args
end
end