Rails Metaprogramming: Как добавить методы экземпляра во время выполнения? - PullRequest
4 голосов
/ 29 марта 2010

Я определяю свой собственный класс AR в Rails, который будет включать динамически создаваемые методы экземпляра для пользовательских полей 0-9. Пользовательские поля не хранятся в БД напрямую, они будут сериализованы вместе, так как они будут использоваться нечасто. Является ли следующий способ лучшим способом сделать это? Альтернативы?

Откуда должен вызываться стартовый код для добавления методов?

class Info < ActiveRecord::Base

end

# called from an init file to add the instance methods
parts = []
(0..9).each do |i|
   parts.push "def user_field_#{i}"     # def user_field_0
   parts.push   "get_user_fields && @user_fields[#{i}]"
   parts.push "end"
end

Info.class_eval parts.join

Ответы [ 2 ]

10 голосов
/ 29 марта 2010

Один хороший способ, особенно если у вас может быть более 0..9 пользовательских полей, это использовать method_missing:

class Info
  USER_FIELD_METHOD = /^user_field_(\n+)$/
  def method_missing(method, *arg)
    return super unless method =~ USER_FIELD_METHOD
    i = Regexp.last_match[1].to_i
    get_user_fields && @user_fields[i]
  end

  # Useful in 1.9.2, or with backports gem:
  def respond_to_missing?(method, private)  
    super || method =~ USER_FIELD_METHOD
  end
end        

Если вы предпочитаете определять методы:

10.times do |i|
  Info.class_eval do
    define_method :"user_field_#{i}" do
      get_user_fields && @user_fields[i]
    end
  end
end
4 голосов
/ 27 марта 2011

Использование method_missing очень сложно в обслуживании и не нужно. Другая альтернатива, использующая define_method, лучше, но приводит к неэффективному коду. Все, что вам нужно, это следующий 1 вкладыш:

class Info
end

Info.class_eval 10.times.inject("") {|s,i| s += <<END}
  def user_field_#{i}
    puts "in user_field_#{i}"
  end
END

puts Info.new.user_field_4
...