okliv
okliv

Reputation: 3959

How can i define dynamically a constant inside dynamically defined subclass of dynamically defined class

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

Answers (2)

Cary Swoveland
Cary Swoveland

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

Simple Lime
Simple Lime

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

Related Questions