Rails のコードを読んでいると、module ClassMethods
って多く書かれている事が分かると思いますが、こいつが何をやっているのかを話していきます。
module ClassMethods
を説明する前に、まず、以下のような Module と Class を定義します。
module ModuleA
def self.class_method_a
'class_method_a'
end
def instance_method_a
'instance_method_a'
end
end
class ClassA
include ModuleA
end
そして、次に include
した、インスタンスメソッドとモジュールメソッドを実行してみると
ClassA.new.instance_method_a #=> "instance_method_a"
ClassA.class_method_a #=> NoMethodError
このように class_method_a
は NoMethodError
になる事が分かります。
class_method_a
はあくまで ModuleA の特異メソッドなので、利用することが出来ないのです。
そこで、以下のようにします。
module ModuleB
def self.included(klass)
klass.extend ClassMethods
end
module ClassMethods
def class_method_b
'class_method_b'
end
end
end
class ClassB
include ModuleB
end
ClassB.class_method_b #=> "class_method_b"
included
をオーバーライドして、ModuleB
を include
する際に、ClassB
を ClassMethods
で extend
しています。こうすることで、クラスメソッドとして使えるようになります。
ちなみに、class_method_b
は extend
するために Module で囲っているだけなので、Module 名に意味はありません。ただ、慣習的に module ClassMethods
とするそうです。
以上が module ClassMethods
の意味です。
しかし、Rails のコードを読んでいると、included
を使って ClassMethods
を extend
している箇所は見つかりません。
module ActiveSupport
module Configurable
extend ActiveSupport::Concern
...
module ClassMethods
...
そして、代わりに ActiveSupport::Concern
を extend
していることに気がつきます。そこで、実際にマネして Module を次のようにしてみると、
module ModuleC
extend ActiveSupport::Concern
module ClassMethods
def class_method_c
'class_method_c'
end
end
end
class ClassC
include ModuleC
end
ClassC.class_method_c #=> "class_method_c"
確かにクラスメソッドとして、使えます。
これは ActiveSupport::Concern
の append_features
内で ClassMethods
を extend
するようにオーバーライドしているためです。(append_features
は include
の手前で呼び出されるメソッドです)
そのため、Module に ActiveSupport::Concern
を extend
すれば、自動的にその Module が include
された際に、ClassMethods
を extend
します。
つまり、extend ActiveSupport::Concern
としておけば、ClassMethods
は全てクラスメソッドとして、使えるようになります。
ちなみに、Rails 4.2 以降は以下のように class_methods
で囲みます。
module ModuleD
extend ActiveSupport::Concern
class_methods do
def class_method_d
'class_method_d'
end
end
end
class ClassD
include ModuleD
end
ClassD.class_method_d #=> "class_method_c"
便利ですね。
長々と書きましたが、最後に、誤解を恐れずに一言で結論づけると module ClassMethods
は include
した際にクラスメソッドとして、利用できるというサインです。