devio
devio

Reputation: 1169

How Do Modules Resolve A Constant's Scope?

I've been reading this Ruby book, then there's this example that I haven't well understood :

CONST = "outer"
module Mod
    CONST = 1
    def Mod.method1
        # module method
        CONST + 1
    end
end
module Mod::Inner
    def (Mod::Inner).method2
        CONST + " scope"
    end
end
Mod::CONST  # => 1
Mod.method1 # => 2
Mod::Inner::method2 # => "outer scope"

Would you please explain this to me (a detailed explanation) so I can fully understand how the scope works in Ruby. Thank you.

Upvotes: 1

Views: 230

Answers (2)

bjhaid
bjhaid

Reputation: 9762

You would find a good explanation on Ruby Constant lookup here

I would share code snippets with some explanation:

CONST = "outer"
module Mod
  CONST = 1
  def Mod.method1
    # module method
    Module.nesting # => [Mod]
    CONST + 1
  end
end
module Mod::Inner
  def (Mod::Inner).method2
    Module.nesting # => [Mod::Inner]
    CONST + " scope"
  end
end


Mod::CONST  # => 1
Mod.method1 # => 2
Mod::Inner::method2 # => "outer scope"

Have a look at the evaluation of Module.nesting above (Mod::Inner).method2 looks in Mod::Inner as specified in the nesting for CONST which it does not find before it calls the main Object for CONST

In the example I have below, you would see that Mod::Inner.method2 makes a call to Mod::Inner and then Mod to seek for CONST which it finds, so no need to call the CONST in Object

module Mod
  CONST = 1
  def Mod.method1
    Module.nesting # => [Mod]
    CONST + 1
  end

  module Inner
    def (Mod::Inner).method2
      Module.nesting # => [Mod::Inner, Mod]
      CONST.to_s + " scope"
    end
  end
end


Mod::CONST  # => 1
Mod.method1 # => 2
Object::CONST # => "outer"
Mod::Inner::method2 # => "1 scope"

When in doubt use nested class/constant definition over lexical, it would always do the right thing as expected

Upvotes: 2

Peter Alfvin
Peter Alfvin

Reputation: 29409

Constants in Ruby (identifiers beginning with a capital letter) are accessible based on the lexical scope in which they are defined/accessed.

In method1, the CONST within the Mod scope takes precedence over the outermost CONST. In method2, the CONST within the Mod scope is not visible lexically, so the outermost CONST is accessed.

As for the method definitions themselves, when the name is qualified with a preceding module constant (e.g. Mod or Mod::Inner), the method is defined as a "module method" rather than as an instance method of self.class (the default).

Names in the module/method hierarchy are separated by :: or, alternatively in the case of the separator between the module and the module method, a ..

Update: Note that the reason why Mod's constants are not visible to method2 is that Mod was not separately "opened". The definition skipped directly to Mod::Inner. If the code were changed to:

module Mod
  module Inner
    def (Mod::Inner).method2
      ...

Then Mod's constants would be accessible to method2 and take precedence over any in the outer scope.

Upvotes: 2

Related Questions