Forum: Ruby on Rails String to Class?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
starburger (Guest)
on 2007-02-01 18:50
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)


Why I want to to this? I want to create a generic commenting component
with model controller and view (partials) that I can use to comment any
other instance.

For now each class that can be commented (here: Article besides many
others) needs rather the same action code:

class ArticleController < ApplicationController

  def comment
    @commentable = Article.find(params[:id])  # commentable
    @comment = Comment.new
    @comment.user = session['user']
    @commentable.comments << @comment
    if request.post?
      @comment = Comment.new(params[:comment])
      if @comment.save
        render :partial => "comment/comment_list"
      else
        render :inline => "<%= user_message_helper 'comment' %>",
:status => 500
      end
    end
  end
end

I would like to have this action only once in the CommentController,
somehow like this:

class CommentController < ApplicationController

  def comment
    class_name = params[:class_name]
    @commentable = class_name.to_class.find(params[:id])  # commentable
    @comment = Comment.new
    @comment.user = session['user']
    @commentable.comments << @comment
    if request.post?
      @comment = Comment.new(params[:comment])
      if @comment.save
        render :partial => "comment/comment_list"
      else
        render :inline => "<%= user_message_helper 'comment' %>",
:status => 500
      end
    end
  end
end

I'm sure there is an elegant way to handle this...
Rob N. (Guest)
on 2007-02-01 19:19
starburger wrote:
> Hi,
>
> I'm getting a class name as string via params[:class_name]. How can I
> access the class?

I think what you want is eval

eval("Ball").find(:all)

for example will find all balls with a model class Ball. Not that a
class name is case sensitive!!!!!!
Rob N. (Guest)
on 2007-02-01 19:20
Rob N. wrote:
> Not that a class name is case sensitive!!!!!!

Should be:

Note that a class name is case sensitive!!!!!!
Michael G. (Guest)
on 2007-02-01 19:49
(Received via mailing list)
On 2/1/07, starburger <removed_email_address@domain.invalid> wrote:
>
> and then something like
>
> params[:class_name].to_class.find(:first)


Try String#constantize:

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

Why I want to to this? I want to create a generic commenting component
Guest (Guest)
on 2007-02-01 20:07
Michael G. wrote:
> On 2/1/07, starburger <removed_email_address@domain.invalid> wrote:
>>
>> and then something like
>>
>> params[:class_name].to_class.find(:first)
>
>
> Try String#constantize:
>
> params[:class_name].constantize.find(:first)
>
> Why I want to to this? I want to create a generic commenting component

how bout eval(params[:class_name].capitalize + ".find(:first)") ?
starburger (Guest)
on 2007-02-01 20:44
Thanks a lot for your help - you have solved my problem!

Starburger
Jason R. (Guest)
on 2007-02-01 21:14
(Received via mailing list)
Stay away from eval.

Use String#constantize or Object#get_const

Jason
Jodi S. (Guest)
on 2007-02-01 21:29
(Received via mailing list)
On 1-Feb-07, at 2:13 PM, Jason R. wrote:

> Starburger
>
>

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

Michael S. (Guest)
on 2007-02-01 22:48
(Received via mailing list)
On Thursday 01 February 2007 20:28, Jodi S. wrote:

> Why stay away from eval?


  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...)

Michael

--
Michael S.
mailto:removed_email_address@domain.invalid
http://www.schuerig.de/michael/
Ezra Z. (Guest)
on 2007-02-01 22:49
(Received via mailing list)
On Feb 1, 2007, at 11:28 AM, Jodi S. wrote:

>>
> {the_model.id})")
>
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 Z.
-- Lead Rails Evangelist
-- removed_email_address@domain.invalid
-- Engine Y., Serious Rails Hosting
-- (866) 518-YARD (9273)
Jodi S. (Guest)
on 2007-02-02 00:57
(Received via mailing list)
On 1-Feb-07, at 3:48 PM, Ezra Z. wrote:

>>> Jason
>> I'm currently using eval to dynamically build a restful url_helper
>> General Partner
> evaling anything user generated. The OP is trying to make a class
> instance_eval with blocks instead of strings are better. But in your
> Cheers-
>
> -- Ezra Z.
> -- Lead Rails Evangelist
> -- removed_email_address@domain.invalid
> -- Engine Y., 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
Jodi S. (Guest)
on 2007-02-02 18:50
(Received via mailing list)
> 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
unknown (Guest)
on 2007-02-03 01:49
(Received via mailing list)
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
This topic is locked and can not be replied to.