LoginSignup
184
134

More than 5 years have passed since last update.

Rails の module ClassMethods がやっている事

Last updated at Posted at 2015-05-20

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_aNoMethodError になる事が分かります。
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 をオーバーライドして、ModuleBinclude する際に、ClassBClassMethodsextend しています。こうすることで、クラスメソッドとして使えるようになります。

ちなみに、class_method_bextend するために Module で囲っているだけなので、Module 名に意味はありません。ただ、慣習的に module ClassMethods とするそうです。

以上が module ClassMethods の意味です。

しかし、Rails のコードを読んでいると、included を使って ClassMethodsextend している箇所は見つかりません。

module ActiveSupport
  module Configurable
    extend ActiveSupport::Concern

    ...

    module ClassMethods
      ...

そして、代わりに ActiveSupport::Concernextend していることに気がつきます。そこで、実際にマネして 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::Concernappend_features 内で ClassMethodsextend するようにオーバーライドしているためです。(append_featuresinclude の手前で呼び出されるメソッドです)
そのため、Module に ActiveSupport::Concernextend すれば、自動的にその Module が include された際に、ClassMethodsextend します。

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

184
134
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
184
134