In Ruby I create a module like this:
Mod = Module.new do
class MyClass
attr_reader :a
def initialize(a)
@a = a
end
end
end
And I'm trying to create an instance of the class:
puts Mod::MyClass.new(1).a
But I get an error:
(irb):10:in `<main>': uninitialized constant Mod::MyClass (NameError)
puts Mod::MyClass.new(1).a
Although that's how it works:
Mod.module_eval { puts MyClass.new(1).a }
1
=> nil
I'm also trying to make the class public:
Mod.instance_eval do
public_constant :MyClass
end
But I also get an error:
(irb):16:in `public_constant': constant Mod::MyClass not defined (NameError)
public_constant :MyClass
But if you create it like this, then everything works:
module Mod2
class MyClass
attr_reader :a
def initialize(a)
@a = a
end
end
end
puts Mod2::MyClass.new(1).a
1
=> nil
Is there a way to access Mod::MyClass
without eval
?
puts Mod::MyClass.new(1).a
The module
keyword creates a namespace:
Module.nesting #=> [] # <- top level
module Mod
Module.nesting #=> [Mod] # <- module namespace
class MyClass
Module.nesting #=> [Mod::MyClass, Mod]
end
end
whereas Module.new
doesn't:
Module.nesting #=> [] # <- top level
Mod = Module.new do
Module.nesting #=> [] # <- still top level
class MyClass
Module.nesting #=> [MyClass]
end
end
Which is why your class is defined on the top level despite being created from inside the module. Namespace-wise, your code is equivalent to:
Mod = Module.new do
# ...
end
class MyClass
# ...
end
You can use the scope resolution operator ::
to explicitly define a class under an existing module. (see the docs on Nesting)
Within the Module.new
block this would either be self
:
Mod = Module.new do
class self::MyClass
# ...
end
end
or the block's argument which refers to the module object:
Mod = Module.new do |m|
class m::MyClass
# ...
end
end
or the constant you're assigning the anonymous module to: (note that the constant becomes available only after the block has been evaluated)
Mod = Module.new do
# ...
end
class Mod::MyClass
# ...
end
... depending on your use case.