Techioz Blog

モジュール内のクラスメソッドにエイリアスを付けるにはどうすればよいですか?

概要

私は Ruby v1.9.2 と Ruby on Rails v3.2.2 gem を使用しています。次のモジュールがありました

module MyModule
  extend ActiveSupport::Concern

  included do
    def self.my_method(arg1, arg2)
      ...
    end
  end
end

そして、クラスメソッド my_method の別名を付けたかったのです。そこで、次の(機能しない)コードを記述しました。

module MyModule
  extend ActiveSupport::Concern

  included do
    def self.my_method(arg1, arg2)
      ...
    end

    # Note: the following code doesn't work (it raises "NameError: undefined
    # local variable or method `new_name' for #<Class:0x00000101412b00>").
    def self.alias_class_method(new_name, old_name)
      class << self
        alias_method new_name, old_name
      end
    end

    alias_class_method :my_new_method, :my_method
  end
end

言い換えれば、MyModule 全体で使用できる alias_class_method メソッドを追加するために、何らかの方法で Module クラスを拡張しようと考えました。ただし、これを動作させ、すべての Ruby on Rails アプリケーションで利用できるようにしたいと考えています。

しかし、より重要なことは、クラスを拡張する必要がないように、私が達成しようとしているものを実現する Ruby の機能はあるのかということです。

解決策

次のように、define_singleton_method を使用して古いメソッドを新しい名前でラップすることができます。

module MyModule
  def alias_class_method(new_name, old_name)
    define_singleton_method(new_name) { old_name }
  end
end

class MyClass
  def my_method
    puts "my method"
  end
end

MyClass.extend(MyModule)
MyClass.alias_class_method(:my_new_method, :my_method)
MyClass.my_new_method     # => "my method"

コメントに答えると、すべてのクラスを手動で拡張する必要はありません。 define_singleton_method は Object クラスに実装されます。したがって、単純に Object クラスを拡張すれば、すべてのクラスで使用可能なメソッドが必要になります…

Object.extend(MyModule)

これをRailsアプリのイニシャライザに入れれば準備完了です…