6

モジュールを含めることで、動的に生成されたメソッドをオーバーライドしようとしています。

以下の例では、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

ただし、 newrows=が呼び出されることはありません。これは、その中で例外を発生させても何も起こらないという事実からも明らかです。

これを入れると例外が発生するため、モジュールが含まれていることはわかっています。

      included do
        raise 'I got included, woo!'
      end

rows=また、モジュールが の代わりに を定義しsomethingelse=ている場合、そのメソッドは呼び出し可能です。

モジュール メソッドが動的に生成されたメソッドをオーバーライドしないのはなぜですか?

4

3 に答える 3

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

何故ですか?答えは簡単です。

A.ancestors
=> [A, B, Object, Kernel, BasicObject]

B祖先チェーンの前です (これは の中にあるA考えることができます)。したがって、 は常に よりも優先されます。B AA.xB.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として実装できます。

module B
  def self.included(base)
    base.alias_method_chain :x, :feature
  end

  def x_with_feature
    x_without_feature + " John"
  end
end

Update Ruby 2 にはModule#prependが付属しています。これは のメソッドをオーバーライドするためA、このaliasハックはほとんどのユース ケースで不要になります。

于 2011-01-19T22:39:10.640 に答える
2

モジュール メソッドが動的に生成されたメソッドをオーバーライドしないのはなぜですか?

それは継承がどのように機能するかではないからです。クラスで定義されたメソッドは、他のクラス/モジュールから継承されたメソッドをオーバーライドします。その逆ではありません。

Ruby 2.0 には があり、モジュールを継承チェーンのスーパークラスではなくサブクラスとして挿入することを除いて、 とModule#prepend同じように機能します。Module#include

于 2011-01-19T22:52:45.803 に答える
0

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'
于 2011-01-19T22:56:05.017 に答える