[Ruby on Rails] ActiveSupport::Concernとは
初めに
今回はActiveSupport::Concernについて触れていく。モジュールを通常のmix-inするよりも、依存関係を気にせずに書けるぐらいの認識だったので、より理解を深めるためにも、公式のコードから掘り下げていく。
結論
- include時にクラスメソッドとしても読み込む必要がある際に、簡易的に書くことができる
- 依存関係をよしなに解決してくれる
前提
include
インスタンスメソッドとして、モジュールを読み込む
extend
クラスメソッドとして、モジュールを読み込む。また、特定のインスタンスにモジュールを読み込ませたいときにも使用可能
本題
公式で取り上げられているコードを深堀し、ActiveSupport::Concernの理解を深める。
モジュールを一般的にmix-inすると以下のようになるとある。
module M def self.included(base) base.extend ClassMethods base.class_eval do scope :disabled, -> { where(disabled: true) } end end module ClassMethods ... end end
メソッド
included
•••モジュールがincludeされたときに呼び出されるメソッドclass_eval
•••クラスにインスタンスメソッドやクラスメソッドを追加することができるメソッド
(base)
に入るのはincludeしたクラス(モジュール)が入っており、extend
メソッドにより、このモジュールをinclude
した際には、インスタンスメソッドとしてだけではなく、クラスメソッドとしても使用できるといった内容である。
ActiveSupport::Concernを使うことで、上記のモジュールは以下のように書くことができるとある。
require "active_support/concern" module M extend ActiveSupport::Concern included do scope :disabled, -> { where(disabled: true) } end class_methods do ... end end
self.included(base)
内でクラスメソッドとして読み込む必要があったものが、extend ActiveSupport::Concern
によって簡素化されたclass_methods do
のブロック内で記述したものがクラスメソッドとして読み込まれるようになった。
また依存するモジュールがある際は、従来以下のように複雑に書かなければならなかった
module Foo def self.included(base) base.class_eval do def self.method_injected_by_foo ... end end end end module Bar def self.included(base) base.method_injected_by_foo end end class Host include Foo include Bar end
- Hostクラスでは、Barモジュールのみを使用したいが、依存関係にあるFooも読み込まないといけないため、include Fooの記載がある。
ActiveSupport::Concernを読み込むことで、よしなに依存関係を解決してくれる
require "active_support/concern" module Foo extend ActiveSupport::Concern included do def self.method_injected_by_foo ... end end end module Bar extend ActiveSupport::Concern include Foo included do self.method_injected_by_foo end end class Host include Bar # It works, now Bar takes care of its dependencies end
extend ActiveSupport::Concernをそれぞれのモジュール内で、読み込ませ、Bar内でFooをincludeすることで最終的なHostクラスでは、Barのみの記述ですんていることがわかる。