包括模块定义动态类方法

问题描述:

我与红宝石玩弄,我写了下面的代码:包括模块定义动态类方法

module IdAndNameRedefine 
    def self.included(base) 
    base.extend(ClassMethods) 
    end 

    module ClassMethods 
    def use_id_and_name_from_module(use_attribute) 
     class_eval <<CODE 
     def id_and_name 
      "\#{id}-\#{#{use_attribute}.downcase}" 
     end 
CODE 
    end 
    end 
end 


class Model 
    include IdAndNameRedefine 

    attr_reader :id, :name1, :name2 

    def initialize(id, name1, name2) 
    @id = id 
    @name1 = name1 
    @name2 = name2 
    end 

    def id_and_name 
    "#{id}-#{name1}" 
    end 

    use_id_and_name_from_module :name2 
end 

model = Model.new(1, "TesT", "Test number TWO") 
puts model.id_and_name 

当我想在这里做的是覆盖类方法id_and_name类使用IdAndNameRedefine模块动态插入模型。当包含该模块时,它将创建一个“静态”方法(实际上,我理解它是Model.class的一个类方法),并调用use_id_and_name_from_module时,它将在Model中创建一个类方法,该方法重新定义id_and_name以使用任何属性模型的要求。

我的问题是...... 有没有更好的方法来做到这一点,或者这是做到这一点的“正确”方式?我真的不确定是否喜欢class_eval,并且需要一个字符串,以便我需要转义值等来完成这项工作。

您不必将字符串传递给class_eval。它可以取而代之。事实上,我无法想象我必须将字符串传递给class_eval。所以我们可以重写ClassMethods模块,像这样:

module ClassMethods 
    def use_id_and_name_from_module(use_attribute) 
    class_eval do 
     define_method(:id_and_name) {"#{id}-#{send(use_attribute).downcase}"} 
    end 
    end 
end 

但是,在这种特殊情况下,我们只是告诉selfclass_eval,这意味着我们已经在这个类的上下文。因此,它实际上可以简化为:

module ClassMethods 
    def use_id_and_name_from_module(use_attribute) 
    define_method(:id_and_name) {"#{id}-#{send(use_attribute).downcase}"} 
    end 
end 

(我只是想说明如何class_eval真正的作品,因为你似乎最感兴趣的部分。)

+0

谢谢!我认为这看起来有点愚蠢,但我找不出一个更好的方式来做到这一点。我相信我也尝试了define_method,但是那一点我在类定义的顶部有`use_id_and_name_from_module``调用,因此它不起作用(在我后面的类中被声明覆盖)。 只是出于好奇;有什么方法可以将`use_id_and_name_from_module`放在类的顶部,并确保它在以后的声明中不会被覆盖。 – 2009-06-18 08:24:24