<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/atom10full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0"><id>tag:blogger.com,1999:blog-11933937</id><updated>2008-07-16T18:29:37.655-05:00</updated><title type="text">STEPHEN CHU . com</title><link rel="alternate" type="text/html" href="http://www.stephenchu.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default?start-index=26&amp;max-results=25&amp;redirect=false" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.stephenchu.com/feeds/posts/default" /><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>67</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><link rel="self" href="http://feeds.feedburner.com/stephenchu" type="application/atom+xml" /><entry><id>tag:blogger.com,1999:blog-11933937.post-2462354306162455570</id><published>2008-06-22T23:02:00.005-05:00</published><updated>2008-06-22T23:08:41.217-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="pattern" /><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails" /><title type="text">Object-Oriented Programming with Rails ActiveRecord</title><content type="html">Have you seen this before?&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="attribute"&gt;@posts&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:conditions&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="symbol"&gt;:user_id&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;This happens to new Rails developers quite a bit. This code retrieves correctly an array of &lt;tt&gt;@posts&lt;/tt&gt; and send them to the view for rendering. But what about it?&lt;br /&gt;&lt;br /&gt;The key thing here is that this can also be done in a better way:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="attribute"&gt;@posts&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;posts&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;When you use the former method to retrieve &lt;tt&gt;@posts&lt;/tt&gt;, you are actually thinking in database terms along the lines of &lt;em&gt;"in the database table '&lt;tt&gt;posts&lt;/tt&gt;' and therefore &lt;tt&gt;ActiveRecord&lt;/tt&gt; model '&lt;tt&gt;Post&lt;/tt&gt;', retrieve the rows whose foreign key column '&lt;tt&gt;user_id&lt;/tt&gt;' has the value &lt;tt&gt;@user.id&lt;/tt&gt;."&lt;/em&gt; &lt;tt&gt;ActiveRecord&lt;/tt&gt; is a pattern for accessing your data from objects, but you also have to combine it with the power of the object-oriented-ness of Ruby to create the chimes of beautiful code. Using Rails and Ruby does not make anyone a better programmer automagically. One can still write code in Java/C# in the same procedural style as if you were writing C. It is how to leverage the best of all worlds makes you a better problem solver.&lt;br /&gt;&lt;br /&gt;Therefore, think like an object bigot. Think in objects, and not in database foreign key column values. Whenever you see any Rails code in the pattern of this:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="symbol"&gt;:model_id&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@model&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Just stop. "Objects on Rails" sounds a lot better than "Ruby on Foreign Keys."&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=0wncGi"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=0wncGi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=NkIj2i"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=NkIj2i" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=Yu4BTI"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=Yu4BTI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=fOQnvI"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=fOQnvI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=4ZjMUI"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=4ZjMUI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stephenchu/~4/317825309" height="1" width="1"/&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/stephenchu/~3/317825309/object-oriented-programming-with-rails.html" title="Object-Oriented Programming with Rails ActiveRecord" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=11933937&amp;postID=2462354306162455570" title="1 Comments" /><link rel="replies" type="application/atom+xml" href="http://www.stephenchu.com/feeds/2462354306162455570/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/2462354306162455570" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/2462354306162455570" /><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://www.stephenchu.com/2008/06/object-oriented-programming-with-rails.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-4982606554165380166</id><published>2008-06-18T01:39:00.006-05:00</published><updated>2008-06-18T01:44:49.338-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="pattern" /><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails" /><title type="text">Learning encapsulation should come before learning Rails</title><content type="html">Do you see anything wrong in this one line of code?&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Drink up your milk!&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@milk&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;expiration_date&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="number"&gt;2&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;days&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;from_now&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I think there is. In particular, the programmer can do better. It is about a programming concept that we have all heard and should be familiar with: &lt;strong&gt;encapsulation&lt;/strong&gt;. If you are into testing, which you ought to be, you can probably identify such encapsulation violations &lt;a href="http://www.stephenchu.com/2007/01/use-your-test-classes-to-identify.html"&gt;by how smelly your tests are&lt;/a&gt;, as I believe in &lt;a href="http://www.stephenchu.com/2007/12/last-d-in-tdd-means-more-than-just.html"&gt;Test Driven Design&lt;/a&gt;. Going back to the line of code. What's wrong with it?&lt;br /&gt;&lt;br /&gt;The pattern of this code is, you have code somewhere that yanks states (&lt;tt&gt;expiration_state&lt;/tt&gt;) out of an object (&lt;tt&gt;@milk&lt;/tt&gt;) and then interrogate against those states (&lt;tt&gt;&amp;lt; 2.days.from_now&lt;/tt&gt;). By no means this is a rule, as exceptions do exist. But, when this happens, your programming siren in your head should go off as if the CI build is broken: can this be a method on the object &lt;tt&gt;@milk&lt;/tt&gt; itself?&lt;br /&gt;&lt;br /&gt;Ruby is a powerful programming language. It allows you to do all sorts of fancy programming: dynamically altering classes, add methods to only the selected few object instances, while being duck-typed and object-oriented all at the same time. There are examples galore in Rails itself. But to enable all such magic, all of your &lt;tt&gt;ActiveRecord&lt;/tt&gt; domain models have getters and setters on all of their states (i.e. data). While that is convenient to access their states, sometimes you have to be careful. With great power comes great responsibility, and that responsibility comes down to you.&lt;br /&gt;&lt;br /&gt;You might not think this applies to you. But have you ever written code like this in Ruby?&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="constant"&gt;MyMailer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;deliver_product_returned_notification&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@product&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="constant"&gt;State&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Returned&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;total&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@line_items&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;sum&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;line_item&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;line_item&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;price&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt; &lt;span class="ident"&gt;line_item&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;quantity&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:admin&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:superuser&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;include?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;role&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now, let's look at these rewritten examples:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Drink up your milk!&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@milk&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;expiring?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="constant"&gt;MyMailer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;deliver_product_returned_notification&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@product&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;returned?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;total&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@line_items&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;sum&lt;/span&gt;&lt;span class="punct"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="symbol"&gt;:subtotal&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;superuser?&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Forget about the loops, the blocks, the hype, and everything about Ruby for a sec. Code is code. Not only does the code become more readable, when you try to enrich your domain by naming things correctly, you could also very well be opening up new business concepts that wasn't previously clear or accurate. If you have a domain model, you are modeling it against domain concepts so that your app can interact with. You'd better be sure it is right, or else lots of time will go wasted on code that solves only half of the business problems all the time.&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=RSie0i"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=RSie0i" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=dCzrVi"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=dCzrVi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=RmIOzI"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=RmIOzI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=xr4eeI"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=xr4eeI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=d5gBWI"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=d5gBWI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stephenchu/~4/314394397" height="1" width="1"/&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/stephenchu/~3/314394397/learning-encapsulation-should-come.html" title="Learning encapsulation should come before learning Rails" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=11933937&amp;postID=4982606554165380166" title="8 Comments" /><link rel="replies" type="application/atom+xml" href="http://www.stephenchu.com/feeds/4982606554165380166/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/4982606554165380166" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/4982606554165380166" /><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://www.stephenchu.com/2008/06/learning-encapsulation-should-come.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-89965006330598668</id><published>2008-06-09T16:03:00.003-05:00</published><updated>2008-06-09T18:19:58.729-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="efficiency" /><title type="text">Launching apps in command line (Mac)</title><content type="html">In most cases, to launch an application on a Mac is just a matter of using the awesome QuickSilver keyboard shortcut and type in what you want to open. But, sometimes it is useful to be able to launch/open an application in command line or terminal in a scripting context.&lt;br /&gt;&lt;br /&gt;So, instead of going through all the typing &lt;tt&gt;$ /Application/MyFavouriteApp/Contents/MacOS/MyFavouriteApp&lt;/tt&gt; in your terminal, you can do:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;$ open -a MyFavouriteApp&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;This will open any applications in your &lt;tt&gt;/Applications&lt;/tt&gt; folder by name.&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=QwgxVi"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=QwgxVi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=yvPBli"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=yvPBli" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=93D2DI"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=93D2DI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=v1WajI"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=v1WajI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=Sd8inI"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=Sd8inI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stephenchu/~4/308379887" height="1" width="1"/&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/stephenchu/~3/308379887/launching-apps-in-command-line-mac.html" title="Launching apps in command line (Mac)" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=11933937&amp;postID=89965006330598668" title="0 Comments" /><link rel="replies" type="application/atom+xml" href="http://www.stephenchu.com/feeds/89965006330598668/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/89965006330598668" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/89965006330598668" /><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://www.stephenchu.com/2008/06/launching-apps-in-command-line-mac.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-3716593703045725703</id><published>2008-05-21T03:10:00.005-05:00</published><updated>2008-05-21T03:32:44.567-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails" /><title type="text">Rails composed_of validation</title><content type="html">ActiveRecord allows you to declaratively write validations (e.g. &lt;tt&gt;validates_presence_of&lt;/tt&gt;) in any &lt;tt&gt;ActiveRecord::Base&lt;/tt&gt; models. In addition, the errors will be stored in the &lt;tt&gt;@model#errors&lt;/tt&gt; collection and be used by various view helpers and methods like &lt;tt&gt;FormBuilder&lt;/tt&gt;, &lt;tt&gt;error_messages_for&lt;/tt&gt;, and &lt;tt&gt;error_message_on&lt;/tt&gt;, wrapping a nice error CSS around the offending html form elements. But when you have a normal domain model object that is non-&lt;tt&gt;ActiveRecord::Base&lt;/tt&gt; such as a &lt;tt&gt;composed_of&lt;/tt&gt; type Value Object, you will not have access to these declarative validation magic methods. Today let me try to elaborate on how to use &lt;tt&gt;ActiveRecord::Validation&lt;/tt&gt; methods in your &lt;tt&gt;composed_of&lt;/tt&gt; objects in Rails 2.0.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;br /&gt;  (The following entry revolves around the assumption that you are using &lt;tt&gt;f.fields_for&lt;/tt&gt; to create and assign value object domain models onto your &lt;tt&gt;ActiveRecord::Base&lt;/tt&gt; models. Using this approach eliminates most value object creation code in your controllers, achieving "Skinny Controllers". To learn about, visit, check out the &lt;a href="http://www.stephenchu.com/2008/03/introduction-to-rails-composedof.html"&gt;Rails composed_of &amp;block conversion&lt;/a&gt; blog entry.)&lt;br /&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Using the same example in the previous entry, let's include our &lt;tt&gt;ActiveRecord::Validation&lt;/tt&gt; module into our &lt;tt&gt;Money&lt;/tt&gt; class like such:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Money&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="symbol"&gt;:Validations&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="ident"&gt;attr_reader&lt;/span&gt; &lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:currency&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="ident"&gt;validates_presence_of&lt;/span&gt;  &lt;span class="symbol"&gt;:currency&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_inclusion_of&lt;/span&gt; &lt;span class="symbol"&gt;:currency&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:in&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;USD&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;EUR&lt;/span&gt;&lt;span class="punct"&gt;'],&lt;/span&gt; &lt;span class="symbol"&gt;:if&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:currency?&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_presence_of&lt;/span&gt;     &lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_numericality_of&lt;/span&gt; &lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:if&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:balance?&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;currency&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="attribute"&gt;@currency&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;currency&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@errors&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Errors&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;new_record?&lt;/span&gt;&lt;br /&gt;    &lt;span class="constant"&gt;true&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;currency?&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;!&lt;/span&gt;&lt;span class="attribute"&gt;@currency&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;blank?&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;balance?&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;!&lt;/span&gt;&lt;span class="attribute"&gt;@balance&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;blank?&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.human_attribute_name&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;attr&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;attr&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;humanize&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;balance_before_type_cast&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@balance&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;  &lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;You will notice a couple things. One, I have to define the &lt;tt&gt;#new_record?&lt;/tt&gt; method. This method is defined on all &lt;tt&gt;ActiveRecord::Base&lt;/tt&gt; objects, but since our PORO object is not a record per se, we just stub it out. Also, we need to store a collection &lt;tt&gt;@errors&lt;/tt&gt; of type &lt;tt&gt;ActiveRecord::Errors&lt;/tt&gt;. &lt;br /&gt;&lt;br /&gt;Depending on what validation routine you will end up using in &lt;tt&gt;Money&lt;/tt&gt;, you may have to stub out different methods. For example, I am showing error messages with &lt;tt&gt;error_messages_for&lt;/tt&gt; (more on this later), and it requires stubbing out &lt;tt&gt;self.human_attribute_name&lt;/tt&gt; (as of Rails 2.0, but no longer needed &lt;a href="http://dev.rubyonrails.org/ticket/8760"&gt;in future Rails&lt;/a&gt;). Using &lt;tt&gt;validates_numericality_of&lt;/tt&gt; requires me to stub out &lt;tt&gt;balance_before_type_cast&lt;/tt&gt;. Also, the validation &lt;tt&gt;:if&lt;/tt&gt; conditions requires me to add the question-mark methods &lt;tt&gt;balance?&lt;/tt&gt; and &lt;tt&gt;currency?&lt;/tt&gt;. Remember, this approach does not give you all the validation magic. For example, &lt;tt&gt;validates_uniqueness_of&lt;/tt&gt; will not work because it assumes too much about your object being a normal AR model and needs access to a database. But in practice, your Value Objects should not need such validations, and in most cases they contain only simple one-off validations, and provide simple functionalities such as formatting like &lt;a href="http://www.stephenchu.com/2008/01/format-your-data-using-rails-composedof.html"&gt;this&lt;/a&gt; and &lt;a href="http://www.stephenchu.com/2008/03/little-tos-can-do-wonder-in-dry-ing-up.html"&gt;this&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;After all these, let's see our ActiveRecord &lt;tt&gt;Book&lt;/tt&gt; class.&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Book&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Bases&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;composed_of&lt;/span&gt; &lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:class_name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Money&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:mapping&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[%w(&lt;/span&gt;&lt;span class="string"&gt;balance balance&lt;/span&gt;&lt;span class="punct"&gt;),&lt;/span&gt; &lt;span class="punct"&gt;%w(&lt;/span&gt;&lt;span class="string"&gt;currency currency&lt;/span&gt;&lt;span class="punct"&gt;)]&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;     &lt;span class="constant"&gt;Money&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;span class="punct"&gt;],&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:currency&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_presence_of&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_associated&lt;/span&gt;  &lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The &lt;tt&gt;composed_of&lt;/tt&gt; conversion block remains. You will notice the &lt;tt&gt;validates_associated :balance&lt;/tt&gt; line as well. This tells your book instances that they should not be persisted should there be any &lt;tt&gt;balance&lt;/tt&gt; validations failing, just like any normal validations you would write. By default, any failing &lt;tt&gt;balance&lt;/tt&gt; validation will add an error message &lt;em&gt;'Balance is invalid'&lt;/em&gt; in your &lt;tt&gt;@book#errors&lt;/tt&gt; collection. If you want to suppress that message from showing up, you can pass in option &lt;tt&gt;:message =&gt; nil&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;So, to put it all together, here is the view and the controller code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="scrolling_div textmate-source"&gt;&lt;span class="text text_html text_html_ruby"&gt;&lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;h1&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;New book&lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;h1&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%=&lt;/span&gt; error_messages_for &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;object&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;[&lt;/span&gt;&lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;book&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;book&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;balance&lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;]&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%&lt;/span&gt; form_for&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;book&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby"&gt;do &lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby"&gt;|&lt;/span&gt;&lt;span class="variable variable_other variable_other_block variable_other_block_ruby"&gt;f&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby"&gt;|&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Name: &lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%=&lt;/span&gt; f&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;text_field &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;name&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%&lt;/span&gt; f&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;fields_for &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;balance&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;book&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;balance &lt;span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby"&gt;do &lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby"&gt;|&lt;/span&gt;&lt;span class="variable variable_other variable_other_block variable_other_block_ruby"&gt;ff&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby"&gt;|&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Balance: &lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%=&lt;/span&gt; ff&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;text_field &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;balance&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Currency: &lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%=&lt;/span&gt; ff&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;text_field &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;currency&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%=&lt;/span&gt; f&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;submit &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;Create&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%=&lt;/span&gt; link_to &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;'&lt;/span&gt;Back&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; books_path &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To show error messages from multiple objects on the view, I am using the view helper method &lt;tt&gt;error_messages_for(*args)&lt;/tt&gt;. The &lt;tt&gt;:object&lt;/tt&gt; option actually allows you to pass an array of objects (c'mon, you should know this trick about &lt;tt&gt;ActiveRecord&lt;/tt&gt; by now. If not, check it out &lt;a href="http://www.stephenchu.com/2008/03/paramsfu-3-using-fieldsfor-and-index.html"&gt;here&lt;/a&gt; and &lt;a href="http://www.stephenchu.com/2008/03/paramsfu-5-update-multiple-models-in.html"&gt;here&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@book&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Book&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:book&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@book&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:notice&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Book was successfully created.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;redirect_to&lt;/span&gt; &lt;span class="attribute"&gt;@book&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;render&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Again, a skinny, thin, sexy-looking controller action.&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=Y3Dxxh"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=Y3Dxxh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=gSvnoh"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=gSvnoh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=HMcMhH"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=HMcMhH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=bLU1wH"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=bLU1wH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=p0gUuH"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=p0gUuH" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stephenchu/~4/294877952" height="1" width="1"/&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/stephenchu/~3/294877952/rails-composedof-validation.html" title="Rails composed_of validation" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=11933937&amp;postID=3716593703045725703" title="1 Comments" /><link rel="replies" type="application/atom+xml" href="http://www.stephenchu.com/feeds/3716593703045725703/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/3716593703045725703" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/3716593703045725703" /><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://www.stephenchu.com/2008/05/rails-composedof-validation.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-613937363191220353</id><published>2008-05-06T14:53:00.006-05:00</published><updated>2008-05-06T14:58:34.744-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails" /><title type="text">A normal Rails un-RESTful page as a resource... or can it?</title><content type="html">An &lt;tt&gt;index&lt;/tt&gt; page comes with Rails scaffolding. It is used to show a list of the "thing" you are CRUD-ing on. However, all too often we are tasked to show some pages that we aren't doing any CRUD operations on. Just a few examples:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Dashboard page after user logs in&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;The "Forgot your password?" page&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Tabs or subtabs of multiple lists&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;List page showing multiple lists of different entities&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;A normal dashboard page isn't something you would normally CRUD on. It is more like a place holder page that a user sees the first thing after s/he logs in, showing many items of all sorts valuable to a user. In many Rails app, depending on what those items are, the dashboard page will end up being rendered by one of the items' &lt;tt&gt;index&lt;/tt&gt; action. Worse yet, next time when you actually need that &lt;tt&gt;index&lt;/tt&gt; page by that model, you have to call it something else, maybe 'list', and then play with the routes to get it wired up correctly.&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;CustomersController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="comment"&gt;# The dashboard page&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;index&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@customers&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Customer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@products&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Product&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@tasks_of_the_day&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Task&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="comment"&gt;# A list of customers&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;list&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@customers&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Customer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="constant"&gt;ActionController&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Routing&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Routes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;draw&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;resources&lt;/span&gt; &lt;span class="symbol"&gt;:customers&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:products&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:tasks&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;with_options&lt;/span&gt; &lt;span class="symbol"&gt;:controller&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;customer&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;r&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;r&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;dashboard&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;/customers/list&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;list&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Here's a suggestion: how about put it in &lt;tt&gt;views/dashboards/show.html.erb&lt;/tt&gt;, and, while you are at it, give it a &lt;tt&gt;DashboardsController&lt;/tt&gt;? Then, put it in your &lt;tt&gt;routes.rb&lt;/tt&gt; as a (singular) &lt;tt&gt;map.resource :dashboard&lt;/tt&gt;, like such:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;DashboardsController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;show&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@customers&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Customer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@products&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Product&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@tasks_of_the_day&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Task&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="constant"&gt;ActionController&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Routing&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Routes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;draw&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;resources&lt;/span&gt; &lt;span class="symbol"&gt;:customers&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:products&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;tasks&lt;/span&gt;  &lt;br /&gt;  &lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;resource&lt;/span&gt; &lt;span class="symbol"&gt;:dashboard&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;By rendering the dashboard page in a completely different controller, you now have a very readable &lt;tt&gt;GET dashboard_path&lt;/tt&gt; named route (&lt;tt&gt;GET: http://localhost:3000/dashboards&lt;/tt&gt;), and you will not contaminate the &lt;tt&gt;index&lt;/tt&gt; action of your other models' controllers with instance variables of all kinds. You also have a more readable &lt;tt&gt;routes.rb&lt;/tt&gt; file.&lt;br /&gt;&lt;br /&gt;One of the examples above for non-RESTful pages is the "Forgot My Password" page. Can you think of a good way to do it the Rails REST-ful way? Please go to &lt;a href="http://www.stephenchu.com/2007/11/seeing-rails-resources-clearly.html"&gt;Seeing Rails Resources Clearly&lt;/a&gt; to share some of your thoughts.&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=ZK9qLh"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=ZK9qLh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=PoajAh"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=PoajAh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=kgoKsH"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=kgoKsH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=MyKoWH"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=MyKoWH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=jT3HdH"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=jT3HdH" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stephenchu/~4/284863775" height="1" width="1"/&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/stephenchu/~3/284863775/normal-rails-un-restful-page-as.html" title="A normal Rails un-RESTful page as a resource... or can it?" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=11933937&amp;postID=613937363191220353" title="1 Comments" /><link rel="replies" type="application/atom+xml" href="http://www.stephenchu.com/feeds/613937363191220353/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/613937363191220353" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/613937363191220353" /><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://www.stephenchu.com/2008/05/normal-rails-un-restful-page-as.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-4713401589862405098</id><published>2008-04-05T13:43:00.007-05:00</published><updated>2008-04-05T13:56:26.093-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="pattern" /><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails" /><title type="text">Pragmatic Rake tasks organization</title><content type="html">Do you have a lot of rake tasks on your Rails projects? I am sure you do. Rake tasks are handy, and developing your next-big-idea Rails app is a lot less attractive without it. Most of the time you drop a new &lt;tt&gt;foo.rake&lt;/tt&gt; file in your &lt;tt&gt;lib/tasks&lt;/tt&gt; folder, and you write code in it and get out. Over the course of development, that folder is likely to look like your favorite Halloween party house (ahem, &lt;a href="http://www.flickr.com/photos/nealford/291110547/"&gt;Neal's...&lt;/a&gt;), except on an every day basis.&lt;br /&gt;&lt;br /&gt;If you want your rake tasks to be more manageable, here are a few tips:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1) Namespace your rake tasks.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Namespaces in rake is nice. They make sure all tasks in your namespace are cohesive as a area of work. Still, your job is to keep your rake tasks not too crowded in a single namespace. For example, the &lt;tt&gt;db&lt;/tt&gt; namespace that Rails provides very easily become crowded with tasks of all sorts, because, well, on a Rails application what you do almost always is related to your database: drop/create/migrate your database, &lt;a href="http://www.stephenchu.com/2007/12/why-create-database-migrations-when-you.html"&gt;importing reference/application data&lt;/a&gt;, database integration points with other applications, db related background tasks, backups... Phew! How about create a new namespace for &lt;tt&gt;import&lt;/tt&gt;, &lt;tt&gt;integration&lt;/tt&gt;, &lt;tt&gt;background&lt;/tt&gt;, and &lt;tt&gt;backup&lt;/tt&gt; for each of them?&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2) Name your rake files by their namespaces.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Even if you namespace all your rake tasks, still, you are likely to end up with a bunch of &lt;tt&gt;.rake&lt;/tt&gt; files in &lt;tt&gt;lib/tasks&lt;/tt&gt;. But, now that you have your tasks grouped by their meaningful and not-so-crowded namespaces, you should name your files by your namespace. For example, &lt;tt&gt;db.rake&lt;/tt&gt;, &lt;tt&gt;data.rake&lt;/tt&gt;, &lt;tt&gt;import.rake&lt;/tt&gt;, &lt;tt&gt;integration.rake&lt;/tt&gt;, and &lt;tt&gt;backup.rake&lt;/tt&gt;. Can you guess where that &lt;tt&gt;rake db:awesome&lt;/tt&gt; is implemented? You certainly don't know what it does, but you got that right, in &lt;tt&gt;lib/tasks/db.rake&lt;/tt&gt;. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;3) Try not to nest more than 2 namespaces.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;This tip is not a rule, the point being don't over do it. Sometimes, that third-level namespace really makes sense, For example, &lt;tt&gt;db:create:all&lt;/tt&gt; from Rails creates databases for all environments. It isn't for everything, though. For example, I prefer &lt;tt&gt;rake data:load&lt;/tt&gt; better than &lt;tt&gt;rake db:data:load&lt;/tt&gt;. Not only is it shorter, but also the namespace &lt;tt&gt;db&lt;/tt&gt; is simply implied. Short nested-ness forces you to name your namespaces more descriptively, and thus easier to navigate to their implementation.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;4) Move your rake file and its external ruby files into a sub-folder, named by that namespace.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;When your rake tasks are complicated enough, you obviously will use objects to do the job for you. When that happens, instead of having &lt;tt&gt;lib/tasks/integration.rake&lt;/tt&gt;, you will instead want to have a sub-folder &lt;tt&gt;lib/tasks/integration/&lt;/tt&gt;, and put files like &lt;tt&gt;integration.rake&lt;/tt&gt; and &lt;tt&gt;integration_awesome_klass.rb&lt;/tt&gt; in it. There really is no need to contaminate your &lt;tt&gt;lib&lt;/tt&gt; folder with rake specific files.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Shameless Plug: Still using NAnt? Check out how to organize your NAnt scripts as well, on &lt;a href="http://www.stephenchu.com/2006/03/pragmatic-nant-scripting.html"&gt;this blog post&lt;/a&gt;.&lt;/em&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=JJvrRxg"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=JJvrRxg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=ErfJNcg"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=ErfJNcg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=Y7JW4FG"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=Y7JW4FG" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=wkDZFuG"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=wkDZFuG" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=58M2lnG"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=58M2lnG" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stephenchu/~4/264687354" height="1" width="1"/&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/stephenchu/~3/264687354/pragmatic-rake-tasks-organization.html" title="Pragmatic Rake tasks organization" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=11933937&amp;postID=4713401589862405098" title="0 Comments" /><link rel="replies" type="application/atom+xml" href="http://www.stephenchu.com/feeds/4713401589862405098/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/4713401589862405098" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/4713401589862405098" /><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://www.stephenchu.com/2008/04/pragmatic-rake-tasks-organization.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-6529429523836691861</id><published>2008-03-27T22:25:00.003-05:00</published><updated>2008-03-27T22:42:37.084-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails" /><title type="text">params[:fu] #5 ) Update multiple models in update action atomically.</title><content type="html">Updating multiple models is hard? It sounds complicated, but with Rails it actually isn't, if you know how to take advantage of it. Knowing what you know about Rails params, let's take a look at today's topic: the &lt;tt&gt;update&lt;/tt&gt; action.&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;form_for&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="attribute"&gt;@subscription&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="attribute"&gt;@subscriptions&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;    &amp;lt;p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;fields_for&lt;/span&gt; &lt;span class="attribute"&gt;@subscription&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;ff&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;      &amp;lt;%= ff.collection_select :magazine_id, Magazine.find(:all), :id, :name, {}, :index =&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@subscription&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;    &amp;lt;% end %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;p&amp;gt;&lt;br /&gt;  &amp;lt;% end %&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%= f.submit 'Save' %&amp;gt;&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;span class="normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;generates:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt; &lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;subscription_4_magazine_id&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;subscription[4][magazine_id]&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;101&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;PC&lt;/span&gt; &lt;span class="constant"&gt;Magazine&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt; &lt;br /&gt;    &amp;lt;option value=&amp;quot;102&amp;quot;&amp;gt;IT Pro&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;    &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;103&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="ident"&gt;selected&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;selected&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;WIRED&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt;&lt;br /&gt;  &amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;p&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;p&amp;gt;     &lt;br /&gt;  &amp;lt;select id=&amp;quot;subscription_5_magazine_id&amp;quot; name=&amp;quot;subscription[5][magazine_id]&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;option value=&amp;quot;101&amp;quot;&amp;gt;PC Magazine&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;    &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;102&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;IT&lt;/span&gt; &lt;span class="constant"&gt;Pro&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt; &lt;br /&gt;    &amp;lt;option value=&amp;quot;103&amp;quot; selected=&amp;quot;selected&amp;quot;&amp;gt;WIRED&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;select&amp;gt;&lt;br /&gt;&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;  &lt;br /&gt;&lt;span class="punct"&gt;...&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="ident"&gt;more&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;    &lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class='scrolling_div'&gt;&lt;pre&gt;Processing ReadersController#update (for 127.0.0.1 at 2008-01-14 21:12:56) [PUT]&lt;br /&gt;Parameters: { "commit"             =&gt; "Update",&lt;br /&gt;              "reader"             =&gt; { "name" =&gt; "stephen chu" },&lt;br /&gt;              "subscriptions"      =&gt; { "4" =&gt; { "magazine_id" =&gt; "101" },&lt;br /&gt;                                        "6" =&gt; { "magazine_id" =&gt; "102" }, &lt;br /&gt;                                        "7" =&gt; { "magazine_id" =&gt; "103" } },&lt;br /&gt;              "authenticity_token" =&gt; "238ba79b8282882ba01d840352616c2cc79280f0",&lt;br /&gt;              "action"             =&gt; "create",&lt;br /&gt;              "controller"         =&gt; "readers" }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;See the pattern? The POST-ed parameters are of the same structure as in one of our &lt;a href="http://www.stephenchu.com/2008/03/paramsfu-3-using-fieldsfor-and-index.html"&gt;last example&lt;/a&gt;, hash of hashes. The sub-hashes are keying off of the subscription id, because this time around we are updating existing subscriptions. So last time we used &lt;tt&gt;params[:subscriptions].values&lt;/tt&gt;, what would it look like this time? Let take a look.&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;update&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:id&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;attributes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;subscriptions&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;update&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:subscription&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;keys&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:subscription&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;values&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:notice&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Reader was successfully updated.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;redirect_to&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:notice&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Failed.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Again, another ActiveRecord model method utilizes the array of hashes pattern! The &lt;tt&gt;update&lt;/tt&gt; method source code on RDoc looks like it is just updating one at a time. But a peek at the source code says otherwise:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;update&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;attributes&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Array&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;idx&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;-&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;collect&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;idx&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;update&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;attributes&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;idx&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;object&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;object&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;update_attributes&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;attributes&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;object&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Again, the &lt;tt&gt;update&lt;/tt&gt; method recognizes array! So, where to get the arrays that we will use in our controller action? They come from &lt;tt&gt;.keys&lt;/tt&gt; and &lt;tt&gt;.values&lt;/tt&gt; of course:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;keys&lt;/span&gt;    &lt;span class="comment"&gt;# =&amp;gt; [ &amp;quot;4&amp;quot;, &amp;quot;6&amp;quot;, &amp;quot;7&amp;quot; ]&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;values&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; [ { &amp;quot;magazine_id&amp;quot; =&amp;gt; &amp;quot;101&amp;quot; }, { &amp;quot;magazine_id&amp;quot; =&amp;gt; &amp;quot;102&amp;quot; }, ... ]&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So in essence, our controller code is free from all those ugly params-munging activities. Remember, controller actions should not shuffle around their params, or otherwise it fails to abide the "Skinny Controller, Fat Model" principle, and they will stink.&lt;br /&gt;&lt;br /&gt;Now, if you are thinking about by using &lt;tt&gt;update&lt;/tt&gt;, we run the risk of not atomically saving all of our models should any of our models fail validation, you are correct. This is where &lt;tt&gt;rescue_from&lt;/tt&gt; in controller saves the day. Just transact our &lt;tt&gt;update&lt;/tt&gt; action using AR transaction, and re-render the edit page should it catches &lt;tt&gt;ActiveRecord::RecordInvalid&lt;/tt&gt; error, you should be able to make your &lt;tt&gt;update&lt;/tt&gt; action atomic. Given how lean our controller action looks like, having a transaction block that wraps around our code is not so much a nuisance anymore.&lt;br /&gt;&lt;br /&gt;This also wraps up our &lt;a href="http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html"&gt;&lt;tt&gt;params[:fu]&lt;/tt&gt; series&lt;/a&gt;. Remember, how you assemble your views form elements have a lot to do with how thin and skinny your controllers look like. Thanks for reading!&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=qLneY8f"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=qLneY8f" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=8KGqCFf"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=8KGqCFf" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=RcEPO2F"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=RcEPO2F" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=ypL0QxF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=ypL0QxF" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=TDNUJbF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=TDNUJbF" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stephenchu/~4/259400035" height="1" width="1"/&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/stephenchu/~3/259400035/paramsfu-5-update-multiple-models-in.html" title="params[:fu] #5 ) Update multiple models in &lt;tt&gt;update&lt;/tt&gt; action atomically." /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=11933937&amp;postID=6529429523836691861" title="1 Comments" /><link rel="replies" type="application/atom+xml" href="http://www.stephenchu.com/feeds/6529429523836691861/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/6529429523836691861" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/6529429523836691861" /><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://www.stephenchu.com/2008/03/paramsfu-5-update-multiple-models-in.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-3770960745050475939</id><published>2008-03-26T22:53:00.003-05:00</published><updated>2008-03-26T23:05:01.216-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails" /><title type="text">params[:fu] #4 ) Use the magical &lt;association_name&gt;_ids=([...array of ids]) association proxy method.</title><content type="html">In the &lt;a href="http://www.stephenchu.com/2008/03/paramsfu-3-using-fieldsfor-and-index.html"&gt;last blog entry&lt;/a&gt;, we looked at an example of having to build a new parent model and build its join models all in two lines of code. But if you are trying to just assign a list of &lt;tt&gt;has_many&lt;/tt&gt; or &lt;tt&gt;has_and_belongs_to_many&lt;/tt&gt; models, you don't have to use the &lt;tt&gt;fields_for&lt;/tt&gt; and &lt;tt&gt;index&lt;/tt&gt; trick. You can just use an association proxy method. Let's check it out:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Reader&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;has_and_belongs_to_many&lt;/span&gt; &lt;span class="symbol"&gt;:blogs&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;form_for&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; f.collection_select :blog_ids, Blog.find(:all), :id, :name, {}, :multiple &lt;/span&gt;&lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%= f.submit 'Create' %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;generates:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt; &lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;reader_blog_ids&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="ident"&gt;multiple&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;multiple&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;reader[blog_ids][]&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;Martin&lt;/span&gt; &lt;span class="constant"&gt;Fowler&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt;&lt;br /&gt;  &amp;lt;option value=&amp;quot;2&amp;quot;&amp;gt;Stephen Chu&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;select&amp;gt; &lt;span class="normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class='scrolling_div'&gt;&lt;pre&gt;Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]&lt;br /&gt;Parameters: { "commit"             =&gt; "Create",&lt;br /&gt;              "reader"             =&gt; { "name" =&gt; "stephen chu"&lt;br /&gt;                                        "blog_ids" =&gt; ["1", "2"] },&lt;br /&gt;              "authenticity_token" =&gt; "238ba79b8282882ba01d840352616c2cc79280f0",&lt;br /&gt;              "action"             =&gt; "create",&lt;br /&gt;              "controller"         =&gt; "readers" }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;And now, in my controller action, the creation of the reader only takes one line of code:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;... thanks to the following &lt;tt&gt;has_many&lt;/tt&gt; (or &lt;tt&gt;habtm&lt;/tt&gt;) association assignment method:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;blog_ids&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[...many ids...]&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So, stop doing any more params-munging code in your controller action like these:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:blog_ids&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;blogs&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;Blog&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Get on the Rails bandwagon!&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=2hM8Aqf"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=2hM8Aqf" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=9bgaCHf"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=9bgaCHf" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=fDp6HrF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=fDp6HrF" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=XUrdVXF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=XUrdVXF" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=uihYWyF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=uihYWyF" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stephenchu/~4/258733238" height="1" width="1"/&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/stephenchu/~3/258733238/paramsfu-4-use-magical-of-ids.html" title="params[:fu] #4 ) Use the magical &lt;tt&gt;&amp;lt;association_name&amp;gt;_ids=([...array of ids])&lt;/tt&gt; association proxy method." /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=11933937&amp;postID=3770960745050475939" title="1 Comments" /><link rel="replies" type="application/atom+xml" href="http://www.stephenchu.com/feeds/3770960745050475939/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/3770960745050475939" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/3770960745050475939" /><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://www.stephenchu.com/2008/03/paramsfu-4-use-magical-of-ids.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-6542929948815344833</id><published>2008-03-25T21:19:00.003-05:00</published><updated>2008-03-25T21:30:59.525-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails" /><title type="text">params[:fu] #3 ) Using fields_for and the index option to create a new parent model with multiple child models.</title><content type="html">Alright, the last couple days were easy. Today, let's take a look at a slightly more complicated example, but one that occurs on almost every single Rails project out there: saving multiple models on one POST. Let's say your &lt;tt&gt;new&lt;/tt&gt; form allows you to create a new reader and attach 3 subscriptions to it. Here's the code:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Reader&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;has_many&lt;/span&gt; &lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;has_many&lt;/span&gt; &lt;span class="symbol"&gt;:magazines&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:through&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_associated&lt;/span&gt; &lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;form_for&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="number"&gt;3&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;index&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;    &amp;lt;% fields_for :subscriptions do |ff| %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; ff.collection_select :magazine_id, Magazine.find(:all), :id, :name, {}, :index &lt;/span&gt;&lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;index&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&amp;lt;/p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;% end %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; f.submit 'Create' %&amp;gt;&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;span class="normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;generates:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt; &lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;subscriptions_1_magazine_id&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;subscriptions[1][magazine_id]&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;101&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;PC&lt;/span&gt; &lt;span class="constant"&gt;Magazine&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt;&lt;br /&gt;  &amp;lt;option value=&amp;quot;102&amp;quot;&amp;gt;IT Pro&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;103&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;WIRED&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt;&lt;br /&gt;&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt; &lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;subscriptions_2_magazine_id&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;subscriptions[2][magazine_id]&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;101&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;PC&lt;/span&gt; &lt;span class="constant"&gt;Magazine&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt;&lt;br /&gt;  &amp;lt;option value=&amp;quot;102&amp;quot;&amp;gt;IT Pro&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;103&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;WIRED&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt;&lt;br /&gt;&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;... (and more)&lt;br /&gt;&lt;br /&gt;&lt;div class='scrolling_div'&gt;&lt;pre&gt;Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]&lt;br /&gt;Parameters: { "commit"             =&gt; "Create",&lt;br /&gt;              "reader"             =&gt; { "name" =&gt; "stephen chu" },&lt;br /&gt;              "subscriptions"      =&gt; { "1" =&gt; { "magazine_id" =&gt; "101" },&lt;br /&gt;                                        "2" =&gt; { "magazine_id" =&gt; "102" }, &lt;br /&gt;                                        "3" =&gt; { "magazine_id" =&gt; "103" } },&lt;br /&gt;              "authenticity_token" =&gt; "238ba79b8282882ba01d840352616c2cc79280f0",&lt;br /&gt;              "action"             =&gt; "create",&lt;br /&gt;              "controller"         =&gt; "readers" }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;By using the &lt;tt&gt;:index&lt;/tt&gt; html option in our form builder generated fields, we are essentially inserting a unique index key to the posted value of that field. Why should we care? Well, here is how the controller code would look like:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@subscriptions&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;subscriptions&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;build&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;values&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:success&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Good.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:error&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Bad.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I just persisted a reader with &lt;em&gt;multiple&lt;/em&gt; subscriptions, and the notable differences I added in the create action, was these characters: &lt;tt&gt;params[:subscriptions]&lt;strong&gt;.values&lt;/strong&gt;&lt;/tt&gt;. Nothing much changed from the last &lt;tt&gt;has_one :computer&lt;/tt&gt; example besides association related differences. There is no looping, map/collect-ing, gsub-ing, etc., in my action while creating and attaching these multiple subscriptions onto the new reader. The controller action is just doing its same-old routine: receives posted parameters, and shove them into the corresponding models. So how is this done? &lt;br /&gt;&lt;br /&gt;Looking at the source of the &lt;tt&gt;#build&lt;/tt&gt; method on the association proxy classes (e.g. &lt;tt&gt;HasManyAssociation&lt;/tt&gt;), you will notice something interesting:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;build&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;attributes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{})&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;attributes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Array&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;attributes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;collect&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;attr&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;build&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;attr&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;build_record&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;attributes&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;record&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;set_belongs_to_association_for&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;record&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The method recognizes array! If you pass in an array of hashes, it will process them one by one! So how do we get an array of hashes? In our case the way to get array of hashes is by calling &lt;tt&gt;Hash#values&lt;/tt&gt;, because they are organized in sub-hashes. Calling &lt;tt&gt;.values&lt;/tt&gt; will yield us the following array:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;values&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; [ { &amp;quot;magazine_id&amp;quot; =&amp;gt; &amp;quot;101&amp;quot; }, { &amp;quot;magazine_id&amp;quot; =&amp;gt; &amp;quot;102&amp;quot; }, ... ]&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;By organizing your &lt;tt&gt;params&lt;/tt&gt; on your view in ways that they can be directly consumed by your models, you end up with a lot less code to write. No more bastardizing your action code!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html"&gt;(back to the TOC of the params[:fu] series)&lt;/a&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=NxL9B2f"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=NxL9B2f" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=p4bDncf"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=p4bDncf" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=Bofw3rF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=Bofw3rF" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=TaHOtyF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=TaHOtyF" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=gMjNPiF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=gMjNPiF" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stephenchu/~4/258034988" height="1" width="1"/&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/stephenchu/~3/258034988/paramsfu-3-using-fieldsfor-and-index.html" title="params[:fu] #3 ) Using &lt;tt&gt;fields_for&lt;/tt&gt; and the &lt;tt&gt;index&lt;/tt&gt; option to create a new parent model with multiple child models." /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=11933937&amp;postID=6542929948815344833" title="6 Comments" /><link rel="replies" type="application/atom+xml" href="http://www.stephenchu.com/feeds/6542929948815344833/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/6542929948815344833" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/6542929948815344833" /><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://www.stephenchu.com/2008/03/paramsfu-3-using-fieldsfor-and-index.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-5909803697093839433</id><published>2008-03-24T23:03:00.002-05:00</published><updated>2008-03-24T23:04:49.552-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails" /><title type="text">params[:fu] #2 ) Put attributes into a different params key using fields_for if they belong to different models.</title><content type="html">&lt;tt&gt;fields_for&lt;/tt&gt; is very useful for separating out your params:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;form_for&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;fields_for&lt;/span&gt; &lt;span class="symbol"&gt;:favourite_computer&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="attribute"&gt;@computer&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;ff&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;    &amp;lt;%= ff.text_field :name %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;%= f.submit 'Create' %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="scrolling_div"&gt;&lt;pre&gt;Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]&lt;br /&gt;Parameters: { "commit"             =&gt; "Create",&lt;br /&gt;              "reader"             =&gt; { "name" =&gt; "stephen chu" },&lt;br /&gt;              "favourite_computer" =&gt; { "name" =&gt; "macbook pro" },&lt;br /&gt;              "authenticity_token" =&gt; "238ba79b8282882ba01d840352616c2cc79280f0",&lt;br /&gt;              "action"             =&gt; "create",&lt;br /&gt;              "controller"         =&gt; "readers" }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@computer&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;build_computer&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:favourite_computer&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:success&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Good.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:error&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Bad.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Reader&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;has_one&lt;/span&gt; &lt;span class="symbol"&gt;:computer&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_associated&lt;/span&gt; &lt;span class="symbol"&gt;:computer&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Here, I am on a page that creates the reader and its favorite computer. Two models sounds hard? Not really, if you key your params to a different key for each of the model, your controller is still nice and thin. You do need to add one line to the action to create the computer though, but that maps nicely to the fact that you are operating on two models from the page post. I would not want to add more than one line of code to create one additional model, so forget those params-munging/looping.&lt;br /&gt;&lt;br /&gt;Also, you can see that I am using &lt;tt&gt;validates_associated&lt;/tt&gt; here. The point being I do not want to call &lt;tt&gt;save&lt;/tt&gt; or &lt;tt&gt;valid?&lt;/tt&gt; once for each model in my action, making my controller code fat.&lt;br /&gt;&lt;br /&gt;Also, &lt;tt&gt;fields_for&lt;/tt&gt; is not only just a helper method, but also a method available on your &lt;tt&gt;FormBuilder&lt;/tt&gt; as well (you know, the &lt;tt&gt;f&lt;/tt&gt; in your &lt;tt&gt;form_for&lt;/tt&gt; is an instance of a &lt;tt&gt;FormBuilder&lt;/tt&gt;. To see an example of how to call it off of a form builder, check out this &lt;a href="http://www.stephenchu.com/2008/03/introduction-to-rails-composedof.html"&gt;blog entry&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html"&gt;(back to the TOC of the params[:fu] series)&lt;/a&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=NrlWeTf"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=NrlWeTf" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=gtb8FDf"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=gtb8FDf" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=K8cQaTF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=K8cQaTF" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=f76sggF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=f76sggF" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=ecN6fhF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=ecN6fhF" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stephenchu/~4/257423406" height="1" width="1"/&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/stephenchu/~3/257423406/paramsfu-2-put-attributes-into.html" title="params[:fu] #2 ) Put attributes into a different params key using &lt;tt&gt;fields_for&lt;/tt&gt; if they belong to different models." /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=11933937&amp;postID=5909803697093839433" title="1 Comments" /><link rel="replies" type="application/atom+xml" href="http://www.stephenchu.com/feeds/5909803697093839433/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/5909803697093839433" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/5909803697093839433" /><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://www.stephenchu.com/2008/03/paramsfu-2-put-attributes-into.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-4750190244110730864</id><published>2008-03-23T22:47:00.009-05:00</published><updated>2008-03-24T23:04:29.704-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails" /><title type="text">params[:fu] #1 ) Wrap all relevant attributes in a single params key.</title><content type="html">This is the simplest form of using &lt;tt&gt;form_for&lt;/tt&gt; and &lt;tt&gt;params&lt;/tt&gt;:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;form_for&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; f.text_field :birth_place %&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;submit&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Create&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;&amp;lt;% end %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class='scrolling_div'&gt;&lt;pre&gt;Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]&lt;br /&gt;Parameters: { "commit"             =&gt; "Create",&lt;br /&gt;              "reader"             =&gt; { "name" =&gt; "stephen chu", "birth_place" =&gt; "hong kong" },&lt;br /&gt;              "authenticity_token" =&gt; "238ba79b8282882ba01d840352616c2cc79280f0",&lt;br /&gt;              "action"             =&gt; "create",&lt;br /&gt;              "controller"         =&gt; "readers"}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;create!&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;By doing this, you eliminate the need to explicitly spell out each parameters posted, like the ugly &lt;tt&gt;Reader.new :name =&gt; params[:name], :birth_place =&gt; params[:birth_place]&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; { 'first_name' =&amp;gt; 'stephen', 'last_name' =&amp;gt; 'chu' }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="attribute"&gt;@readers&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:conditions&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:search&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; { 'name' =&amp;gt; 'chu' }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;attributes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; { 'first_name' =&amp;gt; 'martin', 'last_name' =&amp;gt; 'fowler' }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;update_attributes&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; { 'birth_place' =&amp;gt; 'hong kong' }&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Notice the find with condition one. No one says I cannot have a hash or &lt;tt&gt;form_for&lt;/tt&gt; that must map to a database table!&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html"&gt;(back to the TOC of the params[:fu] series)&lt;/a&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=yZaVDhf"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=yZaVDhf" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=wSuuKAf"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=wSuuKAf" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=EuxspeF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=EuxspeF" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=BT3dQNF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=BT3dQNF" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/stephenchu?a=mszoduF"&gt;&lt;img src="http://feeds.feedburner.com/~f/stephenchu?i=mszoduF" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stephenchu/~4/256825564" height="1" width="1"/&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/stephenchu/~3/256825564/paramfu-1-wrap-all-relevant-attributes.html" title="params[:fu] #1 ) Wrap all relevant attributes in a single params key." /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=11933937&amp;postID=4750190244110730864" title="1 Comments" /><link rel="replies" type="application/atom+xml" href="http://www.stephenchu.com/feeds/4750190244110730864/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/default/4750190244110730864" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/11933937/posts/defau