このモジュールを含めることは、動的に生成された方法を無効にしないのはなぜですか?
質問
モジュールを含めることにより、動的に生成された方法をオーバーライドしようとしています。
以下の例では、波紋協会が追加します rows=
テーブルへの方法。私はその方法を呼び出したいだけでなく、その後いくつかの追加のことをしたいと思います。
モジュールをオーバーライドするモジュールを作成しました。 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
しかし、私の新しい rows=
私がその内部に例外を提起した場合、何も起こらないという事実から証明されるように、決して呼ばれることはありません。
モジュールが含まれていることはわかっています。なぜなら、これを入れれば、例外が提起されるからです。
included do
raise 'I got included, woo!'
end
また、代わりに rows=
, 、モジュールが定義します somethingelse=
, 、その方法は呼ばれます。
動的に生成されたモジュールメソッドを無効にしないのはなぜですか?
解決
実験をしましょう:
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
であること 中身 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(したがってレール)を使用すると、このパターンが実装されています 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
アップデート Ruby 2には付属しています モジュール#プレップエンド, 、の方法を無効にします A
, 、これを作っています alias
ほとんどのユースケースでは不要です。
他のヒント
動的に生成されたモジュールメソッドを無効にしないのはなぜですか?
それが継承の仕組みではないからです。クラスで定義された方法は、他のクラス/モジュールから継承されたものをオーバーライドしますが、その逆ではありません。
Ruby 2.0にはあります Module#prepend
, 、同じように機能します Module#include
, 、モジュールを挿入することを除きます サブクラス 継承チェーンのスーパークラスの代わりに。
もし、あんたが extend
クラスのインスタンス、あなたはそれをすることができます。
class A
def initialize
extend(B)
end
def hi
'hi'
end
end
module B
def hi
super[0,1] + 'ello'
end
end
obj = A.new
obj.hi #=> 'hello'