tags:

views:

198

answers:

3

I have a string, containing an Class name. It is, for example, a string containing "Article". That string came up from the params[]. What should I do to work with this string as if it was a class name? For instance, I want to do:

Article.all

and so on.

Any idea?

+3  A: 
class Abc
end #=> nil
klass = eval("Abc") #=> Abc
klass.new #=> #<Abc:0x37643e8>

Assumes there really is a class with the name provided...

In ActiveSupport, there was String#constantize, which did the same thing, but I believe it's deprecated after 2.1.

EDIT: this is the implementation of constantize from ActiveSupport 2.1.2:

  def constantize(camel_cased_word)
    names = camel_cased_word.split('::')
    names.shift if names.empty? || names.first.empty?

    constant = Object
    names.each do |name|
      constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
    end
    constant
  end
Mike Woodhouse
It's a bit dangerous to use eval for params cause it might contain harmful code. I don't recommend this solutions
khelll
Wow! Thanks a LOT!
gmile
khell, any secure solutions?
gmile
Check my solution bellow...
khelll
A: 

I am not sure whether I understand your intention correctly. Here I assume all is an Class method of Article and all return an array of articles.

class Article
    def self.all
       ["Peopleware" , "The Mythical Man-Month"]
    end

end

s = "Article"
all_of_article = []
eval("all_of_article = #{s + ".all"}")
puts all_of_article.inspect  # ["Peopleware", "The Mythical Man-Month"]
pierr
+4  A: 

This solution is better than eval as you are evaluating params hash that might be manipulated by the user and could contain harmful actions. As a general rule: Never evaluate user input directly, that's a big security hole.

# Monkey patch for String class
    class String
      def to_class
        klass = Kernel.const_get(self)
        klass.is_a?(Class) ? klass : nil
      rescue NameError
        nil
      end
    end

# Examples
"Fixnum".to_class #=> Fixnum
"Something".to_class #=> nil

Update - a better version that works with namespaces:

 # Monkey patch for String class
    class String
      def to_class
        chain = self.split "::"
        klass = Kernel
        chain.each do |klass_string|
          klass = klass.const_get klass_string
        end
        klass.is_a?(Class) ? klass : nil
      rescue NameError
        nil
      end
    end
khelll
This won't won't work with namespaced classes, like `MyModule::MyClass`. For that you'd have to split on "::" and then, for example, use inject with const_get.
sepp2k
Yes it needs further refinement... Thanks!
khelll
Good point. I added the active_support def, which is much more in line with your (better) suggestion.
Mike Woodhouse
I have added a new post regarding it http://stackoverflow.com/questions/1448670/ruby-stringtoclass
khelll
For now, the code you posted is a bit of "dark materia" for me :P Great thanks anyway!
gmile
It's a more general solution as a response to sepp2k's note regarding namespaces like MyModule::MyClass
khelll