Définition de classe dynamic AVEC un nom de classe

Comment définir dynamicment une classe dans Ruby AVEC un nom?

Je sais créer dynamicment une classe sans nom en utilisant quelque chose comme:

dynamic_class = Class.new do def method1 end end 

Mais vous ne pouvez pas spécifier un nom de classe. Je veux créer une classe dynamicment avec un nom.

Voici un exemple de ce que je veux faire mais bien sûr, cela ne fonctionne pas.
(Notez que je ne crée pas une instance d’une classe mais une définition de classe)

 class TestEval def method1 puts "name: #{self.name}" end end class_name = "TestEval" dummy = eval("#{class_name}") puts "dummy: #{dummy}" dynamic_name = "TestEval2" class_ssortingng = """ class #{dynamic_name} def method1 end end """ dummy2 = eval(class_ssortingng) puts "dummy2: #{dummy2}" # doesn't work 

Sortie réelle:

 dummy: TestEval dummy2: 

Sortie désirée:

 dummy: TestEval dummy2: TestEval2 

================================================== ====

Réponse: Une solution totalement dynamic utilisant la méthode de sepp2k

 dynamic_name = "TestEval2" Object.const_set(dynamic_name, Class.new) dummy2 = eval("#{dynamic_name}") puts "dummy2: #{dummy2}" 

Le nom d’une classe est simplement le nom de la première constante qui y fait référence.

Par exemple, si myclass = Class.new et MyClass = myclass , le nom de la classe deviendra MyClass . Cependant, je ne peux pas faire MyClass = si je ne connais pas le nom de la classe avant l’exécution.

Au lieu de cela, vous pouvez utiliser le Module#const_set , qui définit dynamicment la valeur d’un const. Exemple:

 dynamic_name = "ClassName" Object.const_set(dynamic_name, Class.new { def method1() 42 end }) ClassName.new.method1 #=> 42 

J’ai aussi joué avec ça. Dans mon cas, j’essayais de tester les extensions d’ActiveRecord :: Base. Je devais être capable de créer dynamicment une classe, et parce que l’enregistrement actif recherche une table basée sur un nom de classe, cette classe ne peut pas être anonyme.

Je ne sais pas si cela vous aide, mais voici ce que j’ai imaginé:

 test_model_class = Class.new(ActiveRecord::Base) do def self.name 'TestModel' end attr_accessible :foo, :bar end 

En ce qui concerne ActiveRecord, la définition de self.name était suffisante. Je suppose que cela fonctionnera dans tous les cas où une classe ne peut pas être anonyme.

(Je viens de lire la réponse de sepp2k et je pense que la sienne est meilleure. Je vais laisser ça ici quand même.)

Je sais que c’est une très vieille question, et certains autres Rubyistes pourraient me fuir de la communauté pour cela, mais je travaille à la création d’une gemme très mince qui enveloppe un projet Java populaire avec des classes Ruby. Sur la base de la réponse de @ sepp2k, j’ai créé quelques méthodes d’assistance, car je devais le faire plusieurs fois dans un même projet. Notez que j’ai nommé ces méthodes de manière à ne pas polluer certains espaces de noms de niveau supérieur tels que Object ou Kernel.

 module Redbeam # helper method to create thin class wrappers easily within the given namespace # # @param parent_klass [Class] parent class of the klasses # @param klasses [Array[Ssortingng, Class]] 2D array of [class, superclass] # where each class is a Ssortingng name of the class to create and superclass # is the class the new class will inherit from def self.create_klasses(parent_klass, klasses) parent_klass.instance_eval do klasses.each do |klass, superklass| parent_klass.const_set klass, Class.new(superklass) end end end # helper method to create thin module wrappers easily within the given namespace # # @param parent_klass [Class] parent class of the modules # @param modules [Array[Ssortingng, Module]] 2D array of [module, supermodule] # where each module is a Ssortingng name of the module to create and supermodule # is the module the new module will extend def self.create_modules(parent_klass, modules) parent_klass.instance_eval do modules.each do |new_module, supermodule| parent_klass.const_set new_module, Module.new { extend supermodule } end end end end 

Pour utiliser ces méthodes (notez que ceci est JRuby):

 module Redbeam::Options Redbeam.create_klasses(self, [ ['PipelineOptionsFactory', org.apache.beam.sdk.options.PipelineOptionsFactory] ]) Redbeam.create_modules(self, [ ['PipelineOptions', org.apache.beam.sdk.options.PipelineOptions] ]) end 

POURQUOI??

Cela me permet de créer un joyau JRuby qui utilise le projet Java et qui permettrait à la communauté open source et à moi-même de décorer ces classes si nécessaire. Cela crée également un espace de noms plus convivial pour utiliser les classes. Comme mon joyau est un wrapper très, très mince, j’ai dû créer beaucoup, beaucoup de sous-classes et de modules pour étendre d’autres modules.

Comme nous le disons chez JD Power, “c’est un développement basé sur les excuses: je suis désolé”.

Que diriez-vous du code suivant:

 dynamic_name = "TestEval2" class_ssortingng = """ class #{dynamic_name} def method1 end end """ eval(class_ssortingng) dummy2 = Object.const_get(dynamic_name) puts "dummy2: #{dummy2}" 

Eval ne réitère pas l’object de classe d’exécution, du moins sur mon PC. Utilisez Object.const_get pour obtenir l’object Class.