为什么Active Record会在内部对某些功能使用module_eval?

时间:2014-07-23 11:35:29

标签: ruby-on-rails ruby activerecord ruby-on-rails-4

我注意到Rails源代码中的一些地方使用了module_eval。一个位于ActiveRecord::Enum,另一个位于ActiveRecord::Store。我熟悉class_evalinstance_eval,并使用它们来扩展现有类或对象的功能,但在module_eval的情况下,似乎它用于不同的目的。

在这两种情况下,他们都使用类似的模式来定义模块:

def _store_accessors_module
  @_store_accessors_module ||= begin
    mod = Module.new
    include mod
    mod
  end
end

如果模块被包含在它定义的类中,那么在这样的嵌套模块中定义相关方法有什么好处?是否更好地隔离了代码?我问的原因是因为我有一个为Active Record增加功能的gem,我想知道这种方法是否更像是一种“最佳实践”方式来做同样的事情。 Here's the relevant source code of my gem供参考。

1 个答案:

答案 0 :(得分:3)

在嵌套模块中定义方法的原因是,用户可以覆盖这些方法,并且仍然可以访问super以获取原始功能。回想一下,当你在Ruby中包含模块时,它们被插入到当前类的祖先列表中,super通过简单地遍历祖先数组来工作,寻找响应当前方法的第一个对象。为此,模块的名称并不重要,因为它只是一个类似继承链的传递机制。这就是他们为什么只定义一个匿名的新模块并将其包含在运行中的原因。

如果您查看列出的2个示例的责备视图,您可以看到更改背后的原因。 commit message in the ActiveRecord::Store example表明情况非常好。如您所见,他们正在添加覆盖访问者定义color的功能,并通过super || 'red'添加原始方法的结果。然而,在原始实现中,必须覆盖color访问器方法,然后执行与原始访问器方法相同的工作,即调用read_store_attribute(:settings, :color) || 'red'。因此,所有关于不被迫重现内部或使用别名方法链来增强动态定义方法的功能。

我不确定这是否是您宝石中的有用功能,但我猜测可能不是因为您的访问器似乎正在返回定义良好的对象枚举相关对象。但是,当然,这取决于你和宝石的用户:)。