Railsのソースをちょろちょろ読むようにしている。読んで学んだことをメモしておきたい。だいたい読んだ当時の最新リリースを参考にします。

ActiveSupport::Concern を読みました。

このモジュールはモジュールの定義を手助けします。 クラスメソッドの定義場所をルール決めして include するだけで済むようにしたり、クラスのコンテキストで実行したい処理を書く場所を用意してくれます。

具体的にいきます。 以下のコードがあったとします。

class ConcernSample

  attr_accessor :hoge

  def self.mogu
    "mogu"
  end

  def goro
    "goro"
  end
end

これを以下のようにするだけで同じ機能を提供できるようにしたいと思います。

class ConcernSample
  include Sample
end

処理を Sample モジュールにまとめたいということです。 これができると

class ConcernSample
  include Sample
end

class ConcernSample2
  include Sample
end

class ConcernSample3
  include Sample
end

と、似たような機能をもつクラスを量産できます。 クラスに機能を追加するのが簡単になるという視点を持つとよいでしょう。

さて、Sample はどのように書くかということです。 ここで ActiveSupport::Concern を利用します。

require 'active_support'

module Sample

  extend ActiveSupport::Concern

  included do
    attr_accessor :hoge
  end

  module ClassMethods
    def mogu
      "mogu"
    end
  end

  def goro
    "goro"
  end
end

こんな感じになります。 クラスメソッドの定義の仕方を少し代えて横に並べてみると、非常に変化が少なくて済むのがわかると思います。

参考

ActiveSupport::Concern を利用せずに実装するとこんな感じになります。

module Sample
  def self.included(base)
    base.extend ClassMethods
    base.class_exec do
      attr_accessor :hoge
    end
  end

  module ClassMethods
    def mogu
      "mogu"
    end
  end

  def goro
    "goro"
  end
end

def self.included がなくなり、モジュールの特異メソッドの利用がなくなるのと、base.extend が不要になるところが若干便利になります。よく rails のコード内で出てくるので知っておきたいですね。

@NeXTSTEP2OSさん から質問があって

サブクラスじゃダメなの?

と、質問されました。

親クラスにも重複するコードをまとめることができますが、機能を追加するという視点で考えた場合、module の場合はいくつも include することができます。継承を利用した場合は、親クラスはひとつしか持つことができないため不便です。また、継承はis-aの関係ではないところでは使うべきでないとされています。この Concern が利用されている部分は is-aの関係を持たない部分でコードの重複を避けるためやメソッドが多すぎるクラスでメソッドを分類するために利用されているようです。

利用例としてはたくさんのメソッドが定義されている ActiveRecord::Base クラスで include されてるモジュールなどがあります。 ActiveRecrod::Persistenceなどをみてみると利用されています。

Concernというクラスの名前の由来がよくわからない。

つづき