ruby - 仅当不包含类/模块时才允许模块定义方法

标签 ruby module include active-model-serializers

我对 ActiveModel 的序列化很感兴趣,特别是 as_jsonserializable_hash 的纠结网络。

我的应用程序有大量模型,通过包含一个模块来共享行为,我们将其称为 SharedBehavior

我的团队决定我们有一个默认格式,我们希望所有这些类在转换为 JSON(用于在 Rails 应用程序中渲染)时遵循,但其中一些类的行为应该略有不同。由于 ActiveModel 库中这两个方法的奇怪行为,在模型本身中添加白名单或黑名单属性会被此模块中的方法定义覆盖,然后传递到 ActiveModel 中的 super 声明。

因此,我希望此模块仅将这些方法的定义应用于模型(如果这些方法没有在这些模型中显式重写)(实质上,将模块从祖先链中取出以进行一些方法调用) ,但我仍然需要此模块的共享行为。

我尝试通过有条件地、动态地应用 IRB 中模块包含的方法来解决这个问题:

class A
  def foo
    puts 'in A'
  end
end

module D
  def self.included(base)
    unless base.instance_methods(false).include?(:foo)
      define_method(:foo) do
        puts 'in D'
        super()
      end
    end
  end
end

class B < A
  include D
end

class C < A
  include D
  def foo
    puts 'in C'
    super
  end
end

通过此声明,我预计 C.new.foo 的输出为

in C
in A

但事实却是

in C
in D
in A

我唯一的另一个想法是将此逻辑移出到另一个模块中,并将该模块包含在每个未显式重写此方法的类(大约有 54 个)中,但这样做有几个缺点:

  1. 它在项目中引入了一些隐式耦合,即新模型包含此模块,前提是它不想覆盖此方法实现
  2. 模块中这些序列化方法的当前实现与该模块建立的行为和属性有关,因此我觉得拥有第二个了解并依赖于 SharedBehavior 的实现细节的模块是不直观的,尽管第二个模块几乎与第一个模块无关。

其他人能否想到另一个解决方案,或者可能在上面的代码示例中发现我的疏忽,允许我在 included Hook 中进行调用? (我还尝试切换 C 类定义 foo 方法并包含 D 模块的顺序,但看到了完全相同的行为)。

最佳答案

这里有两个棘手的错误。

  1. Ruby 评估类,因此表达式的顺序很重要。您C中定义foo之前包含D,因此当included钩子(Hook)是调用时,foo 不会在 base 中定义。您需要在类的末尾包含D
  2. 您正在 D 中定义 foo。因此,在 B 中包含 D 后,定义了 D#foo,这意味着它仍然包含在 C 中 即使你修复了之前的错误。您需要 base 作为 define_method 的接收者。

但是有一个有趣的转折:修复第二个错误会使第一个错误变得无关紧要。通过直接在 base 中定义 foo,它将被任何后续定义覆盖。这就像做

class C < A
  def foo
    puts 'in D'
    super()
  end

  # overwrites previous definition!
  def foo
    puts 'in C'
    super
  end
end

总而言之,你只需要

# in D.included
base.class_eval do
  define_method(:foo) do
    puts 'in D'
    super()
  end
end

关于ruby - 仅当不包含类/模块时才允许模块定义方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26938732/

相关文章:

python - 导入错误 : No module named tensorflow

python - 访问类实例的父模块的 __all__ 列表

Ruby OptionParser 空开关 "-"行为

ruby - ruby gems/模块的命名空间扩展的约定是什么?

c - 为什么静态值在 IRB 内部被破坏?

php - 缓存包含或需要的文件以便可以反复使用?

c++ - 在模板实现文件中使用命名空间

module - 在 OCaml 中包含两个模块时处理类型名称冲突

ruby-on-rails - 如何在 Rails 中验证今天之后的日期?

ruby-on-rails - 部署到服务器后访问 Rails 控制台