tags:

views:

86

answers:

3

Having a string with the module and name of a class, like:

"Admin::MetaDatasController"

how do I get the actual class?

The following code works if there's no module:

Kernel.const_get("MetaDatasController")

but it breaks with the module:

ruby-1.8.7-p174 > Kernel.const_get("Admin::MetaDatasController")
NameError: wrong constant name Admin::MetaDatasController
        from (irb):34:in `const_get'
        from (irb):34
ruby-1.8.7-p174 > 
+9  A: 

If you want something simple that handles just your special case you can write

Object.get_const("Admin").get_const("MetaDatasController")

But if you want something more general, split the string on :: and resolve the names one after the other:

def class_from_string(str)
  str.split('::').inject(Object) do |mod, class_name|
    mod.const_get(class_name)
  end
end

the_class = class_from_string("Admin::MetaDatasController")

On the first iteration Object is asked for the constant Admin and returns the Admin module or class, then on the second iteration that module or class is asked for the constant MetaDatasController, and returns that class. Since there are no more components that class is returned from the method (if there had been more components it would have iterated until it found the last).

Theo
Out of interest, why are you using `Kernel` as the receiver? a Top-level constant is defined on `Object` not on `Kernel`.
banister
The OP used it, and I didn't stop to look up which might be the actual module that defined top level constants. I've changed it.
Theo
A: 

i could be way off-base, but wouldn't eval return the class?

eval("Admin::MetaDatasController")

so eval("Admin::MetaDatasController").new would be the same as Admin::MetaDatasController.new

potatopeelings
Yeah, but it comes with a lot of baggage. Namely, security and speed. A good general rule is to avoid cavalier use of eval. Also, be careful who you suggest this around, or you could find yourself being torched at the stake for fraternizing with evil (it tends to be a touchy subject).
Joshua Cheek
:-) got it. eval = evil with a spelling mistake.
potatopeelings
+4  A: 

ActiveSupport provides a method called constantize, which will do this. If you are on Rails, which I assume you are based on the name of your constant, then you already have ActiveSupport loaded.

$ irb

ruby-1.8.7-p249 > require 'rubygems'
 => true 

ruby-1.8.7-p249 > require 'active_support'
 => true 

ruby-1.8.7-p249 > class Admin ; class MetaDatasController ; end ; end
 => nil 

ruby-1.8.7-p249 > "Admin::MetaDatasController".constantize
 => Admin::MetaDatasController 

To see how the method is implemented, check out http://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb

Joshua Cheek