Understanding Namespaces in Ruby

Ruby namespacing

When you use

#The code in question
module Api
module V1
class UserController < ApplicationController
end
end
end

ApplicationControllerdefinition will be searched in Api::V1 then if not found in Api then if not found in the root namespace.

I agree it could be confusing, that's why I tend to use absolute paths like so: ::ApplicationController

If ever I'd need Api::ApplicationController, I'd write ::Api::ApplicationController

Basically the :: tells ruby to start from the root namespace and not from where the code lives.


Sidenote

Be aware that there are vicious cases in Rails development mode. In order to gain speed, the strict minimum is loaded. Then Rails looks for classes definitions when needed.

But this sometimes fails big time example, when you have say ::User already loaded, and then look for ::Admin::User. Rails would not look for it, it will think ::User does the trick.

This can be solved using require_dependency statements in your code. Speed has a cost :)

Understanding namespaces in Ruby

ZipkinTracer is inside of Trace namespace, like this:

module Trace
class ZipkinTracer
# ...
end
end

The :: before constant name means that you point to the root. For example in the following code:

class Class1
end

module Module1
class Class1
end

def foo
::Class1
end
end

::Class1 ensures that you refer to the "root" Class1. If you had:

def foo
Class1
end

the Module1::Class1 would be referred.

What's the difference between these Ruby namespace conventions?

The difference lies in nesting.

In the example below, you can see that the former method using class Foo, can get the outer scope's constant variables BAR_A without errors.

Meanwhile, class Baz will bomb with an error of uninitialized constant A::B::Baz::BAR_A. As it doesn't bring in A::* implicitly, only A::B::*explicitly.

module A
BAR_A = 'Bar A!'
module B
BAR_B = 'Bar B!'
class Foo
p BAR_A
p BAR_B
end
end
end

class A::B::Baz
p BAR_A
p BAR_B
end

Both behaviors have their place. There's no real consensus in the community in my opinion as to which is the One True Ruby Way (tm). I personally use the former, most of the time.

Ruby namespacing with a class vs. module?

The most immediate benefit to using modules for namespacing is that you can use include to import the namespace, and use the constants declared within it unqualified:

module Foo; class Bar; end; end

module Elsewhere
include Foo
Bar.new
end

Ruby Class namespacing with modules: Why do I get NameError with double colons but not module blocks?

It may seem counter-intuitive, but constant lookup in Ruby is done using current lexical scope, i.e. the current lexical nesting level (location in the source code), not the semantic nesting level.

This can be tested by inspecting Module.nesting, which prints the current lexical scope:

class Foo::SecondClass
pp Module.nesting # -> [Foo::SecondClass]
end

module Foo
class SecondClass
pp Module.nesting # -> [Foo::SecondClass, Foo]
end
end

Since Ruby uses this nesting level for symbol lookup, it means in the situation where you try to look up FirstClass within nesting [Foo::SecondClass], Ruby will not find it.

However when you try to look it up within nesting [Foo::SecondClass, Foo], it will find FirstClass under Foo, just like you expect.

To get around this, you could do:

class Foo::SecondClass
def meth
Foo::FirstClass.new.meth
end
end

Which will now work as you expect, since you provided the necessary lookup hint for FirstClass, and told Ruby it is inside Foo.

What is a Namespace?

A namespace provides a container to hold things like functions, classes and constants as a way to group them together logically and to help avoid conflicts with functions and classes with the same name that have been written by someone else.

In Ruby this is achieved using modules.

Rails, Ruby classes / modules / namespaces confusion

You may need an intermediate A::B declared in a/b.rb to properly define the module A::B before you reference it, or you can re-style your declaration in your final file.

For example:

# a/b/c.rb
class A::B::C
end

This depends on module or class A and A::B being defined before that file is loaded.

You can side-step this:

class A
module B
class C
end
end
end

That will force-declare all the intermediates. As this introduces a lot of indentation, a slightly less messy approach is:

class A
module B
end
end

class A::B::C
end

Of course, having an a.rb and a/b.rb with the appropriate intermediate declarations often helps avoid all of this.

Namespacing in Ruby require

You will want to wrap your code in an actual module or class

something like

class Foo
def self.bar
"quxx"
end
end

This way, you will be able to call Foo.bar to access your method.

Proper rails model namespacing

Indeed, if you do that, Ruby will complain because Party can't be a module and a class at the same time. So at least, Party would need to remain a class.

Now as the "Rails way" is concerned, we don't usually subclass our associations (ie. your Chat will probably belong_to a Party), we just put all our models in the root namespace (unless one has a good reason to). So you'd have Party at app/models/party.rb and Chat at app/models/chat.rb.

I'm guessing one would make a namespaced subclass Party::Chat only if there's a different ::Chat already present, and even that could lead to trouble with Ruby's constant lookup which is sometimes counter-intuitive.



Related Topics



Leave a reply



Submit