1

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
4
  • 1
    MyClass is defined in the outer scope, so referring to it as MyClass will work. This occurs because Module.new do does not create a proper "scope gate" Commented Jun 17 at 23:58
  • 1
    you'd have to define it like this class self::MyClass Commented Jun 18 at 0:35
  • "access Mod::MyClass without eval" – even with eval there’s no Mod::MyClass. You’re merely referring to the top level class from within the module. Commented Jun 18 at 6:01
  • Just wondering because of puts Mod::MyClass.new(1).a – why are you creating an anonymous module in the first place if you're referring to it via a constant anyway? Or is this more of a conceptual question? Commented Jun 25 at 13:12

1 Answer 1

6

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.