Calling blocks with arguments from other contexts

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?

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.

···

--- Martin <yodlep@gmail.com> wrote:

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?

__________________________________
Yahoo! Mail - PC Magazine Editors' Choice 2005

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

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

"Sean O'Halpin" <sean.ohalpin@gmail.com> wrote in message
news:3736dd30510111609t1b096d20rda04361197abc6b8@mail.gmail.com...
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

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 :wink:

Sean

···

On 10/12/05, Sean O'Halpin <sean.ohalpin@gmail.com> wrote:

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

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

···

On Wed, 12 Oct 2005, Martin wrote:

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

--

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

===============================================================================

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 = Foo.new

x.red do |y|
green
puts y
end

x.blue 5

a = 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

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