web-dev-qa-db-ja.com

Ruby:define_method vs. def

プログラミングの練習として、クラスを作成し、そのクラスから2つのオブジェクトをインスタンス化し、1つのオブジェクトにモンキーパッチを適用し、もう1つにmonkeypatchを行うmethod_missingに依存するRubyスニペットを書きました。

これが取引です。これは意図したとおりに機能します。

class Monkey

  def chatter
    puts "I am a chattering monkey!"
  end

  def method_missing(m)
    puts "No #{m}, so I'll make one..."
    def screech
      puts "This is the new screech."
    end
  end
end

m1 = Monkey.new
m2 = Monkey.new

m1.chatter
m2.chatter

def m1.screech
  puts "Aaaaaargh!"
end

m1.screech
m2.screech
m2.screech
m1.screech
m2.screech

Method_missingのパラメーターがあることに気付くでしょう。 define_methodを使用して、適切な名前で欠落しているメソッドを動的に作成することを望んでいたため、これを行いました。ただし、機能しません。実際、次のような静的な名前でdefine_methodを使用しても:

def method_missing(m)
  puts "No #{m}, so I'll make one..."
  define_method(:screech) do
    puts "This is the new screech."
  end
end

次の結果で終了します。

ArgumentError: wrong number of arguments (2 for 1)

method method_missing   in untitled document at line 9
method method_missing   in untitled document at line 9
at top level    in untitled document at line 26
Program exited.

エラーメッセージをさらに混乱させるのは、method_missing...の引数が1つしかないことです。

65
gauth

define_methodは、オブジェクトの(プライベート)メソッドですClassinstanceから呼び出しています。 define_methodと呼ばれるインスタンスメソッドがないため、method_missing(今回は:define_method(欠落しているメソッドの名前)、および:screech(唯一の引数)で再帰します。 define_method)に渡しました。

代わりにこれを試してください(すべてのMonkeyオブジェクトで新しいメソッドを定義するには):

def method_missing(m)
    puts "No #{m}, so I'll make one..."
    self.class.send(:define_method, :screech) do
      puts "This is the new screech."
    end
end

または、これ(オブジェクトの「固有クラス」を使用して、呼び出されるオブジェクトでのみ定義する):

def method_missing(m)
    puts "No #{m}, so I'll make one..."
    class << self
      define_method(:screech) do
        puts "This is the new screech."
      end
    end
end
136
Avdi

define.methodはプライベートメソッドであるため、self.class.define_method(:screech)は機能しません。

class << self
    public :define_method
end
def method_missing(m)
puts "No #{m}, so I'll make one..."
Monkey.define_method(:screech) do
  puts "This is the new screech."
end
4
clf
def method_missing(m)
    self.class.class_exec do
       define_method(:screech) {puts "This is the new screech."}
    end 
end

screechメソッドは、すべてのMonkeyオブジェクトで使用できます。

4
Andrew