gpt4 book ai didi

ruby - 如果不包含类/模块,则只允许模块定义方法

转载 作者:数据小太阳 更新时间:2023-10-29 08:46:29 25 4
gpt4 key购买 nike

我从 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 评估 类,因此表达式的顺序很重要。您在Cinclude D before 定义foo,所以当included 钩子(Hook)是调用时,foo 不会在 base 中定义。您需要在类(class)的末尾包含 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com