Ruby Modules
Modules serve two functions in Ruby, namespacing and mix-in functionality. Namespacing is a programming concept that separates and organizes code to avoid global collisions. This is similar to the idea of avoiding global variables as to not pollute the global namespace. By creating a module named 'x', you can organize all of the associated code under the 'x' namespace.
Modules also provide an option for mixing in functionality. As discussed previously, Ruby is a single inheritance language. In order to "mix in" other functionality we can include modules. By including modules we gain all of the associated methods within those modules. This allows us to share common methods across multiple classes or modules. The enumerable mix-in module is one example of sharing methods, as we'll explore in detail in the chapter, Enumerable.
Modules are defined by the module keyword, followed by the name of the module. The module ends with the keyword end.
module A
# here is all the code for module A
end
Modules can be mixed-in classes that reside in the same file using the include keyword. By mixing a module into a class, the class will gain access to all of the methods defined in the module. These methods will be available to the instances of the class.
module A
def say_hello(name)
puts "hello " + name
end
end
class User
include A # mix-in module A
end
>> user = User.new
>> user.say_hello("Bob")
hello Bob
The 'User' class has no methods defined but gained the "say_hello" method through the mix-in of module 'A'. Note: the method would be available on all instances of the 'User' class. Also, all code in the example is in the same file. If module 'A' were located in a separate file it could not be mixed-in with the keyword include. In that case, we would need to use the keyword require. This loads the module from another file and remembers that it is loaded. The keyword load would also work but it doesn't track if the file has already been loaded. To summarize, in cases where the modules are in the same file, use include. If the modules are in separate files, use require.
Modules are particularly handy when you have one method that is repeated in multiple classes. In order to follow the DRY principle, (Don't Repeat Yourself) the method can be extracted into a separate module and then included in the classes where it's needed. Consider the following example:
class Comment < ActiveRecord::Base
def word_count
body.split.count
end
end
class Article < ActiveRecord::Base
def word_count
body.split.count
end
end
# both classes contain the word_count method
# we can refactor and improve this code by creating a module
module TextContent
def word_count
body.split.count
end
end
# once created, we can rewrite our Comment and Article classes
class Comment < ActiveRecord::Base
include TextContent
end
class Article < ActiveRecord::Base
include TextContent
end
# we can call the method in the same way, article.word_count
Our example uses one rather simple method but the context should be clear. Now, if we need to make a change to the 'word_count' method we only have to change it in one place. This allows us to write better, more maintainable code.