如何在Rails中从名称字符串实例化类?


77

我们如何在Ruby-on-Rails中从名称字符串实例化类?

例如,我们在数据库中使用“ ClassName”或“ my_super_class_name”之类的格式来命名它。

我们如何从中创建对象?

解:

我自己在寻找它,但没有找到,所以在这里。 Ruby on Rails API方法

name = "ClassName"
instance = name.constantize.new  

它甚至可以不格式化,我们可以使用用户字符串方法.classify

name = "my_super_class"
instance = name.classify.constantize.new

当然,这可能不是很“ Rails Way”,但它解决了它的目的。


4
仅供参考,constantize是一个ActiveSupport便捷方法,Object.const_get而Classify是一个ActiveSupport方法,它将字符串转换为标准类格式。您所做的与Evginey的答案相同,但还有一些其他检查。尽管常量化可能是一个更好的解决方案(因为它进行完整性检查),但它有助于了解您使用的工具。
quandrum

谢谢您,老实说没有检查手册中的内容。
Vjatseslav Gedrovits

Answers:


77
klass = Object.const_get "ClassName"

关于类方法

class KlassExample
    def self.klass_method
        puts "Hello World from Class method"
    end
end
klass = Object.const_get "KlassExample"
klass.klass_method

irb(main):061:0> klass.klass_method
Hello World from Class method

这样就无法访问类方法。我上面的解决方案可以正确地与类方法一起使用。
Vjatseslav Gedrovits

Loading development environment (Rails 3.2.9) irb(main):001:0> name = "PartnerGateway" => "PartnerGateway" irb(main):002:0> klass = name.constantize.new => #<PartnerGateway id: nil, name: nil, partner_id: nil, gateway: nil, changed_by_id: nil, deleted_at: nil, created_at: nil, updated_at: nil> irb(main):003:0> klass.name => nil
Vjatseslav Gedrovits

irb(main):005:0> klass2 = Object.const_get name => PartnerGateway(id: integer, name: string, partner_id: integer, gateway: string, changed_by_id: integer, deleted_at: datetime, created_at: datetime, updated_at: datetime) irb(main):006:0> klass2.name => "PartnerGateway" irb(main):008:0> klass2.id NoMethodError: undefined method id'for#<Class:0xac3e4c0>`
Vjatseslav Gedrovits

用我的方法,您可以同时做这两种方式。“ KlassExample” .constantize.self_method_name和klass =“ KlassExample” .constantize.new klass.normal_method
Vjatseslav Gedrovits

是的...它并没有证明任何东西你显然不会调用它们,而是使用“ Object.const_get”尝试klass2.new.name
Eugene Rourke

40

其他人也可能正在寻找一种替代方法,如果它找不到该类,则不会抛出错误。safe_constantize就是这样。

class MyClass
end

"my_class".classify.safe_constantize.new #  #<MyClass:0x007fec3a96b8a0>
"omg_evil".classify.safe_constantize.new #  nil 

8

您可以通过以下方法简单地转换字符串并从中初始化一个类:

klass_name = "Module::ClassName"
klass_name.constantize

要初始化一个新对象:

klass_name.constantize.new

我希望这对您有所帮助。谢谢!


不要使用这个!这是一种非常脆弱的方法:praetorian.com/blog/ruby-unsafe-reflection-vulnerabilities
TiSer

@TiSer您能解释一下,只要我不从请求参数中常量化一个字符串,它是多么容易受到攻击?
Pushp Raj Saurabh

感谢您到博客的链接。这很有帮助。只要我们使用常量化而不传递任何请求参数并将其封装在私有函数中,我就看不到任何威胁。
Pushp Raj Saurabh

您在博客中共享的这一行在使用反射时要牢记,这是至关重要的:反射用于在运行时修改程序的性质,不应与不受信任的来源的字符串一起使用。
Pushp Raj Saurabh

1
在您的直接示例中-是的,但是我不知道您想在哪里将这种对象从单个完整字符串转换为现实世界中的类。
TiSer

5

令我惊讶的是,没有人考虑在他们的回应中采用安全性和黑客手段。甚至可能间接来自用户输入的任意字符串的实例化都在引起麻烦和黑客攻击。除非我们确定字符串已受到完全控制和监控,否则我们所有人都应该/必须将其列入白名单

def class_for(name)
  {
    "foo" => Foo,
    "bar" => Bar,
  }[name] || raise UnknownClass
end

class_for(name_wherever_this_came_from).create!(params_somehow)

在没有白名单的情况下如何任意知道合适的参数将是一个挑战,但是您知道了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.