Passing a block with define_method

Hi list,

I've got a bit of a problem whereby I need to pass a block to a method
defined using define_method this works:

def some_method(*args, &block)
  # ...
end

but this does not -- the interpreter complains about unexpected ampersands:

define_method(:some_method) do |*args, &block|
  # ...
end

Is there any way to make this work? I'm doing something a little meta, so I
don't know the name of the method or the number of arguments, so it's all
got to be dynamic.

···

--
James Coglan

module_eval a string.

a @ http://codeforpeople.com/

···

On Aug 26, 2008, at 8:30 AM, James Coglan wrote:

Hi list,

I've got a bit of a problem whereby I need to pass a block to a method
defined using define_method this works:

def some_method(*args, &block)
# ...
end

but this does not -- the interpreter complains about unexpected ampersands:

define_method(:some_method) do |*args, &block|
# ...
end

Is there any way to make this work? I'm doing something a little meta, so I
don't know the name of the method or the number of arguments, so it's all
got to be dynamic.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

Hi,

···

In message "Re: Passing a block with define_method" on Tue, 26 Aug 2008 23:30:21 +0900, "James Coglan" <jcoglan@googlemail.com> writes:

but this does not -- the interpreter complains about unexpected ampersands:

define_method(:some_method) do |*args, &block|
# ...
end

You have to wait until 1.9.

              matz.

a working example:

