Search code examples
rubymetaprogramming

Ruby: Is there a way to get the enclosing Module const of a Class?


I'm doing some metaprogramming in Ruby, and I need to dynamically generate a sibling class inside of a module. In doing so, I want to call const_set on the module, but I don't know which Module constant to call that on until runtime. An example:

Given classes

Foo::Bar::Baz
Foo::Quox::Quack

I want to be able to call a function like this (oversimplified here):

def generate_from klass
  mod = klass.enclosing_module # <- THIS LINE is the one I need to figure out
  mod.const_set("GeneratedClassName", Class.new)
end

and what I want to end up with, when calling with Baz, is a new class defined as

Foo::Bar::GeneratedClassName

and with a Quack, I want

Foo::Quox::GeneratedClassName

The only way I know of is to split up klass.name, then repeatedly call const_get on those strings, constantized. Does anyone know of a more elegant way?


Solution

  • This should get you on track:

    module Foo
      module Bar
        class Baz
          def initialize
            @nesting = Module.nesting
          end
    
          def enclosing_module
            @nesting.last
          end
        end
      end
    end
    
    puts Foo::Bar::Baz.new.enclosing_module #=> Foo
    

    Relevant documentation:

    http://ruby-doc.org/core/classes/Module.html#M000441