Ruby 具有类名的动态类定义
如何在Ruby中用名称动态定义类? 我知道如何动态创建一个类,而不使用类似以下内容的名称:Ruby 具有类名的动态类定义,ruby,class,dynamic,metaprogramming,Ruby,Class,Dynamic,Metaprogramming,如何在Ruby中用名称动态定义类? 我知道如何动态创建一个类,而不使用类似以下内容的名称: dynamic_class = Class.new do def method1 end end 但不能指定类名。我想用一个名字动态创建一个类 下面是我想做的一个例子,但当然它实际上不起作用。 (请注意,我不是在创建类的实例,而是在创建类定义) 实际产量: dummy: TestEval dummy2: 期望输出: dummy: TestEval dummy2: TestEval2 ===
dynamic_class = Class.new do
def method1
end
end
但不能指定类名。我想用一个名字动态创建一个类
下面是我想做的一个例子,但当然它实际上不起作用。(请注意,我不是在创建类的实例,而是在创建类定义) 实际产量:
dummy: TestEval
dummy2:
期望输出:
dummy: TestEval
dummy2: TestEval2
======================================================
回答:使用sepp2k方法的完全动态解决方案
dynamic_name = "TestEval2"
Object.const_set(dynamic_name, Class.new) # If inheriting, use Class.new( superclass )
dummy2 = eval("#{dynamic_name}")
puts "dummy2: #{dummy2}"
类的名称只是引用它的第一个常量的名称 也就是说,如果我执行
myclass=Class.new
,然后执行myclass=myclass
,则类的名称将变为myclass
。但是,如果在运行时之前我不知道类的名称,我就不能执行MyClass=
因此,您可以使用模块#const_set
,它可以动态设置const的值。例如:
dynamic_name = "ClassName"
Object.const_set(dynamic_name, Class.new { def method1() 42 end })
ClassName.new.method1 #=> 42
我也一直在胡闹。在我的例子中,我试图测试ActiveRecord::Base的扩展。我需要能够动态创建一个类,并且因为活动记录根据类名查找表,所以该类不能是匿名的 我不确定这是否有助于你的案子,但我想到的是:
test_model_class = Class.new(ActiveRecord::Base) do
def self.name
'TestModel'
end
attr_accessor :foo, :bar
end
就ActiveRecord而言,定义self.name就足够了。我猜这实际上适用于所有不能匿名的类
(我刚刚读了sepp2k的答案,我认为他的更好。无论如何,我将把这个留在这里。)下面的代码如何:
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不会重新运行运行时类对象,至少在我的电脑上不会。使用Object.const_get获取类对象。我知道这是一个非常老的问题,其他一些Rubyists可能会因此避开我,但我正在创建一个非常薄的包装器gem,它用ruby类包装一个流行的java项目。基于@sepp2k的答案,我创建了两个helper方法,因为我必须在一个项目中多次这样做。注意,我给这些方法命名了名称空间,这样它们就不会污染像对象或内核这样的顶级名称空间
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):
为什么??
这允许我创建一个使用Java项目的JRubyGem,并允许开源社区和我在将来根据需要装饰这些类。它还创建了一个更友好的名称空间来使用中的类。因为我的gem是一个非常非常薄的包装器,所以我必须创建很多很多子类和模块来扩展其他模块
正如我们在J.D.Power所说,“这是道歉驱动的发展:我很抱歉”。我真的没有得到你想要实现的目标。有一个类TestEval2,您可以在之后执行test_eval2=TestEval2.new。和:A级。。。end总是产生nil,所以我想您的输出是可以的;-)这是一个TDD测试步骤。我需要动态创建一个测试类,然后引用它的名称,因为这就是它在野外的使用方式。sepp2K做对了。@Philip:
class-A。。。end
的计算结果不是nil
,它的计算结果是在它内部计算的最后一个表达式的值,就像Ruby中的所有其他复合表达式(块、方法、模块定义、表达式组)一样。恰好在许多类定义体中,最后一个表达式是方法定义表达式,其计算结果为nil
。但是,有时让类定义体计算为特定值是有用的,例如在类中!谢谢这正是我需要的。谢谢。这对我很有帮助:哇。对我来说,(常量)赋值有这样的副作用似乎很奇怪。出于某种原因,这在开发中对我有效,但在生产中无效。我尝试在新类本身上设置常量,但没有任何效果。有了对象,它就工作了。谢谢。不过,它可以在不使用eval
的情况下完成。使用eval
,您必须清理输入以防止恶意代码执行。顺便说一句,您可以明确设置类的表名,如下所示:self.table\u name=“my\u things”
@EarlJenkins,如果此类中定义了关联(例如,属于),self.name
是必需的,否则#demodulize
将在尝试遵循关联时在nil上失败:(至少我在Rails 5.2.3中就是这样)。
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
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