プラグイン作成中に覚えたこと 補足

メソッドを追加するメソッド

  • シンプルに考えれば、以下のようにメソッド定義の中にメソッド定義を書いても良いと気付いた。(define_methodは必要ない?)
class Test
  def self.make_foo
    def foo
      'foo'
    end
  end
end

test = Test.new

Test.make_foo
test.foo
=> "foo"
  • それでは、define_methodを使うと、どんな良いことがあるのだろう?
class Test
  def self.make_method(method)
    define_method(method) do
      method
    end
  end
end

test = Test.new

Test.make_method('free')
test.free
=> "free"

Test.make_method('free2')
test.free2
=> "free2"
  • 追加定義するメソッド名を動的に設定したい時に便利だ。さらに、もっと大事なことが...
二重の(ネストした)defの場合
class Test
  def self.make_foo
    def foo
      'foo'
    end
  end
end

class TestChild < Test
  make_foo
end
# Testクラスを継承したTestChildクラスでmake_fooを実行してみる。

TestChild.new.foo
=> "foo"
# 期待通り、TestChildクラスにfooメソッドが追加された...

Test.new.foo
=> "foo"
# と思っていたら、Testクラスにもfooメソッドが...

class Test
  def foo
    'Test.foo'
  end
end
# 確認のため、Testクラスのfooメソッドを'Test.foo'を返すように変更してみると...

TestChild.new.foo
=> "Test.foo"
# TestChildクラスも'Test.foo'を返すようになってしまった。
# つまり、fooメソッドはTestクラスに定義されていたのだ...。
  • 二重の(ネストした)defメソッド定義の場合は、そのメソッドを定義しているクラスに対してメソッドが追加される。(冷静に考えてみれば、当然の動きである。)
define_methodの場合
  • 同じことをdefine_methodでやってみた。
class Test
  def self.make_foo
    define_method(:foo) do
      'foo'
    end
  end
end

class TestChild < Test
  make_foo
end

TestChild.new.foo
=> "foo"

Test.new.foo
NoMethodError: undefined method 'foo' for #<Test:0x269e28>
# 今度は間違いなくTestChildクラスにfooメソッドが定義された。

class Other
  Test.make_foo
end
# 実験のため、全く無関係なOtherクラスでTest.make_fooを実行してみると...

Other.new.foo
NoMethodError: undefined method 'foo' for #<Other:0x25a5f4>

Test.new.foo
=> "foo"
# Testクラスにfooメソッドが追加された。
# つまり、Test.make_fooはTestクラスに、TestChild.make_fooはTestChildクラスに、fooメソッドが追加されたのだ。
  • define_methodは、そのメソッドが実行された時のオブジェクトに対して、メソッドを定義するのだ!(Test.make_fooとなっていれば、Testクラスに定義される。)