Sunday, March 23, 2008

params[:fu] #1 ) Wrap all relevant attributes in a single params key.

This is the simplest form of using form_for and params:

<% form_for @reader do |f| %>
<%= f.text_field :name %
>
<%= f.text_field :birth_place %>

<%
= f.submit 'Create' %>
<% end %
>


Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]
Parameters: { "commit" => "Create",
"reader" => { "name" => "stephen chu", "birth_place" => "hong kong" },
"authenticity_token" => "238ba79b8282882ba01d840352616c2cc79280f0",
"action" => "create",
"controller" => "readers"}


def create
@reader = Reader.create! params[:reader]
end


By doing this, you eliminate the need to explicitly spell out each parameters posted, like the ugly Reader.new :name => params[:name], :birth_place => params[:birth_place].

Notice, Active Record is extremely hash-happy. Let's take a few examples and see how it is so hash-hungry (or rather, hash-happy).

@reader = Reader.new params[:reader]  # => { 'first_name' => 'stephen', 'last_name' => 'chu' }

@readers = Reader.find(:all, :conditions => params[:search]) # => { 'name' => 'chu' }

@reader.attributes = params[:reader] # => { 'first_name' => 'martin', 'last_name' => 'fowler' }

@reader.update_attributes(params[:reader]) # => { 'birth_place' => 'hong kong' }


Notice the find with condition one. No one says I cannot have a hash or form_for that must map to a database table!

All of the above will work in just one line of code, and I am sure you can find more examples as well. Remember, hashes are one of the most underrated Rails toolset to reduce code you need to write. Stay tuned on how we can optimize the use of these AR one-liners.

(back to the TOC of the params[:fu] series)

5 comments:

Clmens Kofler said...

You should at least mention that there may be some attr_protected/attr_accessible in the way ...

Other than that - great series!

codesnik said...

find :all, :conditions => params[:foo] is prone to sql injection, no? :conditions could be a mere string, and although rails mysql driver doesn't allow to run several sql commands at once, I'm pretty sure someone can write something nasty, just by form tampering.

Stephen Chu said...

Last I checked, passing a hash construct to :conditions will trigger params sanitization inside ActiveRecord. Check out http://ar.rubyonrails.org/classes/ActiveRecord/Base.html the "Conditions" section.

codesnik said...

hash - yes, but string - isn't.
One could easily change your form in a way it would send params[:search] == "1=0);drop table users;select (1"

making resulting SQL:
"select from users where (1=0);drop table users;select (1)".

although this won't work with latest mysql drivers (they have multiple statement execution disabled), I'm pretty sure one can do something interesting with such a technique.

Eric said...

Is there a reason why your main content section is only 40 characters wide?