【问题标题】:Rails Metaprogramming: How to add instance methods at runtime?Rails 元编程:如何在运行时添加实例方法?
【发布时间】:2011-02-02 03:59:58
【问题描述】:

我在 Rails 中定义了我自己的 AR 类,它将包括为用户字段 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

【问题讨论】:

    标签: ruby-on-rails ruby metaprogramming


    【解决方案1】:

    一个很好的方法是使用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
    

    【讨论】:

    • 谢谢。为什么在您的第二个示例中,[i] 会在方法定义而不是方法执行时进行评估?
    • [i] 将按需要工作,因为方法定义是一个块,而在 Ruby 中,块是闭包。 HTH
    【解决方案2】:

    使用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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-26
      • 2013-03-04
      • 2018-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-20
      相关资源
      最近更新 更多