108

How do I call a class from a string containing that class name inside of it? (I guess I could do case/when but that seems ugly.)

The reason I ask is because I'm using the acts_as_commentable plugin, among others, and these store the commentable_type as a column. I want to be able to call whatever particular commentable class to do a find(commentable_id) on it.

Thanks.

unsorted
  • 3,114
  • 3
  • 29
  • 39

6 Answers6

149

I think what you want is constantize

That's an RoR construct. I don't know if there's one for ruby core

fotanus
  • 19,618
  • 13
  • 77
  • 111
Jamie Wong
  • 18,104
  • 8
  • 63
  • 81
  • perfect, that's just what I was looking for. – unsorted Aug 12 '10 at 01:12
  • 44
    For plain Ruby, you'd use `Module.const_get`. The advantage of `constantize` is that it works even with deeply nested namespaces, so you could do `'Functional::Collections::LazyList'.constantize` and get the class LazyList from the module Collections in the module Functional, whereas with `const_get`, you'd have to do something like `'Functional::Collections::LazyList'.split('::').reduce(Module, :const_get)`. – Chuck Aug 12 '10 at 01:43
52
"Object".constantize # => Object
jtbandes
  • 115,675
  • 35
  • 233
  • 266
50

It depends on the string...

If it already has the proper shape (casing, pluralization, etc), and would otherwise map directly to an object, then:

Rails:

'User'.constantize # => User

Ruby:

Module.const_get 'User' # => User

But otherwise (note the difference in casing):

'user'.constantize # => NameError: wrong constant name user

Module.const_get 'user' # => NameError: wrong constant name user

Therefore, you must ask... is the source string singular or plural (does it reference a table or not?), is it multi-word and AlreadyCamelCased or is_it_underscored?

With Rails you have these tools at your disposal:

Use camelize to convert strings to UpperCamelCase strings, even handling underscores and forward slashes:

'object'.constantize # => NameError: wrong constant name object
'object'.camelize # => "Object"
'object'.camelize.constantize # => Object
'active_model/errors'.camelize # => "ActiveModel::Errors"
'active_model/errors'.camelize.constantize # => ActiveModel::Errors

Use classify to convert a string, which may even be plural (i.e. perhaps it's a table reference), to create a class name (still a string), then call constantize to try to find and return the class name constant (note that in Ruby class names are constants):

'users'.classify => "User" # a string
'users'.classify.constantize # => User

'user'.classify => "User" # a string
'user'.classify.constantize # => User

'ham_and_eggs'.classify # => "HamAndEgg"

In POR (Plain Old Ruby), you have capitalize, but it only works for the first word:

Module.const_get 'user'.capitalize => User

...otherwise you must use fundamental tools like strip, split, map, join, etc. to achieve the appropriate manipulation:

class HamAndEgg end # => nil
Module.const_get ' ham and eggs '.strip.gsub(/s$/,'').split(' ').map{|w| w.capitalize}.join # => HamAndEgg
user664833
  • 18,397
  • 19
  • 91
  • 140
  • 12
    You should use camelize instead of classify since classify is for table names and doesn't handle pluralization very well. – Peter Brown Jul 15 '12 at 16:14
  • 2
    Your answer is really valuable, but you should use "titleize" for table name which includes by space, and remove white space from string to make meaning full class name. – SSR Apr 15 '15 at 10:05
  • @SSR yes, you could: `' ham and eggs '.titleize.gsub(/ /,'').constantize # => HamAndEggs` – user664833 Jun 03 '21 at 19:04
24

I know this is an old question but I just want to leave this note, it may be helpful for others.

In plain Ruby, Module.const_get can find nested constants. For instance, having the following structure:

module MyModule
  module MySubmodule
    class MyModel
    end
  end
end

You can use it as follows:

Module.const_get("MyModule::MySubmodule::MyModel")
MyModule.const_get("MySubmodule")
MyModule::MySubmodule.const_get("MyModel")
Edgar Ortega
  • 1,672
  • 18
  • 22
7

When ActiveSupport is available (e.g. in Rails): String#constantize or String#safe_constantize, that is "ClassName".constantize.

In pure Ruby: Module#const_get, typically Object.const_get("ClassName").

In recent rubies, both work with constants nested in modules, like in Object.const_get("Outer::Inner").

skalee
  • 12,331
  • 6
  • 55
  • 57
5

If you want to convert string to actuall class name to access model or any other class

str = "group class"

> str.camelize.constantize 'or'
> str.classify.constantize 'or'
> str.titleize.constantize

Example :
  def call_me(str)
    str.titleize.gsub(" ","").constantize.all
  end

Call method : call_me("group class")

Result:
  GroupClass Load (0.7ms) SELECT `group_classes`.* FROM `group_classes`
SSR
  • 6,398
  • 4
  • 34
  • 50