Reputation: 14770
I have the following kind of method definition:
method_name = :foo
method_arguments = [:bar, :baz]
method_mandatory_arguments = {:quux => true}
method_body = ->{ quux ? bar + baz : bar - baz }
So I want to get a real method. But define_method has no any possibility to define method arguments dynamically. I know another way to use class_eval but I know than defining methods with class_eval is much slower than define_method. How I can effectively archive this?
I did some benchmarks in rails console:
class Foo; end
n = 100_000
Benchmark.bm do |x|
x.report('define_method') do
n.times { |i| Foo.send(:define_method, "method1#{i}", Proc.new { |a, b, c| a + b + c }) }
end
x.report('class_eval') do
n.times { |i| Foo.class_eval %Q{ def method2#{i}(a, b, c); a + b + c; end } }
end
end
So I've got the following results:
user system total real
define_method 0.750000 0.040000 0.790000 ( 0.782988)
class_eval 9.510000 0.070000 9.580000 ( 9.580577)
Upvotes: 4
Views: 2829
Reputation: 20639
There is no simple way to implement what you are asking using either class_eval or define_method. Since method_body is a lambda it can only access local variables defined right before it.
method_body = ->{ quux ? bar + baz : bar - baz }
quux = true
method_body.call # undefined local variable or method ‘quux’
quux = true
method_body = ->{ quux ? bar + baz : bar - baz }
method_body.call # undefined local variable or method ‘bar’
I would suggest you to revise your requirements. If your method body should be a lambda, define all arguments for it. Then this is a natural fit for define_method:
method_body = ->(quux, bar = 0, baz = 0){ quux ? bar + baz : bar - baz }
define_method(method_name, &method_body)
If your method body can be a string, eval is the only option:
method_body = "quux ? bar + baz : bar - baz"
eval <<RUBY
def #{method_name} #{arguments}
#{method_body}
end
RUBY
Upvotes: 3