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:
- Utiliser
Class.newpour créer une classe anonyme, puis utilisezdefine_methodpour ajouter des méthodes, et enfin d'appel deconst_setpour ajouter nommé constantes de certains noms. - 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?
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)
...
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>
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)