Ruby provides two different syntaxes to nest modules (and classes):
# Syntax #1 module API module V1 end end # Syntax #2 module API::V1 end
Syntax #2 requires that
API module already exists. Most Rubyists know that.
Besides this difference, they think that these two syntaxes are interchangeable.
With that line of thinking, what syntax to use for nesting modules turns out to
be a matter of preference.
But, the syntax you choose matters, which I will demonstrate shortly with examples that relate to how you would organize a versioned REST API.
Modules are just constants in Ruby, as such, regular constant look-up rules apply. The very first rule relates to how modules are nested. For the purpose of this blog post, we will ignore regular constants and just focus on modules.
When you reference a constant in a nested context, Ruby looks it up inside out:
module API class Responder end module V1 class Controller def action Responder.respond_with('Hello, World!') end end end end
#action gets called, Ruby will see if
Responder is defined inside
API third, and finally in the
top level. If it wasn't for this inside-out look-up, referencing
above would not have worked. Instead, it would have to be referenced as
You can actually access the nesting information Ruby uses for this
module API class Responder end module V1 class Controller p Module.nesting #=> [API::V1::Controller, API::V1, API] def action Responder.respond_with('Hello, World!') end end end end
Now, let's see how the look-up behavior changes when we mix in syntax #2:
module API class Responder end end module API::V1 class Controller p Module.nesting #=> [API::V1::Controller, API::V1] def action Responder.respond_with('Hello, World!') end end end
#action gets called, you will now get a
NameError: uninitialized constant API::V1::Controller::Responder
This is because some nesting information gets lost when you use syntax #2.
You can see this from what
Module.nesting returns in the example above.
As per that example, Ruby now looks for
API::V1. It no longer looks inside
Responder is actually
defined hence the error.
The syntax you use for nesting modules in Ruby should not be a matter of preference. The syntax you use could be the difference between a working program, and the one that throws an error. So choose wisely. Having said that, I prefer syntax #1.
By the way, we've explored just one aspect of how constant look-up works in Ruby in general. If you use Rails, the matters gets even more complicated with the autoloading magic. The following references will help you learn more: