There are two common usages for modules in Ruby: namespacing and mixing in.
An example of namespacing can be seen in the Math module:
Math::PI # 3.14159.... Math.log10(100) # 2.0
Mixing in a module will extend that class with additional constants and methods. Here’s an example where we’ll create our own module and mix it in:
module Taggable DEFAULT_TAG = 'none' def tag_as(tag) # since methods are mixed in, we have access to any instance methods/variables @tag = tag end end class Bookmark attr_reader :tag include Taggable end bookmark = Bookmark.new bookmark.tag_as('recent') bookmark.tag # 'recent'
Private instance/class methods for modules
Defining a private instance method in a module works just like in a class, you use the private keyword.
module Taggable private def finalize! # ... end end class Bookmark include Taggable end bookmark = Bookmark.new bookmark.finalize! # NoMethodError: private method `finalized!` called
You can define private class methods using the private_class_method method.
module TagUtils def self.uses_private_method default_tag end def self.default_tag 'none' end private_class_method :default_tag end TagUtils.default_tag # NoMethodError: private method `default_tag` called TagUtils.uses_private_method # 'none'
Mix in class methods
Whereas include will add instance methods, extend will add class methods.
module Taggable def default_tag 'none' end end class Bookmark include Taggable end class Page extend Taggable end Bookmark.new.default_tag # 'none' Bookmark.default_tag # NoMethodError Page.new.default_tag # NoMethodError Page.default_tag # 'none'
You’ve probably rarely used the extend method to add class methods. A common Ruby idiom for module authors is to abstract that away using an included hook so users won’t have to remember to use extend or include.
Anytime a class includes a module, it will call the class method self.included on the module. This lets us include any instance methods and class methods all at once:
module Taggable def self.included(base) # base is the class which is including this module base.extend(ClassMethods) end module ClassMethods # class methods go here def default_tag 'none' end end # instance methods go here def tag_as # ... end end class Bookmark include Taggable end Bookmark.default_tag # 'none' Bookmark.new.tag_as('awesome')
Overriding mixed in methods
When you’re including a module, it’s similar to inserting a superclass between your current class and its superclass. The super keyword works as expected. If you’re dealing with a complicated hierarchy, you’ll have to make sure modules are being mixed in the correct order. Let’s take a look:
module Bar def bar 'bar' end end module Bar2 def bar "#{super}2" end end class Foo include Bar include Bar2 def bar "#{super}3" end end Foo.new.bar # "bar23"
Foo implicitly inherits from Object. But when you mix in Bar and Bar2, their definition of bar gets inserted between Foo and Object:
Foo.ancestors # [Foo, Bar2, Bar, Object, Kernel]
When Foo#bar gets invoked, Ruby will first look at Foo for the definition. If it wasn’t there, it would continue looking up the chain until it reaches Kernel. Since it was there, it uses Foo#bar. The super keyword is a reference to its ancestor’s #bar implementation.
Handling naming collisions
What if your module defines a new constant which overrides with one of Ruby’s?
module MarshallableType class Float; end; Float # MarshallableType::Float end
The :: operator lets you traverse namespaces to find the correct constant you need. Ruby’s top-level global namespace is just Object, so you can still access Ruby’s float inside MarshallableType.
module MarshallableType class Float; end; Float # MarshallableType::Float Object::Float # plain Float ::Float # shortcut for Object::Float end