I've been trying to dynamically define some instance methods in Ruby 1.9. Here's 开发者_JAVA技巧the code I've been using to try this out:
class Testing
[:one, :two].each do |name|
define_method(name) do
puts __method__
end
end
end
And here's the output:
ruby-1.9.2-p180 :008 > t = Testing.new
=> #<Testing:0x00000100961878>
ruby-1.9.2-p180 :009 > t.one
two
=> nil
ruby-1.9.2-p180 :010 > t.two
two
=> nil
ruby-1.9.2-p180 :011 >
I would expect the result to be one
and two
respectively. If I call define_method
of each one outside of the iteration it works as expected. What am I not understanding here?
Here is one of many examples I saw around online of define_method being called in an iteration. Dynamically defined setter methods using define_method?
What's missing?
Also: Using __method__
isn't critical for me, but it was the best way I could show, that it seems like only the last block sent to define_method
is being used for the defined methods. Maybe that is starting to explain the problem to me, but I still don't understand..
Nice find on the weird behavior. Of all the Rubies I tested, only MRI 1.9.2 demonstrates this.
Ryan Davis has reported the bug on the ruby-core list (referencing this question).
You can use something like this instead of define_method
:
class Testing
[:one, :two].each do |name|
eval <<-EOM
def #{name}
puts __method__
end
EOM
end
end
t = Testing.new
t.one #=> "one"
t.two #=> "two"
The reason this happens is that define_method defines a method in a slightly different way than def does. It has to do with creating anonymous procs and lambdas. What I would suggest is to simply use the method name since you already have it. This should avoid having to search the stack trace for the method name as well, so it should perform better:
class Testing
[:one, :two].each do |name|
define_method name do
"This method's name is #{name}."
end
end
end
Testing.new.one
=> This method's name is one.
Testing.new.two
=> This method's name is two.
To clarify, notice what is returned by the following two statements:
class Testing
define_method :one do
__method__
end
end
=> #<Proc:0x000001009ebfc8@(irb):54 (lambda)>
class Testing
def one
__method__
end
end
=> nil
P.S: There's also a performance difference between using the two formats. You can verify yourself that def is faster than define_method using Benchmark.
精彩评论