Définir dynamiquement les classes nommées dans Ruby

J'écris un DSL interne dans Ruby. Pour cela, j'ai besoin de créer par programmation des classes nommées et des classes imbriquées. Quelle est la meilleure façon de le faire? Je reconnais qu'il y a deux façons de le faire:

  1. Utiliser Class.new pour créer une classe anonyme, puis utilisez define_method pour ajouter des méthodes, et enfin d'appel de const_set pour ajouter nommé constantes de certains noms.
  2. utilisez une sorte de eval

J'ai testé la première façon et cela a fonctionné, mais étant nouveau pour Ruby, Je ne suis pas sûr que mettre des classes en tant que constantes est la bonne façon.

Y a-t-il d'autres moyens, meilleurs? Si pas, ce qui est préférable?

23
demandé sur Little Bobby Tables 2011-07-05 17:26:13

3 réponses

Si vous voulez créer une classe avec un nom dynamique, vous devrez faire presque exactement ce que vous avez dit. Cependant, vous n'avez pas besoin d'utiliser define_method. Vous pouvez simplement passer un bloc à Class.new dans lequel vous initialisez la classe. C'est sémantiquement identique au contenu de class/end.

Rappelez-vous avec const_set, être consciencieux du récepteur (self) dans ce champ. Si vous voulez que la classe soit définie globalement, vous devrez appeler const_set sur le module TopLevel (qui varie en nom et en détail par Rubis).

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)

...
23
répondu Dylan Lukes 2011-07-05 13:58:04

Vous n'avez pas vraiment besoin d'utiliser const_set. La valeur de retour de Class.new peut être affectée à une constante et le bloc de Class.new est 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>
5
répondu Julik 2011-07-05 13:45:30

Devrait être comme ceci

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)
3
répondu msroot 2014-06-24 04:21:45