Passing a block with define_method

J

James Coglan

[Note: parts of this message were removed to make it a legal post.]

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.
 
A

ara.t.howard

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.

module_eval a string.

a @ http://codeforpeople.com/
 
J

James Coglan

[Note: parts of this message were removed to make it a legal post.]
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
 
J

James Coglan

[Note: parts of this message were removed to make it a legal post.]
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
 
E

Emmanuel Oga

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

* 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
 
D

David A. Black

Hi --

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:
In block
10

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


David
 
E

Emmanuel Oga

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...
 
D

David A. Black

Hi --

Hi --



In Ruby 1.9 you can do this:

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 ->(){}.
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
 
A

ara.t.howard

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

* 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

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/
 
J

James Coglan

[Note: parts of this message were removed to make it a legal post.]

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
 
A

ara.t.howard

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


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/
 
A

ara.t.howard

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.


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/
 
S

steve

[Note: parts of this message were removed to make it a legal post.]

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 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.


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/
 

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,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top