How to instantiate class from name string in Rails?

43,733

Solution 1

klass = Object.const_get "ClassName"

about class methods

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

Solution 2

Others may also be looking for an alternative that does not throw an error if it fails to find the class. safe_constantize is just that.

class MyClass
end

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

Solution 3

You can simply convert a string and initialize a class out of it by:

klass_name = "Module::ClassName"
klass_name.constantize

To initialize a new object:

klass_name.constantize.new

I hope this turns out to be helpful. Thanks!

Solution 4

I'm surprised nobody is considering security and hacking in their responses. Instantiation of an arbitrary string that likely came even indirectly from user input is asking for trouble and hacking. We all should/must be whitelisting unless we're sure the string is fully controlled and monitored

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

class_for(name_wherever_this_came_from).create!(params_somehow)

How you would know the appropriate params arbitrarily without having a whitelist would be challenging but you get the idea.

Share:
43,733

Related videos on Youtube

Vjatseslav Gedrovits
Author by

Vjatseslav Gedrovits

Ruby-on-Rails Jedi

Updated on July 08, 2022

Comments

  • Vjatseslav Gedrovits
    Vjatseslav Gedrovits almost 2 years

    How we can instantiate class from it's name string in Ruby-on-Rails?

    For example we have it's name in database in format like "ClassName" or "my_super_class_name".

    How we can create object from it?

    Solution:

    Was looking for it myself, but not found, so here it is. Ruby-on-Rails API Method

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

    It can be even not formatted, we can user string method .classify

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

    Of course maybe this is not very 'Rails way', but it solves it's purpose.

    • quandrum
      quandrum over 11 years
      Just FYI, constantize is an ActiveSupport convenience method that does Object.const_get and Classify is an ActiveSupport method that tries to turn a string into a standard class formatting. What you are doing is identical to Evginey's answer, with some additional checks. While constantize is probably a better solution(because it does sanity checks), it helps to understand the tools you using.
    • Vjatseslav Gedrovits
      Vjatseslav Gedrovits over 11 years
      Thank you for this, to be honest didn't checked what it do in manual.
  • Vjatseslav Gedrovits
    Vjatseslav Gedrovits over 11 years
    With that you cannot access class methods. My solution above works correctly with class methods.
  • Vjatseslav Gedrovits
    Vjatseslav Gedrovits over 11 years
    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
    Vjatseslav Gedrovits over 11 years
    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
    Vjatseslav Gedrovits over 11 years
    With my method you can do the both ways. "KlassExample".constantize.self_method_name and klass = "KlassExample".constantize.new klass.normal_method
  • Eugene Rourke
    Eugene Rourke over 11 years
    yeah... it don't prove anything you obviously don't call new them you use "Object.const_get" try klass2.new.name
  • Eugene Rourke
    Eugene Rourke over 11 years
    Well, so that can be done by klass2 = (Object.const_get name).new like you do with klass = name.constantize.new
  • Eugene Rourke
    Eugene Rourke over 11 years
    that your problem? why do you so insist on proving your way somehow better when it obviously do the exact same thing?
  • Vjatseslav Gedrovits
    Vjatseslav Gedrovits over 11 years
    You think that your solution is elegant and correct? Check top comment please.
  • TiSer
    TiSer almost 4 years
    Don't use this! It's very vulnerable approach: praetorian.com/blog/ruby-unsafe-reflection-vulnerabilities
  • Pushp Raj Saurabh
    Pushp Raj Saurabh almost 4 years
    @TiSer Can you please explain how is it vulnerable as long as I don't constantize a string from my request parameters?
  • Pushp Raj Saurabh
    Pushp Raj Saurabh almost 4 years
    Thanks for the link to the blog. This is quite helpful. As long as we use constantize without passing any request params to it and enclose it in a private function, I don't see a threat.
  • Pushp Raj Saurabh
    Pushp Raj Saurabh almost 4 years
    This line in the blog shared by you is extremely critical to keep in mind when working with reflections: Reflection is used to modifying the nature of a program at runtime and should not be used with Strings from untrusted sources.
  • TiSer
    TiSer almost 4 years
    In your direct example - yes, but I don't know where you will want to make such object transformation from single whole string to a class in real world.