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.
On Aug 26, 2008, at 8:30 AM, James C. wrote:
ampersands:
module_eval a string.
a @ http://codeforpeople.com/
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
Hi,
In message “Re: Passing a block with define_method”
on Tue, 26 Aug 2008 23:30:21 +0900, “James C.”
[email protected] 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.
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 
- 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
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
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
Hi –
On Wed, 27 Aug 2008, David A. Black wrote:
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 M.'s patch, if I’m not
mistaken).
David
On Aug 26, 2008, at 9:12 AM, Emmanuel O. wrote:
I think I saw this “trick” in one of Ara’s posts 
- 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/
On Aug 26, 2008, at 9:03 AM, James C. wrote:
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/
On Aug 26, 2008, at 8:30 AM, James C. wrote:
ampersands:
–
James C.
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/
Hi –
On Wed, 27 Aug 2008, James C. wrote:
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
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
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