Reputation: 3959
How can i define dynamically a constant inside dynamically defined subclass of dynamically defined class, like this, but without NameError: uninitialized constant Foo::Bar
error:
Object.const_set('Foo',Class.new) {|klass| klass.const_set('Bar', Class.new){|subklass| subklass.const_set(:YEP,'Yep!')}}
to get:
> Foo::Bar::YEP #=> 'Yep!'
Upvotes: 2
Views: 113
Reputation: 110685
The following is equivalent to @SimpleLime's answer, just expressed differently.
def make_class(name, parent=Object)
Object.const_set(name, Class.new(parent))
end
foo = make_class('Foo')
#=> Foo
bar = make_class('Bar', foo)
#=> Bar
bar.superclass
#=> Foo
bar.const_set('YEP', 'Yep!')
Bar::YEP
#=> "Yep!"
bar::YEP
#=> "Yep!"
If desired, the variables could be factored out.
make_class('Bar', make_class('Foo')).const_set('YEP', 'Yep!')
Bar::YEP
# => "Yep!"
See Class::new and Module#const_set
Classes created dynamically are often used anonymously (nameless), in which case one would simply write:
foo = Class.new
bar = Class.new(foo)
bar.const_set('YEP', 'Yep!')
bar::YEP
#=> "Yep!"
and reference the classes by the variables foo
and bar
.
Upvotes: 3
Reputation: 11035
When you wrap method parameters in parenthesis, the curly brace block gets applied to the method call, not the last parameter in the list. So those { |klass| ... }
are being passed to Object.const_set
not to Class.new
. If you move that close parenthesis after the block it'll work because the blocks get passed to Class.new
instead of Object.const_set
:
Object.const_set('Foo',Class.new { |klass|
klass.const_set('Bar', Class.new { |subklass|
subklass.const_set(:YEP,'Yep!')
})
})
Foo::Bar::YEP # => "Yep!"
Upvotes: 5