Reputation: 961
I have a module defined as follows in app/presenters/my_namespace/base_presenter.rb
:
module MyNamespace
class BasePresenter
end
MY_SET = Set.new(['a', 'b', 'c'])
def self.my_method
true
end
end
When I run MyNamespace::MY_SET
or MyNamespace::my_method
in the console, I get an uninitialized constant error, but I can do MyNamespace::BasePresenter.new
. What am I doing wrong?
Upvotes: 0
Views: 1006
Reputation: 7482
I assume you're trying this out in Rails development mode. In this mode all constants (classes, modules and constants) are loaded by demand.
When ruby meets undefined constant, it throws an error, which autoloader intercepts and tries to load the constant by converting its name to a path.
In your case, autoloader tries to find
MyNamespace::MY_SET
in app/presenters/my_namespace.rb
(which fails) and has no idea that you actually defined it in app/presenters/my_namespace/base_presenter.rb
. But after you have loaded your MyNamespace::BasePresenter
(which, btw, lies on the correct path), MyNamespace
, MyNamespace::MY_SET
, and MyNamespace.my_method
got initialized and become available.
What you need to do is to
a) define MyNamespace
correctly and move methods and constants to its definition:
app/presenters/my_namespace.rb
module MyNamespace
MY_SET = Set.new(['a', 'b', 'c'])
def self.my_method
true
end
end
app/presenters/my_namespace/base_presenter.rb
# note that I don't open module here,
# but use a constant to enable autoloading of MyNamespace module
class MyNamespace::BasePresenter
end
or
b) just move all methods/constants to your BasePresenter
class. Since it lies on a correct path, constant MyNamespace::BasePresenter::MY_SET
will just work.
module MyNamespace
class BasePresenter
MY_SET = Set.new(['a', 'b', 'c'])
def self.my_method
true
end
end
end
The difference between
module MyNamespace
class BasePresenter
end
end
and
class MyNamespace::BasePresenter
end
is when MyNamespace
is undefined in first case it will be defined (module MyNamespace
either opens existing module or defines new one), but in second case the mechanism described above will try to load MyNamespace
somewhere, and if it fails - you'll get uninitialized constant error MyNamespace
.
What it also means to you is if you define all your namespaced classes as a first case (inside module MyNamespace
)
app/presenters/my_namespace/base_presenter.rb
module MyNamespace
class BasePresenter
end
end
and also have MyNamespace
in its proper place with some code inside
app/presenters/my_namespace.rb
module MyNamespace
MY_SET = Set.new(['a', 'b', 'c'])
end
And if your MyNamespace::BasePresenter
will gets loaded first, it'll actually define MyNamespace
and your app/presenters/my_namespace.rb
will not be loaded (since autoloading loads only missing constants), and you'll have to require
it yourself.
presenter = MyNamespace::BasePresenter.new
MyNamespace::MY_SET # boom, uninitialized constant error
The solution here is to define modules in 1 proper place (that autoloading knows how to find) and use class MyNamespace::BasePresenter
format for defining namespaced classes in their proper locations.
Upvotes: 2
Reputation: 168131
Probably you have not required the set
gem. The class Set
is defined in the set
gem. It is part of the standard library, but not part of Ruby core. In that case, creation of BasePresenter
succeeded because that was done prior to MY_SET = Set.new(['a', 'b', 'c'])
, which raised the error. MY_SET
and my_method
were not defined because those definitions appear on or after the offending line.
You need to do:
require "set"
Upvotes: 0
Reputation: 27961
There's nothing wrong with that code, which suggests that the problem is a load (or reload) problem. Stop and restart your console, and/or spring stop
if you have the default spring gem installed.
Upvotes: 0