calling blocks with arguments from other contexts

M

Martin

Hi,

I'm having trouble with Procs.

The following piece of code works only if I use "x.green" rather than just
plain "green"

class Foo
def red(&p)
@z = p
end

def blue(y)
instance_eval{@z.call y}
end

def green
puts "Yeah!"
end
end

x = Foo.new

x.red do |y|
green # I don't want to use x.green
puts y
end

x.blue 5
The following code works. It is able to call the "green" method from inside
the Foo class without being able to prefix it with "x.", but I can't handle
any arguments like in the above code:

class Foo
def red(&p)
@z = p
end

def blue
instance_eval(&@z)
end

def green
puts "Yeah!"
end
end

x = Foo.new

x.red do
green
end

x.blue

My main goal is to be able to refer to the "green" in the closure "x.red do
green end" without using "x.green" and also be able handle arguments at the
same time like in the first piece of code, but as you know, the first piece
of code doesn't work.


Is there any way to achieve this?
 
E

Eric Mahurin

--- Martin said:
Hi,
=20
I'm having trouble with Procs.
=20
The following piece of code works only if I use "x.green"
rather than just
plain "green"
=20
class Foo
def red(&p)
@z =3D p
end
=20
def blue(y)
instance_eval{@z.call y}
end
=20
def green
puts "Yeah!"
end
end
=20
x =3D Foo.new
=20
x.red do |y|
green # I don't want to use x.green
puts y
end
=20
x.blue 5
The following code works. It is able to call the "green"
method from inside
the Foo class without being able to prefix it with "x.", but
I can't handle
any arguments like in the above code:
=20
class Foo
def red(&p)
@z =3D p
end
=20
def blue
instance_eval(&@z)
end
=20
def green
puts "Yeah!"
end
end
=20
x =3D Foo.new
=20
x.red do
green
end
=20
x.blue
=20
My main goal is to be able to refer to the "green" in the
closure "x.red do
green end" without using "x.green" and also be able handle
arguments at the
same time like in the first piece of code, but as you know,
the first piece
of code doesn't work.
=20
=20
Is there any way to achieve this?

I don't think so. I think there was a thread about adding an
argument passing instance_eval type method and matz was asking
for suggestions of method names.

What's so bad about using a receiver for Foo#green? Using
instance_eval can be dangerous because you bypass all
protections/encapsulation. It also can get confusing because
the block only has access to local variables in the defining
context and not methods, instance variables, etc like other
blocks have access to.

Personally, I think it is better to pass in self as an argument
rather than use instance_eval so casually. I usually use
instance_eval only when I have to - to bypass proctections.



=09
=09
__________________________________=20
Yahoo! Mail - PC Magazine Editors' Choice 2005=20
http://mail.yahoo.com
 
S

Sean O'Halpin

This isn't quite what you asked for but it achieves the same effect:

class Foo
def red(&p)
self.class.send:)define_method, :z, &p)
end

def blue(y)
puts y
z(y)
end

def green
puts "Yeah!"
end
end

x =3D Foo.new

x.red do |y|
green
puts y
end

x.blue 5
__END__
5
Yeah!
5

Regards,

Sean
 
M

Martin

Now that is a super cool ruby hack. Thank you.

I was able to mold it to the way I wanted it. Now it can temporarily define
a method, call the method with arguments and then undefine the temporary
method. I'm guessing this comes with a cost? slowness?

class Foo
def red(&p)
@z = p
end

def blue(y)
Foo.send:)define_method, :blue_temp, @z)
blue_temp(y)
Foo.send:)undef_method, :blue_temp)
end

def green
puts "Yeah!"
end
end

x = Foo.new

x.red do |y|
green
puts y
end

x.blue 5














This isn't quite what you asked for but it achieves the same effect:

class Foo
def red(&p)
self.class.send:)define_method, :z, &p)
end

def blue(y)
puts y
z(y)
end

def green
puts "Yeah!"
end
end

x = Foo.new

x.red do |y|
green
puts y
end

x.blue 5
__END__
5
Yeah!
5

Regards,

Sean
 
A

Ara.T.Howard

Now that is a super cool ruby hack. Thank you.

I was able to mold it to the way I wanted it. Now it can temporarily define
a method, call the method with arguments and then undefine the temporary
method. I'm guessing this comes with a cost? slowness?

class Foo
def red(&p)
@z = p
end

def blue(y)
Foo.send:)define_method, :blue_temp, @z)
blue_temp(y)
Foo.send:)undef_method, :blue_temp)
end

def green
puts "Yeah!"
end
end

x = Foo.new

x.red do |y|
green
puts y
end

x.blue 5

fyi. i posted this code a while back, it does the above in a thread-safe way:

harp:~ > cat a.rb
class Object
def evaluate(*a, &b)
ret, sent = nil
loop do
m = "____evaluate____#{ Thread::current.object_id }____#{ rand 666 }____#{ rand 42 }____"
klass = Class === self ? self : self::class
begin
klass.module_eval{ define_method m, &b }
ret = send(sent = m, *a)
ensure
begin
klass.module_eval{ remove_method m }
ensure
break if sent
end
end
end
ret
end
end

class Foo
attr_accessor "rb"
def red &b
self.rb = b
end
def blue y
evaluate y, &rb
end
def green
puts "green"
end
end

foo = Foo::new

foo.red do |y|
green
puts y
end

foo.blue 5



harp:~ > ruby a.rb
green
5


and yes, it's probaly slow.

cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze. --Nagarjuna
===============================================================================
 
S

Sean O'Halpin

This isn't quite what you asked for but it achieves the same effect:

class Foo
def red(&p)
self.class.send:)define_method, :z, &p)
end

def blue(y)
puts y
z(y)
end

def green
puts "Yeah!"
end
end

x =3D Foo.new

x.red do |y|
green
puts y
end

x.blue 5
__END__
5
Yeah!
5

Regards,

Sean

Scratch that - it defines a method on the class not the object so all
Foos would get the z method. Hmm.. I'll have to think about this
(should have done that before I posted ;)

Sean
 
S

Sean O'Halpin

What do you think of the singleton method version? That doesn't need
to be thread safe in the same way (no more than any concurrent access
to the same object)

Regards,

Sean
 
S

Sean O'Halpin

Hi,

This is better - it defines the z method on the singleton class of the
instance object.

--- CODE ---
class Foo
def singleton
class<<self; self; end
end

def red(&block)
singleton.send:)define_method, :z, &block)
end

def blue(y)
z(y)
end

def green
puts "Yeah!"
end

end

x =3D Foo.new

x.red do |y|
green
puts y
end

x.blue 5

a =3D Foo.new
a.blue 42
# we want this to cause the exception below (because no red block defined)

__END__

Yeah!
5
undefined method `z' for #<Foo:0x2870b38> (NoMethodError)

--- END OF CODE ---

Regards,

Sean
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top