Modules as namespaces
Constants in a module
A module is a container for names. A constant written inside it belongs to it, and the rest of the program reaches it with :::
module Shop
VERSION = "1"
end
puts Shop::VERSION
ruby -e 'module Shop; VERSION = "1"; end; puts Shop::VERSION'
1
Without the module, VERSION would sit at the top level and collide with every gem that has the same idea. The module prefix is the file system of a Ruby project: a namespace is a folder, and :: is the slash.
Nested modules
Modules nest, and the inner one is named with the whole path:
module Shop
module Payments
def self.name
"payments"
end
end
end
p Shop::Payments.name
ruby -e 'module Shop; module Payments; def self.name = "payments"; end; end; p Shop::Payments.name'
"payments"
The compact form module Shop::Payments works too, but only when Shop already exists: it reopens the constant instead of defining it. The nested form defines both, which is why a file usually writes the nested one.
How :: lookup works
Inside Shop::Payments, a plain constant is looked up in the inner module first, then in the enclosing ones, then at the top level. That is how an inner module reads a constant its parent defines:
ruby -e 'module Shop; X = 1; module Payments; def self.x = X; end; end; p Shop::Payments.x'
1
A leading :: skips the nesting and starts at the top: ::VERSION is the top-level constant even inside a module that has its own. Reaching for the leading form is usually a sign that two constants share a name and one of them should be renamed.
The lookup does not follow the class hierarchy for modules that are included. A constant defined in a mixin is found through the ancestors because the mixin is in them; a constant in a module that is merely required is not.
Namespace or mixin
The same keyword does two jobs. A namespace module holds constants and module methods and is never included; a mixin module holds instance methods and is included into a class. The difference is what the methods are: def self.x belongs to the module, def x belongs to whoever includes it.
module Formatting
def money(cents)
format("%.2f", cents / 100.0)
end
end
class Invoice
include Formatting
def total
money(1234)
end
end
puts Invoice.new.total
ruby -e 'module Formatting; def money(cents) = format("%.2f", cents / 100.0); end; class Invoice; include Formatting; def total = money(1234); end; puts Invoice.new.total'
12.34
module_function bridges the two: it makes a method callable as Formatting.money and, when the module is included, as a private instance method. It is the right choice for a helper that is useful both ways.
What to watch for
A module name is a constant, so a second definition reopens it rather
than replacing it. Two files that both say
module Shop add to the same module, which is
convenient and also how a typo in the name silently creates a second
namespace.
Naming a class with :: in a message is normal:
Shop::Payments::Error says exactly where the
error lives. Naming it that way inside its own module is not; a file in
the folder uses the short name.
Next: Singleton methods and class << self
Article author: Arthur Isaev