I am writing an internal DSL in Ruby. For this, I need to programmatically create named classes and nested classes. What is the best way to do so? I recon that there are two ways to do so:
- Use
Class.new
to create an anonymous class, then use define_method
to add methods to it, and finally call const_set
to add them as named constants to some namespace.
- Use some sort of
eval
I've tested the first way and it worked, but being new to Ruby, I am not sure that putting classes as constants is the right way.
Are there other, better ways? If not, which of the above is preferable?
If you want to create a class with a dynamic name, you'll have to do almost exactly what you said. However, you do not need to use define_method
. You can just pass a block to Class.new
in which you initialize the class. This is semantically identical to the contents of class
/end
.
Remember with const_set
, to be conscientious of the receiver (self
) in that scope. If you want the class defined globally you will need to call const_set
on the TopLevel module (which varies in name and detail by Ruby).
a_new_class = Class.new(Object) do
attr_accessor :x
def initialize(x)
print #{self.class} initialized with #{x}"
@x = x
end
end
SomeModule.const_set("ClassName", a_new_class)
c = ClassName.new(10)
...
You don't really need to use const_set
. The return value of Class.new
can be assigned to
a constant and the block of Class.new
is class_eval
.
class Ancestor; end
SomeClass = Class.new(Ancestor) do
def initialize(var)
print "#{self.class} initialized with #{var}"
end
end
=> SomeClass
SomeClass.new("foo")
# SomeClass initialized with foo=> #<SomeClass:0x668b68>
Should be like this
a_new_class = Class.new(Object) do
attr_accessor :x
def initialize(x)
@x = x
end
end
SomeModule = Module.new
SomeModule.const_set("ClassName", a_new_class)
c = SomeModule::ClassName.new(10)