2012-04-19 2 views
0

Хорошо, так что это сводит меня с ума. Точка этого кода заключается в том, что я должен иметь возможность добавлять метод динамически, если он имеет форму object.plusnum, где num - любое число. Я не совсем уверен, как заставить это работать. Это мой лучший выстрел в него до сих пор, но в настоящее время я получаю несколько ошибок.Ошибки метапрограммирования в Ruby

Код:

class Adder 
def initialize(_val) 
    @start_value = _val 
end 

def method_missing(method_name, *args) 
    method = method_name.to_s 
    if method.start_with?("plus") then 
     num = method[4 .. method.length] 
     if (/^[\d]+(\.[\d]+){0,1}$/ === num) then 
      number = Integer(num) 
      self.class_eval("def #{method} return @start_value + #{number} end") 
     else 
      super 
     end 
    else 
     super 
    end 
end 

end 

Ошибка настоящее время я получаю то, что "class_eval" не определено. Я довольно новичок в метапрограммировании и рубине, и это сводит меня с ума.

ответ

2

Я думаю, что вы получили все это неправильно :)

Вызов метода для урожайности первый раз другой результат, чем называть это второй раз, так, вероятно, вам хотел бы, чтобы этот метод вызывался мгновенно после определения. Кроме того, вы используете довольно сложное регулярное выражение, а затем преобразуете значение в Integer и отбрасываете все цифры после точки.

Вы используете class_eval и передаете ему строку, которая обычно плохая идея, и блок следует использовать, когда это возможно, по соображениям безопасности и производительности.

Как я вижу, это может выглядеть:

class Adder 
    def initialize(val) 
    @start_value = val 
    end 

    def method_missing(method_name, *args) 
    if method_name.to_s =~ /^plus(\d+)$/ 
     self.class.class_eval do 
     define_method(method_name) { @start_value + $1.to_i } 
     end 
     self.send(method_name) 
    else 
     super 
    end 
    end 
end 
0
  1. Вы должны вызвать class_eval на Adder класса, а не на экземпляре сумматор.
  2. Строка не действительна Ruby. Поставьте круглые скобки после #{method}.

Новая версия кода:

class Adder 
def initialize(_val) 
    @start_value = _val 
end 

def method_missing(method_name, *args) 
    method = method_name.to_s 
    if method.start_with?("plus") then 
     num = method[4 .. method.length] 
     if (/^[\d]+(\.[\d]+){0,1}$/ === num) then 
      number = Integer(num) 
      self.class.class_eval("def #{method}() return @start_value + #{number} end") 
     else 
      super 
     end 
    else 
     super 
    end 
end 

end 

a = Adder.new(0) 
a.plus1 

Speaking для себя, так, как я бы построил этот метод вверх просто начать с

class Adder 
end 

Adder.class_eval("def plus1() return 0 + 1 end") 
a = Adder.new 
a.plus1 

, а затем постепенно заменен жесткие значения с настраиваемыми значениями, вместо того, чтобы записывать все сразу.

1
class Adder 
def initialize(_val) 
    @start_value = _val 
end 

def method_missing(method_name, *args) 
    method = method_name.to_s 
    if method.start_with?("plus") then 
     num = method[4 .. method.length] 
     if (/^[\d]+(\.[\d]+){0,1}$/ === num) then 
      number = Integer(num) 
      self.class.class_eval("def #{method}() return @start_value + #{number} end") 
      eval(method) 
     else 
      super 
     end 
    else 
     super 
    end 
end 

end 

a = Adder.new(0) 
a.plus1 

Обязательно добавьте eval (method) в конце, чтобы вызвать метод, иначе он будет возвращать нуль только для создания метода. Или вы можете просто с возвратной @start_value + # {номер}

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