RubyのMixinを簡単にだけ整理したメモ

たまにはRubyを。

元ネタというかメインで参照したのはこちら

www.sitepoint.com

prependって結局どうなんだっけ?prependをクラスメソッドにするにはどうするんだっけ?というところを整理したかったので。

実行環境は、ruby2.3.1p112

ruby --version
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]

サンプルコード

Abstract moduleを各クラスに突っ込む。

module Abstract
  def self.included(klass)
    puts ""
    puts "Included hook: #{klass}"
    puts "ancestors: #{klass.ancestors}"
    puts "singleton: #{klass.singleton_class.ancestors}"
  end

  def self.prepended(klass)
    puts ""
    puts "Prepended hook: #{klass}"
    puts "ancestors:  #{klass.ancestors}"
    puts "singleton: #{klass.singleton_class.ancestors}"
  end

  def self.extended(klass)
    puts ""
    puts "Extended hook: #{klass}"
    puts "ancestors: #{klass.ancestors}"
    puts "singleton: #{klass.singleton_class.ancestors}"
  end

  def say
    puts "Hello, World!"
  end
end

class Include
  include Abstract

  def say
    puts "Hello!"
  end
end

Include.new.say

class Prepend
  prepend Abstract

  def say
    puts "Hello!"
  end
end

Prepend.new.say

class Extend
  extend Abstract

  def self.say
    puts "Hello!"
  end
end

Extend.say

class SelfPrepend
  class << self
    prepend Abstract

    def say
      puts "Hello!"
    end
  end
end

SelfPrepend.say

class ClassPrepend
  singleton_class.prepend Abstract

  def self.say
    puts "Hello!"
  end
end

ClassPrepend.say

アウトプット

Included hook: Include
ancestors: [Include, Abstract, Object, Kernel, BasicObject]
singleton: [#<Class:Include>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
Hello!

Prepended hook: Prepend
ancestors:  [Abstract, Prepend, Object, Kernel, BasicObject]
singleton: [#<Class:Prepend>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
Hello, World!

Extended hook: Extend
ancestors: [Extend, Object, Kernel, BasicObject]
singleton: [#<Class:Extend>, Abstract, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
Hello!

Prepended hook: #<Class:SelfPrepend>
ancestors:  [Abstract, #<Class:SelfPrepend>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
singleton: [#<Class:#<Class:SelfPrepend>>, #<Class:#<Class:Object>>, #<Class:#<Class:BasicObject>>, #<Class:Class>, #<Class:Module>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
Hello, World!

Prepended hook: #<Class:ClassPrepend>
ancestors:  [Abstract, #<Class:ClassPrepend>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
singleton: [#<Class:#<Class:ClassPrepend>>, #<Class:#<Class:Object>>, #<Class:#<Class:BasicObject>>, #<Class:Class>, #<Class:Module>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
Hello, World!

理解したこと

prependはancestorsで考えたときに、子孫側に挿入される。

include/extendはancestorsで考えたときに、祖先側に挿入される。

extendは、singleton_class(特異クラス)に対して挿入される。

クラスメソッド(として扱うもの)をprependする書き方は2パターンある。(詳しく追っていないので、どちらかがまずい方法だ、みたいなツッコミ歓迎。)