JQ Blog

Mix-in(クラスメソッド、インスタンスメソッド),concernsの使い方

Rubyのクラスは多重継承ができない。そういう限界を解決できるようにしてくれるのがMix-inという開発パタン。
モジュールを通して複数の機能を追加することができる。

インスタンスメソッドの場合

基本的にincludeするとモジュールのメソッドをインスタンスメソッドとして使用できる。

1
2
3
4
5
6
7
8
9
10
11
module Foo
  def foo
    p "good job!"
  end
end

class Hoge
  include Foo
end

Hoge.new.foo # => "good job!"

クラスメソッドの場合

モジュールのメソッドをクラスメソッドとして使用するためには

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module Foo
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def class_foo
      p "class good job!"
    end
  end

  def foo
    p "good job!"
  end
end

class Hoge
  include Foo
end

Hoge.new.foo # => "good job!"
Hoge.class_foo # => "class good job!"

クラスでインクルードされたらself.included(base)にコールバックされる。ここで引数に渡されるのはインクルードしたクラスになる。そこにモジュールをextendしたらクラスメソッドとしても使用できる。

concernsの使い方

concernsはActiveSupport::Concernをextendするだけでmix-inの複雑な記述を省略できる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module Foo
  extend ActiveSupport::Concern

  # なくても良い
  included do
  end

  class_methods do
    def class_foo
      p "class good job!"
    end
  end

  def foo
    p "good job!"
  end
end

class Hoge
  include Foo
end

Hoge.new.foo # => "good job!"
Hoge.class_foo # => "class good job!"

concernsのメリット
複雑な依存関係を考えなくても良い。FooをextendするBarをHogeクラスでextendするとしたら

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
module Foo
  extend ActiveSupport::Concern

  # なくても良い
  included do
    p "included Foo"
  end

  class_methods do
    def class_method
      p "Foo class good job!"
    end
  end
end

module Bar
  extend ActiveSupport::Concern
  include Foo

  included do
    p "included Bar"
  end

  class_methods do
    def class_method
      super
      p "Bar class good job!"
    end
  end
end

class Hoge
  include Bar
end

Hoge.class_method # =>
# "included Foo"
# "included Bar"
# "Foo class good job!"
# "Bar class good job!"

こういうふうに簡単にできる。

終わり

  • モジュールをインクルードすることでモジュールのメソッドをインスタンスメソッドとして使用できる
  • includedメソッドを使ってextendさせたらインスタンスメソッドを使いながらクラスメソッドも使用できる
  • concernsを使ったらもっと簡単にmix-inができる
  • concernsで複雑な依存関係をさらに簡単にまとめられる

参考

Rails: includeされた時にクラスメソッドとインスタンスメソッドを同時に追加する頻出パターン
Rails 4.2からはmodule ClassMethodsではなくConcern#class_methodsを使おう
[Rails] ActiveSupport::Concern の存在理由