4

ルックアップ チェーンを壊さずに 2 つの Ruby モジュールをマージしたいと考えています。BothAnB基本的に、A と B のテキスト ソース コードを連結し、新しい foo が古いものを置き換えたかのように、 の動作を正確に行いたいと考えています。この問題は、MRO が継承ダイヤモンドを線形化するときに発生します。

module O
  def foo; puts "O" end
end

module A
  include O
  def foo; puts "A"; super end
  def aaa; puts "aaa" end
end

module B
  include O
  def foo; puts "B"; super end
  def bbb; puts "bbb" end
end

module BothAnB
  #insert magic here such that a class C that includes BothAnB:
  # C.new.foo => B O
  # C.new.aaa => aaa
  # C.new.bbb => bbb
end

module JustA
  #insert magic here such that a class C that includes JustA:
  # C.new.foo => A O
  # C.new.aaa => aaa
  # C.new.bbb => FAIL
end
#and similarly JustB

A と B はかなり複雑なモジュールであり、深い継承チェーンを持つことができます (これは、プログラマーがそれを行うことを可能にするメタプログラミング フレームワークのためのものです)。

Include B, Aルックアップ BothAnB->B->A->O の代わりに、BothAnB->B->O (およびオプションで ->A) である必要があるため、機能しません。私は近くに来ました:

  • A の継承ツリー全体のディープ クローニング (ダイヤモンドを削除するため)
  • undef_methodA のクローンで、B にあるメソッドを削除します
  • Moduleこの動作を反映するための新しいメソッドを作成する

これよりも良い解決策はありますか?を呼び出すときに少なくともいくつかのモジュールを認識できるようにしておくことが理想的ですBothAnB.ancestors

[注: Phrogz のフィードバックに基づいて 2 つの回答を得た後、質問を完全に変更しました。

4

4 に答える 4

2

これで解決しますか?

module M1
  def foo; 42; end
  def bar; 17; end
end

class Base
  def foo; 0; end
end

require 'remix' # gem install remix 
class X < Base
  include_after Base, M1
end

p X.new.foo, #=> 0
  X.new.bar  #=> 17
于 2012-07-31T21:05:05.300 に答える
1
M1parent.send(:remove_method, :foo)

たとえば、メソッド foo が M1parent で定義されているため、それは機能しませんM1parentM1.send(:remove_method, :foo)

于 2012-07-31T15:06:00.327 に答える
0

私の提案は次のとおりです: http://bugs.ruby-lang.org/にログインして機能リクエストを送信するか、自信がある場合はバグリクエストを送信してください。これは基本的にRubyの問題だからです。現在の Ruby の動作は、事前の経験がなければ予想外のものであるため、期待される動作に変更する必要があります。

module BothAnB
  include A
  include B
end

あなたが指摘したように、回避策は確かに可能です。しかし、この imho での Ruby の動作は正しくありません。

そのため、それまでは super の呼び出しを控える必要があります。代わりに、で遊ぶことをお勧めします。

O.instance_method( :foo )

便利なキーワード super の代わりに B および A モジュールから呼び出します。

module A
  include O
  def foo
    puts "A"
    O.instance_method( :foo ).bind( self ).call
  end
end

# do same for B and it'll work
于 2012-08-14T10:31:19.407 に答える