类名的dynamic类定义
如何在Ruby中dynamic定义一个类名称?
我知道如何dynamic创build一个没有名字的类,像这样:
dynamic_class = Class.new do def method1 end end
但是你不能指定一个类名。 我想用名字dynamic创build一个类。
这是我想要做的一个例子,当然它实际上并不工作。
(请注意,我不是创build一个类的实例,而是一个类的定义)
class TestEval def method1 puts "name: #{self.name}" end end class_name = "TestEval" dummy = eval("#{class_name}") puts "dummy: #{dummy}" dynamic_name = "TestEval2" class_string = """ class #{dynamic_name} def method1 end end """ dummy2 = eval(class_string) puts "dummy2: #{dummy2}" # doesn't work
实际产出:
dummy: TestEval dummy2:
期望的输出:
dummy: TestEval dummy2: TestEval2
================================================== ====
答:使用sepp2k的方法完全dynamic的解决scheme
dynamic_name = "TestEval2" Object.const_set(dynamic_name, Class.new) dummy2 = eval("#{dynamic_name}") puts "dummy2: #{dummy2}"
一个类的名字就是引用它的第一个常量的名字。
即如果我做myclass = Class.new
,然后MyClass = myclass
,该类的名称将成为MyClass
。 但是我不能做MyClass =
如果我不知道类的名字,直到运行时。
所以你可以使用Module#const_set
来dynamic地设置一个const的值。 例:
dynamic_name = "ClassName" Object.const_set(dynamic_name, Class.new { def method1() 42 end }) ClassName.new.method1 #=> 42
我也一直在搞这个。 在我的情况下,我试图testingActiveRecord :: Base的扩展。 我需要能够dynamic创build一个类,并且由于活动logging根据类名查找表,所以该类不能是匿名的。
我不确定这是否有助于你的情况,但这是我想出的:
test_model_class = Class.new(ActiveRecord::Base) do def self.name 'TestModel' end attr_accessible :foo, :bar end
就ActiveRecord而言,定义self.name就足够了。 我猜这个实际上可以在一个class级不能匿名的情况下工作。
(我刚刚读了sepp2k的回答,我认为他的情况会好一些,不pipe怎么说,我会离开这里的。)
我知道这是一个非常古老的问题,其他一些Rubyists可能会拒绝来自社区的这个,但是我正在创build一个非常薄的包装gem,它包装了一个stream行的Java项目和Ruby类。 基于@ sepp2k的答案,我创build了一对帮助器方法,因为我必须在一个项目中多次执行此操作。 请注意,我命名空间这些方法,使他们不污染一些顶级命名空间,如对象或内核。
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[String, Class]] 2D array of [class, superclass] # where each class is a String 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[String, Module]] 2D array of [module, supermodule] # where each module is a String 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
要使用这些方法(请注意,这是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
为什么??
这使我可以创build一个使用Java项目的JRuby gem,并允许开源社区和我在将来根据需要修饰这些类。 它也创build了一个更友好的命名空间来使用类。由于我的gem是一个非常非常薄的包装,我不得不创build许多子类和模块来扩展其他模块。
正如我们在JD Power所说的,“这是道歉驱动的发展:我很抱歉”。
下面的代码如何?
dynamic_name = "TestEval2" class_string = """ class #{dynamic_name} def method1 end end """ eval(class_string) dummy2 = Object.const_get(dynamic_name) puts "dummy2: #{dummy2}"
Eval不会重新运行运行时类对象,至less在我的电脑上没有。 使用Object.const_get来获取Class对象。