これ僕.com:行動分析学マニアがおくる行動戦略

意図と行動のギャップから生じる「不自由さ」への挑戦。果たして僕たちに自由はあるのか?

includeでクラスメソッドを定義

rubyでModuleをincludeしたときに、クラスメソッドも一緒にinclude(?)してくれないものかと、試行錯誤。

クラスメソッドをincludeしたい!でも・・・

module FooModule
  def self.bar
    ...
  end
end

というModuleがあるとする。で、

class BazClass
  include FooModule
end

としたときに、

BazClass.bar

といったように"barメソッド"がBazClassのクラスメソッドであるかのような振る舞いをしてくれないものかと。でも、moduleでselfと書くと、それはつまりmodule自身を指すんですよね?だから、

FooModule.bar

はできても、

BazClass.bar

はできない。

self.includedで解決

それで、色々と調べた結果、以下の方法が見つかった。

module FooModule
  module ClassMethods
    def bar
      ...
    end
  end

  def self.included(receiver)
    receiver.extend(ClassMethods)
  end
end

moduleがincludeされたタイミングで、include元に対してクラスメソッド用のmoduleをextendしている。extendは、

extend(module ... )

引数で指定したモジュールのインスタンスメソッドを self の特異 メソッドとして追加します。self を返します。

include は、クラス(のインスタンス)に機能を追加します が、extend は、ある特定のオブジェクトだけにモジュールの機能を追加 したいときに使用します。

ということになっているので、この場合、上記の「ある特定のオブジェクト」がinclude元のクラス自身になるという理解。結果、以下のように、include元ではさもクラスメソッドが定義されているかのような振る舞いをするようになった。

irb(main):001:0> module FooModule
irb(main):002:1>   module ClassMethods
irb(main):003:2>     def bar
irb(main):004:3>       print "Hello, World"
irb(main):005:3>     end
irb(main):006:2>   end
irb(main):007:1> 
irb(main):008:1*   def self.included(receiver)
irb(main):009:2>     receiver.extend(ClassMethods)
irb(main):010:2>   end
irb(main):011:1> end
=> nil
irb(main):012:0> class BazClass
irb(main):013:1>   include FooModule
irb(main):014:1> end
=> BazClass
irb(main):015:0> 
irb(main):016:0* BazClass.bar
Hello,World=> nil