2019-07-16
特異メソッドのmix-in
mix-in における特異メソッドの扱いがよく分かっていなかったのでメモ。
結論は以下。
- モジュールに定義された特異メソッドは mix-in されない
- いろいろ mix-in したい場合は include と extend を組み合わせる
- 「extended フックで include」、「included フックで extend」などの工夫でまとめて mix-in できる
- ActiveSupport::Concern を利用すると良しなにやってくれる
例として、以下のメソッドを呼び出せるように mix-in することを考えていく。
Class_a.new.i_method
Class_a.s_method
継承
Class_a.new.i_method
=> できる!Class_a.s_method
=> できる!
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 のみ
Class_a.new.i_method
=> できる!Class_a.s_method
=> できない
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 のみ
Class_a.new.i_method
=> できないClass_a.s_method
=> できる!
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 をそれぞれ明示的に行う
Class_a.new.i_method
=> できる!Class_a.s_method
=> できる!
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 も行うようにする
Class_a.new.i_method
=> できる!Class_a.s_method
=> できる!
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 も行うようにする
Class_a.new.i_method
=> できる!Class_a.s_method
=> できる!
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 してくれる。
Class_a.new.i_method
=> できる!Class_a.s_method
=> できる!
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