In Ruby, Modules are a way of grouping together methods, classes, and constants giving you two major benefits:
- provide namespacing and thus help prevent name collisions
- implement the mixin facility, providing a controlled way of adding functionality to classes
A module can also be used as a logical entity, containing non-state changing methods ("functions") that may be called, not only from the instance level but also from class level, with the module itself as the receiver and not having to prepend the module every time you want to use the functions.
# class level
MyModule.some_method
# instance level
include MyModule
some_method
There are multiple techniques to achieve this in Ruby, but the simplest and best known are Module#module_function
and extend self
.
module_function
module_function
creates module functions.
You can achieve this by:
module MyModule
def some_method
puts "MyModule#some_method"
end
module_function :some_method
end
This is very similar to writing:
module MyModule
def self.some_method
"MyModule#some_method"
end
private
def some_method
"MyModule#some_method"
end
end
This could also be written as the following if you want all the subsequently defined methods to become module functions:
module MyModule
module_function
def some_method
"MyModule#some_method"
end
end
The instance methods are made private:
MyModule.instance_method(:some_method).owner #=> MyModule
MyModule.public_method_defined?(:some_method) #=> false
MyModule.private_method_defined?(:some_method) #=> true
MyModule.method(:some_method).owner #=> #<Class:MyModule>
MyModule.method(:some_method).owner.singleton_class? #=> true
These functions are copies of the original, and so maybe changed independently:
class MyClass
include MyModule
def call_some_method
some_method
end
end
MyModule.some_method #=> "MyModule#some_method"
klass = MyClass.new
klass.call_some_method #=> "MyModule#some_method"
module MyModule
def some_method
"Overridden MyModule#some_method"
end
end
MyModule.some_method #=> "MyModule#some_method"
klass.call_some_method #=> "Overridden MyModule#some_method"
The advantage of module_function
is that when the module gets included in a class, these functions do not become public methods of the class and do not extend the class's public API, eliminating the need to maintain these methods for backward compatibility.
extend self
Another way to get something very similar is by extend self
. By extending self
within the module, all instance methods defined within the module will now be available at the module level and can also be called MyModule.some_method
.
module MyModule
extend self
def some_method
"MyModule#some_method"
end
end
Using extend
, the module will add its instance methods to its inheritance chain.
MyModule.instance_method(:some_method).owner #=> MyModule
MyModule.public_method_defined?(:some_method) #=> true
MyModule.method(:some_method).owner #=> MyModule
MyModule.method(:some_method).owner.singleton_class? #=> false
conclusion
module_function
makes the given instance methods private, then duplicates and puts them into the module's metaclass as public methods.
- creating copies of the original methods and they may be managed independently
- making the module instance methods visibility as private
On the other hand, extend self
adds all instance methods to the module's singleton, leaving their visibilities unchanged.
- there is no method copies made, if you want to modify a method's behavior you only need to do this in one place
- no side effects, like changing the method's visibility