ruby-doc.org describes Module.nesting as “the list of Modules nested at the point of call.” What does this really mean? ...

ruby-doc.org describes Module.nesting as “the list of Modules nested at the point of call.” What does this really mean?


  $top_level_nesting = Module.nesting
  module M1
    $level_1_nesting = Module.nesting

    module M2
      $level_2_nesting = Module.nesting
    end
  end

  module M1::M2
    $re_opened_nesting = Module.nesting
  end

  global_variables.select {|v| v =~ /nesting/}.sort.each do |v|
    puts "#{v}: #{eval(v).inspect}" 
  end

Can you guess what this script outputs?


  $level_1_nesting: [M1]
  $level_2_nesting: [M1::M2, M1]
  $re_opened_nesting: [M1::M2]
  $top_level_nesting: []

In most of the cases it is straightforward, but $re_opened_nesting might surprise you. You’ve reopened the M2 module, which is was defined within M1, but the nesting doesn’t reflect this relationship. The reason is that the module was opened using M1::M2 as the module name, rather than reopening M1 and then reopening M2 from within M1. This point is perhaps more clear looking at this example.

  module M1
    module M2
    end
  end

  M3 = M1::M2
  M3.name #=> "M1::M2" 

  module M3
    Module.nesting #=> [M1::M2]
  end

In this case the module M1::M2 is assigned to a new constant M3 at the top level. This assignment does not change the identify of the module – the name is still M1::M2. When the module is reopened via the M3 constant, it’s more clear why there is only one entry in Module.nesting. When a module is opened using the module keyword, Ruby adds it to the current nesting list. It doesn’t matter whether the name that was originally used to define the module was nested under another module or not – it’s the use of the module keyword that causes the nesting to change.

Most of the time you won’t care about what Module.nesting is, but there is a case that really matters if you re-open classes and modules to add extensions or monkey patch code. When Ruby looks up the value of a constant, it uses the nesting to determine which constant to use.


  A = 1

  module M1
    A = 2

    module M2
      puts "A at #{Module.nesting.inspect}: #{A}" 
    end
  end

  module M1::M2
    puts "A at #{Module.nesting.inspect}: #{A}" 
  end

The output of the snippet above is


  A at [M1::M2, M1]: 2
  A at [M1::M2]: 1

You can see that there are two different constants called A, one nested inside M1 and one at the top level. When M1::M2 is reopened, one might expect the reference to A to evaluate to the value defined within M1, but instead the top level constant is used. If you ever encounter any unexpected effects dealing with constants when you’re reopening classes nested within modules, keep Module.nesting in mind and it may help you figure out what is going on.

Sorry, comments are closed for this article.