k-tokitoh

2019-07-16

特異メソッドのmix-in

mix-in における特異メソッドの扱いがよく分かっていなかったのでメモ。

結論は以下。

例として、以下のメソッドを呼び出せるように mix-in することを考えていく。

継承

class Class_parent
  def i_method
    puts 'this is instance method'
  end

  def self.s_method
    puts 'this is singleton method'
  end
end

class Class_a < Class_parent
end

Class_a.new.i_method

# => this is instance method

Class_a.s_method

=> this is singleton method

include のみ

module Module_1
  def i_method
    puts 'this is instance method'
  end

  def self.s_method
    puts 'this is singleton method'
  end
end

class Class_a
  include Module_1
end

Class_a.new.i_method

# => this is instance method

Class_a.s_method

# => NoMethodError: undefined method `s_method' for Class_a:Class

extend のみ

module Module_2
  def s_method
    puts 'this is singleton method'
  end
end

class Class_a
  extend Module_2
end

Class_a.new.i_method

# => NoMethodError: undefined method `i_method' for #<Class_a:0x007f8a960357c8>

Class_a.s_method

# => this is singleton method

include と extend をそれぞれ明示的に行う

module Module_1
  def i_method
    puts 'this is instance method'
  end
end

module Module_2
  def s_method
    puts 'this is singleton method'
  end
end

class Class_a
  include Module_1
  extend Module_2
end

Class_a.new.i_method

# => this is instance method

Class_a.s_method

# => this is singleton method

extend するとフックで include も行うようにする

module Module_2
  def s_method
    puts 'this is singleton method'
  end

  def self.extended(base)
    base.include Module_1
  end

  module Module_1
    def i_method
      puts 'this is instance method'
    end
  end
end

class Class_a
  extend Module_2
end

Class_a.new.i_method

# => this is instance method

Class_a.s_method

# => this is singleton method

include するとフックで extend も行うようにする

module Module_1
  def i_method
    puts 'this is instance method'
  end

  def self.included(base)
    base.extend Module_2
  end

  module Module_2
    def s_method
    puts 'this is singleton method'
    end
  end
end

class Class_a
include Module_1
end

Class_a.new.i_method

# => this is instance method

Class_a.s_method

# => this is singleton method

ActiveSupport::Concern をつかう

ClassMethods モジュール

ClassMethods モジュール内に定義したメソッドを特異メソッドとして mix-in してくれる。

require 'active_support'

module Module_1
  extend ActiveSupport::Concern

  def i_method
    puts 'this is instance method'
  end

  module ClassMethods
    def s_method
      puts 'this is singleton method'
    end
  end
end

class Class_a
  include Module_1
end

Class_a.new.i_method

# => this is instance method

Class_a.s_method

# => this is singleton method

included ブロック

included ブロック内の処理を、include した先のクラス内で実行してくれる。 なのでここで特異メソッドを定義したり、(似たようなものだが)scope を定義したりできる。

require 'active_support'

module Module_1
  extend ActiveSupport::Concern

  def i_method
    puts 'this is instance method'
  end

  included do
    def self.s_method
      puts 'this is singleton method'
    end
  end
end

class Class_a
  include Module_1
end

Class_a.new.i_method
# => this is instance method
Class_a.s_method
# => this is singleton method