Reputation: 10564
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?
Upvotes: 14
Views: 10451
Reputation: 1213
As of 2024 and Rails 7.x:
Module.parent is deprecated.
It's now module_parent
:
> Auth::User.module_parent
=> Auth
> Auth::User.module_parent_name
=> "Auth"
> Auth::User.module_parents
=> [Auth,Object]
And, in the docs: https://api.rubyonrails.org/classes/Module.html
Upvotes: 2
Reputation: 373
In case anyone is looking for a pure ruby version:
def get_parent_type
#Note: This will break for base types (lacking '::' in the name)
parent_type=self.class.name.split('::')[0...-1]
begin
Object.const_get(parent_type.join('::'))
rescue NameError => e
nil
end
end
Upvotes: 2
Reputation: 837
In Rails you can use a combination of deconstantize and constantize.
'Foo::Bar::Baz'.deconstantize.constantize # => Foo::Bar
so in a method of the class it can be used like this:
self.class.name.deconstantize.constantize
Upvotes: 1
Reputation: 66837
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
Upvotes: 18
Reputation: 10564
Got it. ActiveSupport has this Ruby extension, Module#parent. It's good enough for my use.
Upvotes: 13