String to Class?

Hi,

I’m getting a class name as string via params[:class_name]. How can I access the class?

E.g.

params[:class_name] => “Post”

and then something like

params[:class_name].to_class.find(:first)

Try String#constantize:

params[:class_name].constantize.find(:first)

Stay away from eval.

Use String#constantize or Object#get_const

Jason

Jason, Why stay away from eval?

I’m currently using eval to dynamically build a restful url_helper reference. Is this also a bad case of using eval fro the same reason/?

ala: eval(“change_state_#{the_model.class.name.downcase}_url(#{the_model.id})”)

(sorry to hijack - hopefully this adds to the discussion!)

Cheers,

Jodi

General Partner

The nNovation Group inc.

www.nnovation.ca/blog

on-innovation.gif

class SomethingsController < ApplicationController     def tricky       ...       eval(params[:my_param])       ...     end   end

Then consider what a request like this might do to your server

http://example.com/trickies/tricky?my_param=system("rm%20-rf%20%2F")

Michael

Jodi-

  The reason to stay away from eval is because you want to avoid evaling anything user generated. The OP is trying to make a class from an item in the params object. DANGER!! look here:

how bout eval(params[:class_name].capitalize + ".find(:first)") ?

  Now anyone can try to enter all kinds of things in the params[:class_name] form field. And it will get evaled without any checking on the server. Bad!

  If you can avoid eval please do so at all costs. class_Eval or instance_eval with blocks instead of strings are better. But in your example:

eval("change_state_#{the_model.class.name.downcase}_url(#{the_model.id})")

should be written like this:

send("change_state_#{the_model.class.name.downcase}_url", "#{the_model.id}")

Cheers-

-- Ezra Zygmuntowicz-- Lead Rails Evangelist -- ez@engineyard.com -- Engine Yard, Serious Rails Hosting -- (866) 518-YARD (9273)

Thanx Ezra and Michael.

I understood that Send limitted the scope(object calls) of damage that can be cause by eval - and a object.send("#{params[:command]) is dangerous enough. Whereby Eval's scope isn't limited.

In my particular case the_model is instantiate by the controller action - so eval would likely be as 'safe' as send (?).

My questions was questioning what additional protection that send provides - is there any protection provided above and beyond the localization of the class executing the arbritary code?

I think my learning here is that regardless of means(send, eval, etc), that additionally checking should be done on the methods being called - limiting the scope of the methods that can be called (I've recently used acts_as_state_machine to limit sends based upon model state and other guards - this provides funnel that sends must qualify before running - and still give me the benefit of a measure of dynamism.

ala:    instance.send("#{params[:command]}") if Class.legitimate_commands.include?(params[:command])

thanks for teaching this ruby-nuby. Jodi

eval("change_state_#{the_model.class.name.downcase}_url(# {the_model.id})")

should be written like this:

send("change_state_#{the_model.class.name.downcase}_url", "# {the_model.id}")

Hey Ezra -

the send didn't succeed as : undefined method `change_state_project_url(:id => 85, :event => 'change_project_info')' for #<#<Class:0x345af20>:0x345aef8>

note: this code is running inside application_helper - not sure what self is inside a helper, but it doesn't know about rest url helpers.

Do you know what object 'owns' these methods? I gather they are Class methods.

thanx. Jodi

As everybody has said, avoid eval - use const_get to retrieve a constant. If you want a class from a string, here's a simple implementation:

class String   def to_class     Object.const_get(self)   end end