Ruby symbol to class
Question
Is there a way in Ruby to take a symbol or string and turn it into a class of the same name?
For instance, if I have a class such as
class Bob
def talk
puts "Hi, I'm bob"
end
end
And a method I have somewhere else in the code is passed a symbol :bob, can I in some way turn that into the class Bob? Maybe something like
b = :Bob.new
b.talk
Or is there a way to do something similar to this?
Solution
There are many ways to do this. Your lack of context makes it impossible to elect a "best" way. Here's a few ayways.
Kernel.const_get(:Bob)
eval(:Bob.to_s)
Kernel.const_get(:bob.to_s.capitalize)
OTHER TIPS
Rails
For use with Rails only.
With a string:
"Module".constantize #=> Module
"Class".constantize #=> Class
With a symbol:
:module.to_s.classify.constantize #=> Module
:open_struct.to_s.classify.constantize #=> OpenStruct
If you are dealing with a multi-word symbol, then you'll want to add #classify
to the chain to properly handle the capitalization of all the parts of the constant.
http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize
None of the solutions I've seen work if you want to turn :foo_bar into FooBar. If that's what you're looking for:
:foo_bar.to_s.split("_").collect(&:capitalize).join.constantize
=> FooBar
hope that helps someone.
NameSpace.const_get(classname)
will return the class object (assuming that classname contains the name of a class - if it contains the name of a constant that is not a class, it will return the value of that constant). The toplevel namespace is Object, so you can do Object.const_get(:Bob).new
class Bob
end
def create(name)
return eval("#{name}.new")
end
b = create(:Bob)
puts b.class
Here is something that I came up with while looking for something similar that included module/namespace support:
['foo', 'bar'].inject {|r,e| "#{r.classify}::#{e.classify}"}.constantize
Produces
=> Foo::Bar
However this requires Rails and of course it only works where the array has more than one element.
This seems rather simple so I'm sure I'm missing something out, would anyone care to advise why this wouldn't be a good approach?
Thanks!
in my case, both example below worked, but you also have not to forget namespace:
Object.const_get("ModuleName::#{class_model_name}")
or
Kernel.const_get("ModuleName::#{class_model_name}").