2013-04-07 5 views
2

У меня есть следующий вид определения метода:Рубиновые динамические аргументы в динамически создаваемых методах

method_name = :foo 
method_arguments = [:bar, :baz] 
method_mandatory_arguments = {:quux => true} 
method_body = ->{ quux ? bar + baz : bar - baz } 

Так что я хочу, чтобы получить реальный метод. Но define_method не имеет возможности динамически определять аргументы метода. Я знаю другой способ использовать class_eval, но я знаю, чем определять методы с class_eval намного медленнее, чем define_method. Как я могу эффективно архивировать это?

Я сделал несколько тестов на рельсах консоли:

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 

Таким образом, я получил следующие результаты:

   user  system  total  real 
define_method 0.750000 0.040000 0.790000 ( 0.782988) 
class_eval  9.510000 0.070000 9.580000 ( 9.580577) 
+1

'class_eval' имеет смысл для меня. Является ли производительность действительно проблемой? Трудно представить себе, где это будет. –

+1

Возможно, ваш блок define_method принимает varargs, а затем интерпретирует массив args на основе ваших метаданных аргументов, захваченных в закрытии. – dbenhur

+0

@dbenhur, нормально, но в этом случае на самом деле у меня есть один метод, который реализует многополярное поведение, поэтому я должен реализовывать поведение основных аргументов вручную (присутствие аргументов, аргументы по умолчанию и т. Д.). Это то, чего я пытаюсь избежать. – freemanoid

ответ

0

Там нет простого способа осуществить то, что вы просите, используя либо class_eval или define_method. Поскольку method_body - это лямбда, он может обращаться только к локальным переменным, определенным перед ним.

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’ 

Я предлагаю вам изменить ваши требования. Если тело метода должно быть лямбдой, определите все аргументы для него. Тогда это является естественным для define_method:

method_body = ->(quux, bar = 0, baz = 0){ quux ? bar + baz : bar - baz } 
define_method(method_name, &method_body) 

Если тело метода может быть строка, Eval является единственным вариантом:

method_body = "quux ? bar + baz : bar - baz" 
eval <<RUBY 
def #{method_name} #{arguments} 
    #{method_body} 
end 
RUBY 
+0

С вашей точки зрения, я хочу создать proc (или лямбду, которая не имеет значения в моем случае) с набором аргументов, которые я храню в определенной форме. Итак, у меня есть массив '' 'method_arguments = [: bar,: baz]' '', и я хочу использовать его следующим образом: '' 'method_body = -> (* method_arguments) {quux? bar + baz: bar - baz} '' ' – freemanoid

+1

Это невозможно с procs/lambdas. Можете ли вы сохранить тело метода как строку? Это поможет, если вы дадите более конкретный пример реального мира. –

+0

Кажется, что единственный способ - использовать class_eval. – freemanoid

Смежные вопросы