Rubellum fly light

ほぼPHP日記

Module#prepend

Module#include

クラスAがモジュールMをincludeすると、メソッドの探索順は A → M → …のようになる。

つまり、モジュールMはクラスAの親クラス側に差し込まれる。

module M
  def hello
    puts "M hello"
  end
end

class A
  include M

  def hello
    super
    puts "A hello"
  end
end

A.new.hello
# => M hello
# => A hello

p A.ancestors  # 自身と親クラスの配列を返す
# => [A, M, Object, Kernel, BasicObject]

Module#prepend

Module#prependはクラスの子側(?)にモジュールを差し込む。

したがって、モジュールのメソッドで、インスタンスのメソッドをラッピングできる。

module M
  def hello
    puts "before"
    super
    puts "after"
  end
end

class A
  prepend M

  def hello
    puts "hello"
  end
end

A.new.hello
# => before
# => hello
# => after

p A.ancestors  # 自身と親クラスの配列を返す
# => [M, A, Object, Kernel, BasicObject]

差し込まれる順番は自然。

Module#includeはクラスのすぐ後ろ(親側)に追加していく。

Module#prependは先頭(子側)に追加していく。

module M1; end
module M2; end

class A
  include M1  # A -> M1
  include M2  # A -> M2 -> M1 (Aのすぐ後ろに追加していく)
end

class B
  prepend M1  # M1 -> B
  prepend M2  # M2 -> M1 -> B (先頭に追加していく)
end

p A.ancestors  # 自身と親クラスの配列を返す
# => [A, M2, M1, Object, Kernel, BasicObject]

p B.ancestors  # 自身と親クラスの配列を返す
# => [M2, M1, B, Object, Kernel, BasicObject]

Module#includeはメソッドの前に何かを実行するってことができなかったけど、Module#prependではそれができる。

メソッドの前後に処理を(自然に?)追加できるので便利ですね!

参考リンク

Ruby 2.0 : Module#prepend

Module#prepend - alias_method_chainが滅ぶ日 - I am Cruby!