Railsにおいて共通した処理を切り出すために [ActiveSupport::Concern] (http://api.rubyonrails.org/classes/ActiveSupport/Concern.html) がよく使われています。
しかし、Rubyには元から mix-in という処理を切り出す機能があります。
では、ActiveSupport::Concern
の存在理由は何でしょうか。
mix-inの複雑な記述を省略できる
Rubyの mix-in は通常以下のようなルールで記述します
- 切り出した機能を
module
として作成 - 共通メソッドを
module
内に記述 - クラスメソッドや組み込み先クラスの内部処理を
module
に入れたい場合は特殊なメソッドを使う必要がある
ActiveSupport::Concern
はこの3番目にある 特殊なメソッド の記述を簡単にしてくれます。
具体的にConcernあり/なし の例を見てみましょう。
(具体例コードは ActiveSupport::Concernの公式解説より抜粋)
Concernなし
module M
def self.included(base)
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
module ClassMethods
...
end
end
Concernあり
require 'active_support/concern'
module M
extend ActiveSupport::Concern
included do
scope :disabled, -> { where(disabled: true) }
end
class_methods do
...
end
end
def self.included(base) ...
といった複雑な記述が省略されています。
※なぜこのような記述が必要なのかは少し複雑なのでここでは解説しません。
詳しくは 難しいが強力! Rubyのメタプログラミング、self、特異クラス/メソッド、オープンクラスとモンキーパッチ などのページを読むとわかります。
複雑な依存関係を考えずにすむ
ActiveSupport::Concern
のもう1つの存在理由です。
mix-in するモジュールが他のモジュールに依存する場合、mix-in される側では両方のモジュールをincludeする必要があります。
例えば、Foo
モジュールを必要とする Bar
モジュールを Host
に mix-in したい場合は
class Host
include Foo # Barのために先にFooをincludeしないといけない
include Bar
end
しかし、Host
が欲しいのは Bar
であって、わざわざ依存する Foo
まで考えて先に include
するのは面倒です。
そこで ActiveSupport::Concern
ではその面倒な記述を省略できます。
Concernありの場合
require 'active_support/concern'
module Foo
extend ActiveSupport::Concern
...
end
module Bar
extend ActiveSupport::Concern
include Foo
...
end
class Host
include Bar # Barだけをincludeしても動く!
end