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 した際にクラスメソッドとして、利用できるというサインです。