使用多个模块扩展的对象中方法的 Ruby 优先级
Posted
技术标签:
【中文标题】使用多个模块扩展的对象中方法的 Ruby 优先级【英文标题】:Ruby precedence of methods in objects extended with multiple modules 【发布时间】:2012-01-09 20:39:23 【问题描述】:鉴于以下情况:
class User; attr_accessor :roles; end
module RegisteredUser
def default_context
Submission
end
end
module Admin
def default_context
Review
end
end
current_user = User.new
current_user.roles = ["registered_user", "admin"]
current_user.roles.each do |role|
role_module = role.gsub(/ /, '_').camelize
if module_exists?(role_module)
current_user.extend role_module.constantize
end
end
context = self.extend current_user.default_context
有没有办法设置User#default_context
的优先级?也就是说,我可以说Admin#default_context
始终优先于RegisteredUser#default_context
,而不管current_user
的扩展顺序如何?
【问题讨论】:
您可能可以使用extended
或method_added
做一些事情,但是您需要在模块中或某处拥有某种优先级规则机制......呃......否则。
我在 ruby doc 中找不到这两种方法。
移至伪答案以进行格式化。
【参考方案1】:
method_added
在Module
中。
我实际上是指included
,而不是extended
,但两者也在Module
中。
该机制将围绕执行以下操作:
module Foo
def self.included(base)
base.extend(FooMethods)
end
module FooMethods
def bar
# Whatever
end
end
end
在Foo.included
中,您可以根据任意标准确定是否应将相关方法添加到base
(包括模块的实体)。
在您的情况下,您可以检查是否已包含“更高优先级”模块,或查看该模块是否是“更高优先级”模块。基于此,您将决定是否添加方法。
【讨论】:
我在尝试 Foo.method_added 时收到“NoMethodError: private method `method_added' called for Foo:Module”。 @ReedG.Law 这不是你使用它的方式,我也不相信它是你想要使用的——我认为这是一个“后”挂钩,没有帮助。included
会更合适。【参考方案2】:
你不能;在 Ruby 中,模块包含的顺序是搜索模块的顺序 (after the current class, before parent classes)。更改“优先级”的唯一方法是以您想要的顺序包含模块,或者将它们移动到父类。
虽然不是纯 Ruby,但您可以使用 banisterfiend 的 Remix 库来更改模块顺序(或取消混合模块,或...其他事情)。
【讨论】:
我必须玩,但似乎可以使用included
/extended
的一些挂钩来做到这一点,不是吗?
Remix 看起来不错,但如果没有进一步评估,很难跳到那种解决方案。尽管这种功能对于使用 DCI 设计模式可能是必不可少的。【参考方案3】:
由于管理员也是注册用户,我会这样做
module Admin
include RegisteredUser
...
end
然后只有
current_user.extend Admin
我不确定这是否是正确的方法。如果 Admin 和 RegisteredUser 是类,那么让 Admin 从 RegisteredUser 继承是有意义的。如果是模块,不知道。
【讨论】:
这种方法似乎有效。我能看到的唯一缺点是是否有许多模块和复杂的组合(即一个用户是管理员但不是编辑器,但管理员包括编辑器)。以上是关于使用多个模块扩展的对象中方法的 Ruby 优先级的主要内容,如果未能解决你的问题,请参考以下文章