Module のメソッドをオーバーライドする場合、クラス内に定義することもできますが、オーバーライドしていることを分かりやすくさせるため Module#prepend を使ったりします。加えて prepend であればメソッド呼び出しの優先順も高いため確実にオーバーライドしたメソッドが呼び出されることも保証されます。
Module#include
prepend に入る前に include について。
例えば以下のような Module があった場合
module BaseModule
def self.included(base)
base.extend(ClassMethods)
end
def instance_method
puts 'included_instance_method'
end
module ClassMethods
def singleton_method
puts 'included_singleton_method'
end
end
end
ここで、self.included で ClassMethod を extend しないと、この Module を include したクラスで singleton_method を呼び出した時に NoMethodError となります。base.extend することで Module のメソッドを特異メソッド(クラスメソッド)として使えるようにしてくれて、include したときにインスタンスメソッドとクラスメソッドも追加できます。
この Module を使いたいクラスで、
class CurrentClass
include BaseModule
end
と include すれば
# instance
current_class_obj = CurrentClass.new
current_class_obj.instance_method #=> "included_instance_method"
# singleton
CurrentClass.singleton_method #=> "included_singleton_method"
インスタンスメソッドもクラスメソッドも期待通りに呼び出すことができます。
Module#prepend
上記の Module クラスの特異メソッドである singleton_method をオーバーライドしたい場合、base_module.rb 内に singleton_method を定義しても良いのですが、Module#prepend 使ったほうが「Module のメソッドをオーバーライドしてるよ」というのがわかりやすくて良い気がしています。
module BaseModuleExtention
def self.prepended(base)
base.extend(ClassMethods)
end
module ClassMethods
def singleton_method
puts 'prepended_singleton_method'
end
end
end
今回のようにオーバーライドしたいメソッドがクラスメソッドだった場合、self.prepended で extend を呼び出す必要があります。
class CurrentClass
include BaseModule
prepend BaseModuleExtention
end
オーバーライドできているか確認します。
# instance
current_class_obj = CurrentClass.new
current_class_obj.instance_method #=> "included_instance_method"
# singleton
CurrentClass.singleton_method #=> "prepended_singleton_method"
ちゃんと prepend した BaseModuleExtention のメソッドが呼ばれてます。
ancestors を見ても、
BaseModuleExtention
CurrentClass
BaseModule
Object
Kernel
BasicObject
BaseModuleExtention から探索されることが分かるので期待通りの動きになることも保証されます。