30

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:

  1. 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.
  2. 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?

Little Bobby Tables
  • 5,261
  • 2
  • 39
  • 49

3 Answers3

31

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)

...
Dylan Lukes
  • 935
  • 7
  • 10
5

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>
Julik
  • 7,676
  • 2
  • 34
  • 48
  • 5
    This does not create a class with a dynamic name. SomeClass is statically determined. – Dylan Lukes Jul 05 '11 at 13:53
  • Not really. Constant name will be dedeuced when the class is being used for something. https://gist.github.com/1064909 – Julik Jul 05 '11 at 14:13
  • 2
    in your example, you're defining your new class to "SomeClass". The example you just pasted contradicts your statement that "You don't really need to use const_set". You do need to use it to bind something to a module constant. – Dylan Lukes Jul 05 '11 at 14:18
  • In that sense you are right I thought you meant "a class wth a dynamic name" to be == anon class. – Julik Jul 05 '11 at 14:24
4

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)
msroot
  • 817
  • 11
  • 13