特異メソッドの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