我们来做一个实验:
class A; def x; 'hi' end end
module B; def x; super + ' john' end end
A.class_eval { include B }
A.new.x
=> "hi" # oops
这是为什么?答案很简单:
A.ancestors
=> [A, B, Object, Kernel, BasicObject]
B
是在之前A
在祖先链中(你可以将其视为B
being inside A
)。所以A.x
总是优先于B.x
.
但是,这可以解决:
class A
def x
'hi'
end
end
module B
# Define a method with a different name
def x_after
x_before + ' john'
end
# And set up aliases on the inclusion :)
# We can use `alias new_name old_name`
def self.included(klass)
klass.class_eval {
alias :x_before :x
alias :x :x_after
}
end
end
A.class_eval { include B }
A.new.x #=> "hi john"
使用 ActiveSupport(以及 Rails),您可以将此模式实现为alias_method_chain(target, feature)
http://apidock.com/rails/Module/alias_method_chain http://apidock.com/rails/Module/alias_method_chain:
module B
def self.included(base)
base.alias_method_chain :x, :feature
end
def x_with_feature
x_without_feature + " John"
end
end
Update红宝石 2 附带模块#prepend http://ruby-doc.org/core-2.0.0/Module.html#method-i-prepend,它确实覆盖了方法A
,使得这个alias
对于大多数用例来说,破解是不必要的。