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.new
pour créer une classe anonyme, puis utilisezdefine_method
pour ajouter des méthodes, et enfin d'appel deconst_set
pour 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)