我试图通过包含一个模块来覆盖动态生成的方法.
在下面的示例中,Ripple关联rows=
向Table 添加方法.我想调用那个方法,但之后还要做一些额外的事情.
我创建了一个模块来覆盖该方法,认为模块row=
可以调用super
以使用现有方法.
class Table
# Ripple association - creates rows= method
many :rows, :class_name => Table::Row
# Hacky first attempt to use the dynamically-created
# method and also do additional stuff - I would actually
# move this code elsewhere if it worked
module RowNormalizer
def rows=(*args)
rows = super
rows.map!(&:normalize_prior_year)
end
end
include RowNormalizer
end
Run Code Online (Sandbox Code Playgroud)
但是,我的新rows=
内容从未被调用,事实证明,如果我在其中引发异常,则不会发生任何事情.
我知道该模块已被包含在内,因为如果我把它放入其中,我的异常会被提升.
included do
raise 'I got included, woo!'
end
Run Code Online (Sandbox Code Playgroud)
此外,如果代替rows=
模块定义somethingelse=
,则该方法是可调用的.
为什么我的模块方法不会覆盖动态生成的模块?
gle*_*ebm 11
我们来做一个实验:
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
Run Code Online (Sandbox Code Playgroud)
这是为什么?答案很简单:
A.ancestors
=> [A, B, Object, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)
B
是A
在祖先链之前(你可以把它想象成B
在里面 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"
Run Code Online (Sandbox Code Playgroud)
使用ActiveSupport(以及Rails),您可以将此模式实现为alias_method_chain(target, feature)
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
Run Code Online (Sandbox Code Playgroud)
更新红宝石2自带的模块#前插,这并重写的方法A
,使这个alias
黑客不必要的大多数用例.
归档时间: |
|
查看次数: |
1336 次 |
最近记录: |