Modules serve two purposes:
An example of a module as a namespace:
module Curses
class Window
end
end
Curses::Window.new
Library authors are advised to put their definitions inside a module to avoid name clashes. The standard library usually doesn't have a namespace as its types and methods are very common, to avoid writing long names.
To use a module as a partial type you use include
or extend
.
An include
makes a type include methods defined in that module as instance methods:
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
In the above example, it is as if we pasted the size
method from the module into the Items
class. The way this really works is by making each type have a list of ancestors, or parents. By default this list starts with the superclass. As modules are included they are prepended to this list. When a method is not found in a type it is looked up in this list. When you invoke super
, the first type in this ancestors list is used.
A module
can include other modules, so when a method is not found in it it will be looked up in the included modules.
An extend
makes a type include methods defined in that module as class methods:
module SomeSize
def size
3
end
end
class Items
extend SomeSize
end
Items.size #=> 3
Both include
and extend
make constants defined in the module available to the including/extending type.
Both of them can be used at the top level to avoid writing a namespace over and over (although the chances of name clashes increase):
module SomeModule
class SomeType
end
def some_method
1
end
end
include SomeModule
SomeType.new # OK, same as SomeModule::SomeType
some_method # OK, 1
A common pattern for modules is extend self
:
module Base64
extend self
def encode64(string)
# ...
end
def decode64(string)
# ...
end
end
In this way a module can be used as a namespace:
Base64.encode64 "hello" #=> "aGVsbG8="
But also it can be included in the program and its methods can be invoked without a namespace:
include Base64
encode64 "hello" #=> "aGVsbG8="
For this to be useful the method name should have some reference to the module, otherwise chances of name clashes are high.
A module cannot be instantiated:
module Moo
end
Moo.new # undefined method 'new' for Moo:Class