コンテンツにスキップ

モジュール

モジュールは2つの目的を果たします。

  • 他の型、メソッド、定数を定義するための名前空間として
  • 他の型にミックスインできる部分型として

名前空間としてのモジュールの例

module Curses
  class Window
  end
end

Curses::Window.new

ライブラリの作成者は、名前の衝突を避けるために、定義をモジュール内に入れることをお勧めします。標準ライブラリは、型とメソッドが非常に一般的であるため、長い名前を書くことを避けるために、通常は名前空間を持ちません。

モジュールを部分型として使用するには、includeまたはextendを使用します。

includeを使用すると、そのモジュールで定義されたメソッドをインスタンスメソッドとして型に含めることができます。

module ItemsSize
  def size
    items.size
  end
end

class Items
  include ItemsSize

  def items
    [1, 2, 3]
  end
end

items = Items.new
items.size # => 3

上記の例では、モジュールからsizeメソッドをItemsクラスに貼り付けたかのようです。これが実際に機能する方法は、各型に祖先または親のリストを持たせることです。デフォルトでは、このリストはスーパークラスで始まります。モジュールがインクルードされると、このリストの先頭に追加されます。メソッドが型で見つからない場合、このリストで検索されます。superを呼び出すと、この祖先リストの最初の型が使用されます。

moduleは他のモジュールを含めることができるため、メソッドがモジュールで見つからない場合は、含まれているモジュールで検索されます。

extendを使用すると、そのモジュールで定義されたメソッドをクラスメソッドとして型に含めることができます。

module SomeSize
  def size
    3
  end
end

class Items
  extend SomeSize
end

Items.size # => 3

includeextendの両方で、モジュールで定義された定数を含める/拡張する型で使用できるようにします。

どちらも、名前空間を何度も記述することを避けるためにトップレベルで使用できます(ただし、名前の衝突の可能性は高まります)。

module SomeModule
  class SomeType
  end

  def some_method
    1
  end
end

include SomeModule

SomeType.new # OK, same as SomeModule::SomeType
some_method  # OK, 1

extend self

モジュールの一般的なパターンは、extend selfです。

module Base64
  extend self

  def encode64(string)
    # ...
  end

  def decode64(string)
    # ...
  end
end

これにより、モジュールを名前空間として使用できます。

Base64.encode64 "hello" # => "aGVsbG8="

また、プログラムに含めることができ、そのメソッドは名前空間なしで呼び出すことができます。

include Base64

encode64 "hello" # => "aGVsbG8="

これが役立つには、メソッド名にモジュールへの参照が含まれている必要があります。そうでない場合は、名前の衝突の可能性が高くなります。

モジュールはインスタンス化できません。

module Moo
end

Moo.new # undefined method 'new' for Moo:Module

モジュールの型チェック

モジュールは型チェックにも使用できます。

ABという名前の2つのモジュールを定義する場合

module A; end

module B; end

これらはクラスに含めることができます。

class One
  include A
end

class Two
  include B
end

class Three < One
  include B
end

次に、これらのクラスのインスタンスに対して、クラスだけでなく、含まれているモジュールに対しても型チェックを実行できます。

one = One.new
typeof(one)  # => One
one.is_a?(A) # => true
one.is_a?(B) # => false

three = Three.new
typeof(three)  # => Three
three.is_a?(A) # => true
three.is_a?(B) # => true

これにより、クラスではなくモジュール型に基づいて配列やメソッドを定義できます。

one = One.new
two = Two.new
three = Three.new

new_array = Array(A).new
new_array << one   # Ok, One includes module A
new_array << three # Ok, Three inherits module A

new_array << two # Error, because Two neither inherits nor includes module A