cfp:~ > cat a.rb
module Continuations
  Funcs = {}
  def Continuations.funcs() Funcs end

  def accepts_continuation *argv
    argv.flatten.compact.each do |arg|
      arg = arg.to_s
      klass = Class === self ? self : self.class
      Funcs[arg] = klass.send(:instance_method, arg)
      klass.module_eval <<-code
        def #{ arg }(*a, &b)
          Continuations.funcs[#{ arg.inspect }].bind(self).call(*a, &b)
        ensure
          STDERR.puts 'wrapped!'
        end
      code
    end
  end
end

def foo &b
   b.call 'bar'
end

foo{|bar| puts "foo#{ bar }"}

include Continuations
accepts_continuation :foo

foo{|bar| puts "foo#{ bar }"}

cfp:~ > ruby a.rb
foobar
wrapped!

a @ http://codeforpeople.com/

···

On Aug 26, 2008, at 8:30 AM, James Coglan wrote:

Hi list,

I've got a bit of a problem whereby I need to pass a block to a method
defined using define_method this works:

def some_method(*args, &block)
# ...
end

but this does not -- the interpreter complains about unexpected ampersands:

define_method(:some_method) do |*args, &block|
# ...
end

Is there any way to make this work? I'm doing something a little meta, so I
don't know the name of the method or the number of arguments, so it's all
got to be dynamic.

--
James Coglan

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

module_eval a string.

I'm actually wrapping existing methods so I need to use define_method to
retain access to the old method. Here's my (broken) code so far:

module Continuations
  def accepts_continuation(*args)
    args.each do |method_name|
      func = instance_method(method_name)
      define_method(method_name) do |*params, &block|
        value = func.bind(self).call(*params)
      end
    end
  end
end

here's another way to do it:

    m = instance_method(method_name)

    define_method "__call__#{method_name}" do |b, *a|
      begin
        m.bind(self).call(*a, &b)
      ensure
        p :WRAPPED!
      end
    end

    eval <<-EOF
      def #{method_name} *a, &b
        __call__#{method_name}(b, *a)
      end
    EOF

- steve

···

On Tue, Aug 26, 2008 at 10:28 PM, ara.t.howard <ara.t.howard@gmail.com>wrote:

On Aug 26, 2008, at 8:30 AM, James Coglan wrote:

Hi list,

I've got a bit of a problem whereby I need to pass a block to a method
defined using define_method this works:

def some_method(*args, &block)
# ...
end

but this does not -- the interpreter complains about unexpected
ampersands:

define_method(:some_method) do |*args, &block|
# ...
end

Is there any way to make this work? I'm doing something a little meta, so
I
don't know the name of the method or the number of arguments, so it's all
got to be dynamic.

--
James Coglan

a working example:

cfp:~ > cat a.rb
module Continuations
Funcs = {}
def Continuations.funcs() Funcs end

def accepts_continuation *argv
  argv.flatten.compact.each do |arg|
    arg = arg.to_s
    klass = Class === self ? self : self.class
    Funcs[arg] = klass.send(:instance_method, arg)
    klass.module_eval <<-code
      def #{ arg }(*a, &b)
        Continuations.funcs[#{ arg.inspect }].bind(self).call(*a, &b)
      ensure
        STDERR.puts 'wrapped!'
      end
    code
  end
end
end

def foo &b
b.call 'bar'
end

foo{|bar| puts "foo#{ bar }"}

include Continuations
accepts_continuation :foo

foo{|bar| puts "foo#{ bar }"}

cfp:~ > ruby a.rb
foobar
foobar
wrapped!

a @ http://codeforpeople.com/
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama

I'm actually wrapping existing methods so I need to use define_method to
retain access to the old method. Here's my (broken) code so far:

module Continuations
def accepts_continuation(*args)
   args.each do |method_name|
     func = instance_method(method_name)
     define_method(method_name) do |*params, &block|
       value = func.bind(self).call(*params)
     end
   end
end
end

To clarify, this formulation does not work:

module Continuations
  def accepts_continuation(*args)
    args.each do |method_name|
      func = instance_method(method_name)
      module_eval <<-EOS
        def #{method_name}(*params, &block)
          value = func.bind(self).call(params)
        end
      EOS
    end
  end
end

I'm actually wrapping existing methods so I need to use define_method to
retain access to the old method. Here's my (broken) code so far:

I think I saw this "trick" in one of Ara's posts :smiley:

* Store the method somewhere so it is not GC by ruby
* Retrieve the method object from its object_id with ObjectSpace._id2ref
* use eval instead of define_method

module Continuations

    NOGC =

  def accepts_continuation(*args)
    args.each do |method_name|
      func = instance_method(method_name)

        NOGC << func # Stop garbage collection of the method.
        func_object_id = func.object_id

      #define_method(method_name) do |*params, &block|
      # value = func.bind(self).call(*params)
      #end

        eval(<<-CODE, __FILE__, __LINE__)
          def #{ method_name }(*params, &block)
            func = ObjectSpace._id2ref(#{ func_object_id })
            func.bind(self).call(*params, &block)
          end
        CODE

···

    end
  end
end

--
Posted via http://www.ruby-forum.com/\.

Hi --

module_eval a string.

I'm actually wrapping existing methods so I need to use define_method to
retain access to the old method. Here's my (broken) code so far:

module Continuations
def accepts_continuation(*args)
   args.each do |method_name|
     func = instance_method(method_name)
     define_method(method_name) do |*params, &block|
       value = func.bind(self).call(*params)
     end
   end
end
end

In Ruby 1.9 you can do this:

func = ->(*a,&block) { block.call if block; puts a }

=> #<Proc:0x388bdc@(irb):1 (lambda)>

C = Class.new { define_method(:hi, &func) }

=> C

C.new.hi(10) { puts "In block" }

In block
10

The ->(){} thing lets you create a lambda with method-argument
semantics.

David

···

On Wed, 27 Aug 2008, James Coglan wrote:

--
Rails training from David A. Black and Ruby Power and Light:
   Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
   Advancing with Rails January 19-22 Fort Lauderdale, FL
See http://www.rubypal.com for details and updates!

off the top of my head (un-tested but similar will work)

module Continuations

   Funcs = {}

   def Continuations.funcs() Funcs end

   def accepts_continuation *argv

     argv.each do |arg|

       arg = arg.to_s

       Funcs[arg] = instance_method(arg)

       module_eval <<-code
         def #{ arg }(*a, &b)
           Contiuations.funcs.bind(self).call(*a, &b
         end
       code
     end
   end
end

a @ http://codeforpeople.com/

···

On Aug 26, 2008, at 9:03 AM, James Coglan wrote:

module_eval a string.

I'm actually wrapping existing methods so I need to use define_method to
retain access to the old method. Here's my (broken) code so far:

module Continuations
def accepts_continuation(*args)
   args.each do |method_name|
     func = instance_method(method_name)
     define_method(method_name) do |*params, &block|
       value = func.bind(self).call(*params)
     end
   end
end
end

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

To clarify, this formulation does not work:

module Continuations
  def accepts_continuation(*args)
    args.each do |method_name|
      func = instance_method(method_name)
      module_eval <<-EOS
        def #{method_name}(*params, &block)

          value = func.bind(self).call(params)

            where does the "func" method come from here? because you are
using module_eval, this is just calling a method named "func" on the
target class (the class where you included the module). I don't think
this would do what you want, even if you do have a "func" method lying
around...

···

        end
      EOS
    end
  end
end

--
Posted via http://www.ruby-forum.com/\.

Hi --

Hi --

module_eval a string.

I'm actually wrapping existing methods so I need to use define_method to
retain access to the old method. Here's my (broken) code so far:

module Continuations
def accepts_continuation(*args)
   args.each do |method_name|
     func = instance_method(method_name)
     define_method(method_name) do |*params, &block|
       value = func.bind(self).call(*params)
     end
   end
end
end

In Ruby 1.9 you can do this:

func = ->(*a,&block) { block.call if block; puts a }

=> #<Proc:0x388bdc@(irb):1 (lambda)>

C = Class.new { define_method(:hi, &func) }

=> C

C.new.hi(10) { puts "In block" }

In block
10

The ->(){} thing lets you create a lambda with method-argument
semantics.

Actually the fix is in: you can now (and I think permanently) do this
in 1.9 without the ->(){}.

class C; define_method(:hi) {|*a,&block| block.call if block; puts
a }; end

=> #<Proc:0x1d5754@(irb):1 (lambda)>

C.new.hi(10) { puts "In block" }

In block
10

I'd lost track of where this stood -- I'm very glad to see that it's
there in the block syntax (thanks to Eric Mahurin's patch, if I'm not
mistaken).

David

···

On Wed, 27 Aug 2008, David A. Black wrote:

On Wed, 27 Aug 2008, James Coglan wrote:

--
Rails training from David A. Black and Ruby Power and Light:
   Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
   Advancing with Rails January 19-22 Fort Lauderdale, FL
See http://www.rubypal.com for details and updates!

that's correct. you just need to store the data/block somewhere whcih won't be GC'd and pull it back using the id

a @ http://codeforpeople.com/

···

On Aug 26, 2008, at 9:12 AM, Emmanuel Oga wrote:

I think I saw this "trick" in one of Ara's posts :smiley:

* Store the method somewhere so it is not GC by ruby
* Retrieve the method object from its object_id with ObjectSpace._id2ref
* use eval instead of define_method

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

Just did something similar to Emmanuel's suggestion, works a treat:

module Continuations
  METHODS = {}

  def accepts_continuation(*args)
    args.each do |method_name|
      func = instance_method(method_name)
      id = func.object_id
      METHODS[id] = func

      module_eval <<-EOS
        def #{method_name}(*params, &block)
          value = Continuations::METHODS[#{id}].bind(self).call(*params)
          block.call(value) if block
          value
        end
      EOS
    end
  end
end