NameSpaces and Modules in Rails
Overview
I’ve read a few different posts and articles on how to organize your Rails app codebase. Most eventaully reference modules and namespaces as one good way to do it.
A lot of the basic examples show taking a module (or controller or view ) directory that looks like this:
models/
foo.rb
bar.rbAnd grouping the children (loose term) under the parent to bring connected code together (namespace or module).
models/
foo.rb
foo/
bar.rbThen you can update bar.rb from this:
class Bar < ActiveRecord::Base
endto:
class Foo::Bar < ActiveRecord::Base
endAnd this does work. Yay! (but don’t get too excited)
The above made me think that declaring a module and a namespace are 100% the same thing. I could write either of the following and I’d get the exact same result.
class Foo::Bar
# Compact style.
endis the same as:
module Foo
# Nested declaration.
class Bar
end
end##When $%^& Hit the Fan (ok it wasn’t that bad)
The thing that started to confuse me and made me question what I was doing is RuboCop was telling me to use nested definitions instead of the compact style. So I tried changing class Foo::Bar to module Foo ... and then I recieved an error saying Foo is not a module when I loaded my webpage.
When I started re-reading the comments in the blog post and the accepted answer on StackOverflow I realized what was happening.
In this case class Foo::Bar is looking for Foo to be defined and it finds it as a class so everything works. Once you try to break it out and declare Foo as a module it breaks because Foo is a class. So, if I wanted to keep the folder structure as is I could use the nested delcaration as follows:
class Foo
class Bar
end
endAnd Bam, it works! But am I really wanting to nest my classes?
What we’re really doing here is nesting classes and using it as a namespace and calling it a module. But it’s nesting classes to give reference to us (developers) as to how things are connected together. To me personally, I feel this is what modules do a little better, at least in this case. Kind of like saying, “Hey, these classes aren’t nested I just think they belong together in various ways so lets give them a common name.”
I had been trying to figure out what the difference between namespaces and modules are in Rails (and by extension Ruby). I had started to think they are the same and interchangable. But now, I’m looking at it like this: NameSpaces are a way to reference related things demonstrating hierarchy. There are different ways to accomplish namespacing (i.e. nesting classes, nesting modules, nesting modules and classes). The NameSpace does not define the type of things being nested/grouped.
Another interesting piece with the above Foo::Bar example is that Foo must already be defined when calling Foo::Bar. When you call Foo::Bar you are just looking it up. If Foo hasn’t been previsouly defined, you’ll get an error. In the first case you’re defining module Foo. The second is more of a reference. Also, you can’t define a class and a module with the same name.
Where I Landed
So for me, what I’m going to start trying is the following:
models/
foo.rb
bar.rbAnd grouping the children (loose term) under the parent to bring connected code together (namespace or module).
models/
foo.rb
foos/
bar.rbThen you can update bar.rb from this:
class Bar < ActiveRecord::Base
endto:
module Foo
class Bar < ActiveRecord::Base
end
endI’m doing this because I like the nested declaration of a module. I also feel that in these cases I’m not wanting to nest a class but group classes under a common area. To me a module seems to be appropriate. Maybe I’ll change my mind in the future, but at least I’ve thought it through and am understanding what I’m doing a bit more vs. blindly jumping in.
References
- This answer in a SO question really helped things click
- Namespaced Classes in Rails - the comments were helpful too
- ORGANIZING LARGE RAILS PROJECTS WITH NAMESPACES was a great example but it helped enforce my original understanding of namespacing and modules
- How DHH Organizes His Rails Controllers
- Using “::” instead of “module …” for Ruby namespacing
- Ruby - Lexical scope vs Inheritance