Mix-inにおけるModule#includeとObject#extend

これもメモです。

2014/7/30追記(まとめ)

includeとextendの違いについて。
オブジェクト指向スクリプト言語 Ruby」pp.195-196 から引用

モジュールを Mix-in としてクラスに「混ぜ込む」ためには include を使います。 include は Class クラスのメソッドで、あるモジュールで定義されているメソッドや定数を対象のクラスやモジュールに混ぜ込みます。正確には、そのクラスとスーパークラスの間にそのモジュールと同じ性質を持つ隠れたクラスを挟み込んだのと同じような働きをします。

補足すると、モジュールのメソッドがインスタンスメソッドとして働くということですね。

メソッドに対して特異メソッド(引用者註:クラスメソッド)があるように、クラスに対する include に対して、オブジェクト(引用者註:インスタンス)に対してモジュールの属性を追加する extend があります。 extend はモジュールに定義されているメソッドを特異メソッドとしてそのオブジェクトに追加する働きがあります。
extend は以下のような用途で使われます。

  • オブジェクト(引用者註:インスタンス)にある性質をまとめて付加する
  • クラスにクラスメソッドを追加する

こちらは(最後に記載されていますが)クラスメソッドとして追加するわけです。
言い直すと、クラスはClassクラスのインスタンスである、というRubyの言語としての性質から「オブジェクトに対してモジュールの属性を追加する extend」を使ってクラスにクラスメソッドを追加できるわけです。

いずれにせよモジュールのメソッドを(includeで)インスタンスメソッドへ組み込む、または(extendで)クラスメソッドなど特異メソッドへ組み込むわけで、モジュールのモジュールメソッド(特異メソッド)は組み込まれないということになります。
(追記ここまで)

もとの記事

moduleの取扱についてハッキリした気がしたので、http://memo.yomukaku.net/entries/319から引用させて貰いました*1

mix-inする対象のmodule
method_1とクラスメソッド*2class_method_1を持っている

# module1.rb

# coding:utf-8
module Module1
  def self.class_method_1
    puts 'output form class_method_1'
  end
  
  def method_1
    puts 'output from method_1'
  end
end

引用者2014/7/30追記:
Module1モジュールのメソッドと特異メソッド(モジュールメソッド)を確認

Module1.instance_methods
#=> [:method_1]
Module1.singleton_methods
#=> [:class_method_1]
includeを使った方法

includeを使うと、モジュールのメソッドがincludeしたクラスのインスタンスメソッドとして使える

# coding:utf-8
require './module1'

class Sample
  include Module1
  
  # moduleのクラスメソッドが使える
  Module1.class_method_1
  # output form class_method_1
  
  # こんなことはできない
  begin
    Module1.method_1
  rescue Exception => e
    p e
    # #<NoMethodError: undefined method `method_1' for Module1:Module>
  end
end

# Sampleクラスのインスタンスを作る
sample = Sample.new

# moduleのメソッドがSampleクラスのインスタンスメソッドとして使用できる。
sample.method_1
# output from method_1

引用者2014/7/30追記:
module1モジュールをincludeされたSampleクラスのインスタンスメソッドと特異メソッド(クラスメソッド)を確認

Sample.instance_methods - Object.instance_methods
=> [:method_1]
Sample.singleton_methods
=> []

モジュールのクラスメソッド*3(Module1.class_method_1)は、上記のように名前空間を指定して使えるが、includeしたクラスのクラスメソッドとしては使えない*4

Sample.method_1
#=> NoMethodError: undefined method `method_1' for Sample:Class
Sample.class_method_1
#=> NoMethodError: undefined method `class_method_1' for Sample:Class
extendを使った方法

extendを使うと、モジュールのインスタンスメソッド*5がクラスのクラスメソッドとして使える

# coding:utf-8

require './module1'

