ruby convert class name in string to actual class

Viewed 62849

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.

6 Answers

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
Related