Ruby

Rubyのmixinの振る舞いを#~methodsを使って確認する

環境

Ruby 2.4.3

準備

下のようなmoduleとclassを用意します。

main.rb
module Included
  def self.included_class_method; end

  def included_public_instance_method; end

  protected

  def included_protected_instance_method; end

  private

  def included_private_instance_method; end

  module_function

  def included_module_function; end
end

module Extended
  def self.extended_class_method; end

  def extended_public_instance_method; end

  protected

  def extended_protected_instance_method; end

  private

  def extended_private_instance_method; end

  module_function

  def extended_module_function; end
end

class A
  include Included
  extend Extended
end

テスト

インスタンスメソッド

# public
A.public_instance_methods
# => [:included_public_instance_method, :remove_instance_variable, ...]

# protected
A.protected_instance_methods
# => [:included_protected_instance_method]

# private
A.private_instance_methods
# => [:included_private_instance_method, :included_module_function, :DelegateClass, ...]

モジュール関数はprivateのインスタンスメソッドとしてincludeされるようです。
あとはまあ予想通りです。

特異メソッド

# 特異メソッド
A.singleton_methods
# => [:extended_public_instance_method, :extended_protected_instance_method]

クラスメソッド

最後にクラスメソッドです。

# public
A.public_methods
# => [:extended_public_instance_method, :new, :allocate, :superclass, :<=>, ...]
A.public_methods(false)
# => [:extended_public_instance_method, :new, :allocate, :superclass]

# protected
A.protected_methods
# => [:extended_protected_instance_method]
A.protected_methods(false)
# => [:extended_protected_instance_method]

# private
A.private_methods
# => [:extended_private_instance_method, :extended_module_function, :initialize, :inherited, :using, ...]
A.private_methods(false)
# => [:extended_private_instance_method, :extended_module_function, :initialize, :inherited]

やはりモジュール関数はextendではprivateなクラスメソッドになるようです。
また、あまり予想していなかったことですが、#new#initialize等がfalseを渡したときにも含まれます。よくよく考えてみると自明ですが、これらはClassクラスのインスタンスメソッドです。

Class.instance_methods(false)
# => [:new, :allocate, :superclass]
Class.private_instance_methods(false)
# => [:initialize, :inherited]

まとめ

  • インスタンスメソッドはアクセス修飾子はそのままにinclude or extendされる
  • クラスメソッドはinclude or extendされない
  • モジュール関数はprivateのインスタンスメソッドとしてincludeされる
  • モジュール関数はprivateのクラスメソッドとしてextendされる

補足

今回使いませんでしたが、
#methods#public_methods + #protected_methods,
#instance_methods#public_instance_methods + #protected_instance_methods