class Sample
  extend Module1

  # moduleのクラスメソッドが使える
  Module1.class_method_1
  # output form class_method_1
  
  # こんなことはできない
  begin
    Module1.method_1
  rescue Exception => e
    p e
    # <NoMethodError: undefined method `method_1' for Module1:Module>
  end
  
  # extendするとmoduleのメソッドが自クラスのクラスメソッドとして使える
  self.method_1
  # output from method_1
end

# Sample.class_method_1はできない
begin
  Sample.class_method_1
rescue Exception => e
  p e
  # #<NoMethodError: undefined method `class_method_1' for Sample:Class>
end

# Sampleクラスは先のようにmoduleのメソッドを自分のクラスメソッドとして持っている
Sample.method_1
# output from method_1

# Sampleクラスのインスタンスを作る
sample = Sample.new

# moduleのメソッドはSampleクラスのインスタンスメソッドではない
begin
  sample.method_1
rescue Exception => e
  p e
  # <NoMethodError: undefined method `method_1' for #<Sample:0x0000010085a0b0>>
end
さらにincludeでもextendでも

モジュールのクラスメソッドはmix-inしたクラスのクラスメソッドとして使える。(2014/7/30削除)

引用者2014/7/30追記:
module1モジュールをextendされたSampleクラスのインスタンスメソッドと特異メソッド(クラスメソッド)を確認

Sample.instance_methods - Object.instance_methods
#=> []
Sample.singleton_methods
#=> [:method_1]

るりま

instance method Object#extend

extend(*modules) -> self
引数で指定したモジュールのインスタンスメソッドを self の特異 メソッドとして追加します。
Module#include は、クラス(のインスタンス)に機能を追加します が、extend は、ある特定のオブジェクトだけにモジュールの機能を追加 したいときに使用します。

module Foo
  def a
    'ok Foo'
  end
end

module Bar
  def b
    'ok Bar'
  end
end

obj = Object.new
obj.extend Foo, Bar
p obj.a #=> "ok Foo"
p obj.b #=> "ok Bar"

class Klass
  include Foo
  extend Bar
end

p Klass.new.a #=> "ok Foo"
p Klass.b     #=> "ok Bar"

extend の機能は、「特異クラスに対する Module#include」 と言い替えることもできます。

# obj.extend Foo, Bar とほぼ同じ
class << obj
  include Foo, Bar
end

instance method Object#extend (Ruby 1.9.3)

トップレベルというかmainでのinclude

mainという暗黙のObjectクラスのインスタンスがselfである。トップレベルで何らかのメソッドを呼び出すと、self.method、つまりmainオブジェクトのメソッドを呼び出す形になる。
 :
def self.hogeで定義したメソッドは全てのクラスのクラスメソッドになり、def hogeで定義したメソッドは全てのインスタンスメソッドになる。
 :
class文の中のincludeはModuleクラスのクラスメソッドであるところのincludeだけど、トップレベルのincludeは微妙に特殊で、トップレベルのincludeメソッドつまりmainオブジェクトのincludeメソッドを実行するとObjectクラスのincludeクラスメソッドを呼んだような挙動をする。
 :
mainオブジェクトはObjectクラスのインスタンスなのでincludeしたモジュールの中でdef fugaとして定義されていたメソッドがインスタンスメソッドとして使える、そのためfugaが組み込み関数のように見える
requireとincludeとextendとmodule_function(1) : As Sloth As Possible

この記事(連作)は他の内容も良いので再読予定。
requireとincludeとextendとmodule_function(1) : As Sloth As Possible
requireとincludeとextendとmodule_function(2) : As Sloth As Possible

*1:が、追記したことがあったので、結局はハッキリしてなかったわけで^^;;

*2:モジュールメソッドと書くべきかも

*3:モジュールメソッド

*4:引用元の著者もどうやら「クラスメソッド」の意味を勘違いしているようですね…

*5:モジュールはインスタンスを生成できないので「インスタンスメソッド」という表現はあり得ないのですが、分かりやすくするための表記だと思います