<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-11933937</id><updated>2025-12-04T13:32:29.341-06:00</updated><category term="ruby/rails"/><category term=".net"/><category term="pattern"/><category term="efficiency"/><category term="agile"/><category term="humor"/><category term="mysql"/><title type='text'>STEPHEN CHU . com</title><subtitle type='html'>My thoughts on software development, open source, agile methodology, technology, and stuff that matters.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.stephenchu.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default?alt=atom'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default?alt=atom&amp;start-index=26&amp;max-results=25'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>69</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-11933937.post-6593069220704055537</id><published>2008-12-01T18:22:00.004-06:00</published><updated>2008-12-01T18:31:07.245-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="mysql"/><title type='text'>Speed up your MySQL data load</title><content type='html'>Perhaps you have some large datasets you would like to load into MySQL, such as a big text file generated from &lt;em&gt;mysqldump&lt;/em&gt; that contains many sql INSERT statements. You can obviously populate your empty database by redirecting the content of this file into the &lt;em&gt;mysql&lt;/em&gt; command line like such:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  $ mysql -u root -p your_database &lt; /tmp/table.dump&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Another way to load data into your database or table is to use the sql syntax LOAD DATA INFILE. It requires you to provide a file that is in some delimiter format such as csv or tab-delimited values. One can use the &lt;em&gt;mysqldump&lt;/em&gt; command line switches &lt;em&gt;--fields-*-by&lt;/em&gt; and &lt;em&gt;--tab&lt;/em&gt; to dump the content of the database to such format.&lt;br /&gt;&lt;br /&gt;After you started either of these data load, you may want to kick off your shoes and take a nap, cos this will take a while if your MySQL server is not tuned and your dump file is pretty big (in this example, an InnoDB table of 790,000 rows [~500 MB dump] took 45 mins on my MBP using a Macports [mysql5 @5.0.67_1] installation with default settings). If you have to reload your database/tables often, this is unbearably slow.&lt;br /&gt;&lt;br /&gt;Fortunately, there are a few MySQL tuning settings that you can specify at mysql server start-up time to tremendously speed up your data load time. Keep in mind, it is advisable to flush your MySQL logs (like the following) prior to tinkering with any log file size settings to avoid any log file corruptions:&lt;br /&gt;&lt;br /&gt;(Before you proceed, I recommend you backup your data first)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  $ sudo mysqladmin flush-logs&lt;br /&gt;  $ ... then shutdown your MySQL server ...&lt;br /&gt;  $ sudo rm /path/to/mysql/datadir/ib_logfile*&lt;br /&gt;  $ ... then start your MySQL server ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, now it&#39;s time to put in our magic sauce. On my Macports MySQL installation, the settings were meagerly defaulted to:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  +---------------------------------+---------+&lt;br /&gt;  | Variable_name                   | Value   |&lt;br /&gt;  +---------------------------------+---------+&lt;br /&gt;  | innodb_additional_mem_pool_size | 1048576 |&lt;br /&gt;  | innodb_buffer_pool_size         | 8388608 |&lt;br /&gt;  | innodb_log_buffer_size          | 1048576 |&lt;br /&gt;  | innodb_log_file_size            | 5242880 |&lt;br /&gt;  | bulk_insert_buffer_size         | 8388608 |&lt;br /&gt;  +---------------------------------+---------+&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Add the following settings in your appropriate &lt;em&gt;my.cnf&lt;/em&gt; file. Your settings may vary with the kind of machine you have (mine is a MBP 2GB RAM):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  [mysqld]&lt;br /&gt;  innodb_additional_mem_pool_size=256M&lt;br /&gt;  innodb_buffer_pool_size=512M&lt;br /&gt;  innodb_log_buffer_size=256M&lt;br /&gt;  innodb_log_file_size=512M&lt;br /&gt;  bulk_insert_buffer_size=256M&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Our settings will now look like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  +---------------------------------+-----------+&lt;br /&gt;  | Variable_name                   | Value     |&lt;br /&gt;  +---------------------------------+-----------+&lt;br /&gt;  | innodb_additional_mem_pool_size | 268435456 |&lt;br /&gt;  | innodb_buffer_pool_size         | 536870912 |&lt;br /&gt;  | innodb_log_buffer_size          | 268435456 |&lt;br /&gt;  | innodb_log_file_size            | 536870912 |&lt;br /&gt;  | bulk_insert_buffer_size         | 268435456 |&lt;br /&gt;  +---------------------------------+-----------+&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then, as I reload my table, it only takes just 4&#39; 55&quot;, a whopping &lt;strong&gt;900% performance boost&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;Bonus Tip:&lt;br /&gt;&lt;br /&gt;You might notice that you cannot use SELECT COUNT(*) to track your data load progress while LOAD DATA INFILE is being executed (it always returns a count of zero). Instead, you can use the SHOW INNODB STATUS command. Find the line that says &quot;undo log entries&quot; under section TRANSACTIONS, and that number is the number of rows inserted so far. You can also look at how many inserts were performed per second by finding the line &quot;inserts/s&quot; under the ROW OPERATIONS section.</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/6593069220704055537/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/6593069220704055537' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/6593069220704055537'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/6593069220704055537'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/12/speed-up-your-mysql-data-load.html' title='Speed up your MySQL data load'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-4031210489060497508</id><published>2008-09-26T12:18:00.003-05:00</published><updated>2008-09-26T12:38:17.810-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="efficiency"/><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails"/><title type='text'>Go to Ruby ! and ? method definition with TextMate CTags</title><content type='html'>If you are not yet using the TextMate &lt;a href=&quot;http://gerd.knops.org/?p=7&quot;&gt;CTags bundle&lt;/a&gt; in your Ruby/Rails development, you are missing out big time. This bundle allows you to go to a method definition as easy as pointing your cursor at the method and hit a keyboard shortcut. It has served me well and I highly recommend it.&lt;br /&gt;&lt;br /&gt;But soon you might realize that this &quot;go to definition&quot; shortcut only works most of the time. Specifically, whenever you try to go to the definition of a Ruby method that ends with a bang (!) or question-mark (?), CTags will fail open to the line that defines that method for you.&lt;br /&gt;&lt;br /&gt;Here&#39;s a fix for you. The perl script that got executed underneath this little utility actually uses a TextMate variable &lt;span style=&quot;font-style:italic;&quot;&gt;TM_CURRENT_WORD&lt;/span&gt;. This variable points at the &quot;word&quot; your cursor is currently laying on top of in TextMate. But since by default, TextMate does not consider ! or ? to be part of a word, your CTags fails on you. All you need to do, is go to &lt;span style=&quot;font-style:italic;&quot;&gt;Preferences / Text Editing&lt;/span&gt;, and change &lt;span style=&quot;font-style:italic;&quot;&gt;Word Characters&lt;/span&gt; to include ! and ?.&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://3.bp.blogspot.com/_OeQDslkBljg/SN0dmv4NgpI/AAAAAAAAADg/7kEDBsZxxsc/s1600-h/ctags_result.jpg&quot;&gt;&lt;img style=&quot;cursor:pointer; cursor:hand;&quot; src=&quot;http://3.bp.blogspot.com/_OeQDslkBljg/SN0dmv4NgpI/AAAAAAAAADg/7kEDBsZxxsc/s400/ctags_result.jpg&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5250385292108333714&quot; /&gt;&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/4031210489060497508/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/4031210489060497508' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/4031210489060497508'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/4031210489060497508'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/09/go-to-ruby-and-method-definition-with.html' title='Go to Ruby ! and ? method definition with TextMate CTags'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_OeQDslkBljg/SN0dmv4NgpI/AAAAAAAAADg/7kEDBsZxxsc/s72-c/ctags_result.jpg" height="72" width="72"/><thr:total>2</thr:total></entry><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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;attribute&quot;&gt;@posts&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:conditions&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:user_id&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;punct&quot;&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;attribute&quot;&gt;@posts&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&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;&quot;in the database table &#39;&lt;tt&gt;posts&lt;/tt&gt;&#39; and therefore &lt;tt&gt;ActiveRecord&lt;/tt&gt; model &#39;&lt;tt&gt;Post&lt;/tt&gt;&#39;, retrieve the rows whose foreign key column &#39;&lt;tt&gt;user_id&lt;/tt&gt;&#39; has the value &lt;tt&gt;@user.id&lt;/tt&gt;.&quot;&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;symbol&quot;&gt;:model_id&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@model&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Just stop. &quot;Objects on Rails&quot; sounds a lot better than &quot;Ruby on Foreign Keys.&quot;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/2462354306162455570/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/2462354306162455570' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/2462354306162455570'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/2462354306162455570'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/06/object-oriented-programming-with-rails.html' title='Object-Oriented Programming with Rails ActiveRecord'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;ident&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Drink up your milk!&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@milk&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;expiration_date&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;days&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&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=&quot;http://www.stephenchu.com/2007/01/use-your-test-classes-to-identify.html&quot;&gt;by how smelly your tests are&lt;/a&gt;, as I believe in &lt;a href=&quot;http://www.stephenchu.com/2007/12/last-d-in-tdd-means-more-than-just.html&quot;&gt;Test Driven Design&lt;/a&gt;. Going back to the line of code. What&#39;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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;constant&quot;&gt;MyMailer&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;deliver_product_returned_notification&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@product&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Returned&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;ident&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@line_items&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;line_item&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;line_item&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;line_item&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;quantity&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:admin&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:superuser&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;attribute&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now, let&#39;s look at these rewritten examples:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;ident&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Drink up your milk!&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@milk&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;expiring?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;constant&quot;&gt;MyMailer&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;deliver_product_returned_notification&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@product&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;returned?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;ident&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@line_items&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;amp;&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:subtotal&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;superuser?&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&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&#39;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&#39;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.</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/4982606554165380166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/4982606554165380166' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/4982606554165380166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/4982606554165380166'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/06/learning-encapsulation-should-come.html' title='Learning encapsulation should come before learning Rails'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></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.</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/89965006330598668/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/89965006330598668' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/89965006330598668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/89965006330598668'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/06/launching-apps-in-command-line-mac.html' title='Launching apps in command line (Mac)'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></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 &quot;Skinny Controllers&quot;. To learn about, visit, check out the &lt;a href=&quot;http://www.stephenchu.com/2008/03/introduction-to-rails-composedof.html&quot;&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&#39;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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Money&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:Validations&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:balance&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:currency&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;validates_presence_of&lt;/span&gt;  &lt;span class=&quot;symbol&quot;&gt;:currency&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;validates_inclusion_of&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:currency&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:in&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;[&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;USD&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;,&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;EUR&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;],&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:if&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:currency?&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;validates_presence_of&lt;/span&gt;     &lt;span class=&quot;symbol&quot;&gt;:balance&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;validates_numericality_of&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:balance&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:if&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:balance?&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;balance&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;currency&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@balance&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@currency&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;balance&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;currency&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@errors&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Errors&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;self&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;new_record?&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;constant&quot;&gt;true&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;currency?&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;punct&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;attribute&quot;&gt;@currency&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;blank?&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;balance?&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;punct&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;attribute&quot;&gt;@balance&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;blank?&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;self.human_attribute_name&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;humanize&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;balance_before_type_cast&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@balance&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;  &lt;br /&gt;&lt;span class=&quot;keyword&quot;&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=&quot;http://dev.rubyonrails.org/ticket/8760&quot;&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=&quot;http://www.stephenchu.com/2008/01/format-your-data-using-rails-composedof.html&quot;&gt;this&lt;/a&gt; and &lt;a href=&quot;http://www.stephenchu.com/2008/03/little-tos-can-do-wonder-in-dry-ing-up.html&quot;&gt;this&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;After all these, let&#39;s see our ActiveRecord &lt;tt&gt;Book&lt;/tt&gt; class.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Bases&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;composed_of&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:balance&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:class_name&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:mapping&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;[%w(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;balance balance&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%w(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;currency currency&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;br /&gt;     &lt;span class=&quot;constant&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:balance&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:currency&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;validates_presence_of&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:name&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;validates_associated&lt;/span&gt;  &lt;span class=&quot;symbol&quot;&gt;:balance&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&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;&#39;Balance is invalid&#39;&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=&quot;scrolling_div textmate-source&quot;&gt;&lt;span class=&quot;text text_html text_html_ruby&quot;&gt;&lt;span class=&quot;meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;New book&lt;span class=&quot;meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;&amp;lt;%=&lt;/span&gt; error_messages_for &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;object&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_separator punctuation_separator_key-value&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby&quot;&gt;@&lt;/span&gt;book&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby&quot;&gt;@&lt;/span&gt;book&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;balance&lt;span class=&quot;punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;&amp;lt;%&lt;/span&gt; form_for&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby&quot;&gt;@&lt;/span&gt;book&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby&quot;&gt;do &lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;variable variable_other variable_other_block variable_other_block_ruby&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Name: &lt;span class=&quot;source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;&amp;lt;%=&lt;/span&gt; f&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;text_field &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;name&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;&amp;lt;%&lt;/span&gt; f&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;fields_for &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;balance&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby&quot;&gt;@&lt;/span&gt;book&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;balance &lt;span class=&quot;keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby&quot;&gt;do &lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;variable variable_other variable_other_block variable_other_block_ruby&quot;&gt;ff&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Balance: &lt;span class=&quot;source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;&amp;lt;%=&lt;/span&gt; ff&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;text_field &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;balance&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Currency: &lt;span class=&quot;source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;&amp;lt;%=&lt;/span&gt; ff&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;text_field &lt;span class=&quot;constant constant_other constant_other_symbol constant_other_symbol_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby&quot;&gt;:&lt;/span&gt;currency&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;&amp;lt;%=&lt;/span&gt; f&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby&quot;&gt;.&lt;/span&gt;submit &lt;span class=&quot;string string_quoted string_quoted_double string_quoted_double_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&quot;&lt;/span&gt;Create&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword keyword_control keyword_control_ruby&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html&quot;&gt;&lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&gt;&amp;lt;%=&lt;/span&gt; link_to &lt;span class=&quot;string string_quoted string_quoted_single string_quoted_single_ruby&quot;&gt;&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby&quot;&gt;&#39;&lt;/span&gt;Back&lt;span class=&quot;punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby&quot;&gt;,&lt;/span&gt; books_path &lt;span class=&quot;punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby&quot;&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&#39;mon, you should know this trick about &lt;tt&gt;ActiveRecord&lt;/tt&gt; by now. If not, check it out &lt;a href=&quot;http://www.stephenchu.com/2008/03/paramsfu-3-using-fieldsfor-and-index.html&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;http://www.stephenchu.com/2008/03/paramsfu-5-update-multiple-models-in.html&quot;&gt;here&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@book&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:book&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;save&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;flash&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:notice&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Book was successfully created.&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@book&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:action&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&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.</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/3716593703045725703/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/3716593703045725703' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/3716593703045725703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/3716593703045725703'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/05/rails-composedof-validation.html' title='Rails composed_of validation'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></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 &quot;thing&quot; you are CRUD-ing on. However, all too often we are tasked to show some pages that we aren&#39;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 &quot;Forgot your password?&quot; 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&#39;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&#39; &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 &#39;list&#39;, and then play with the routes to get it wired up correctly.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;CustomersController&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;comment&quot;&gt;# The dashboard page&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;index&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@customers&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:all&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@products&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:all&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@tasks_of_the_day&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:all&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;comment&quot;&gt;# A list of customers&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;list&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@customers&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:all&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;constant&quot;&gt;ActionController&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Routing&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Routes&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;draw&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;resources&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:customers&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:products&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:tasks&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;with_options&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:controller&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;customer&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;dashboard&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;/customers/list&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:action&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Here&#39;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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;DashboardsController&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;show&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@customers&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:all&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@products&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:all&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@tasks_of_the_day&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:all&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;constant&quot;&gt;ActionController&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Routing&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Routes&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;draw&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;resources&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:customers&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:products&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;tasks&lt;/span&gt;  &lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:dashboard&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&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&#39; 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 &quot;Forgot My Password&quot; page. Can you think of a good way to do it the Rails REST-ful way? Please go to &lt;a href=&quot;http://www.stephenchu.com/2007/11/seeing-rails-resources-clearly.html&quot;&gt;Seeing Rails Resources Clearly&lt;/a&gt; to share some of your thoughts.</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/613937363191220353/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/613937363191220353' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/613937363191220353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/613937363191220353'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/05/normal-rails-un-restful-page-as.html' title='A normal Rails un-RESTful page as a resource... or can it?'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></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=&quot;http://www.flickr.com/photos/nealford/291110547/&quot;&gt;Neal&#39;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=&quot;http://www.stephenchu.com/2007/12/why-create-database-migrations-when-you.html&quot;&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&#39;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&#39;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&#39;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=&quot;http://www.stephenchu.com/2006/03/pragmatic-nant-scripting.html&quot;&gt;this blog post&lt;/a&gt;.&lt;/em&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/4713401589862405098/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/4713401589862405098' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/4713401589862405098'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/4713401589862405098'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/04/pragmatic-rake-tasks-organization.html' title='Pragmatic Rake tasks organization'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></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&#39;t, if you know how to take advantage of it. Knowing what you know about Rails params, let&#39;s take a look at today&#39;s topic: the &lt;tt&gt;update&lt;/tt&gt; action.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;form_for&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@subscription&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@subscriptions&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;    &amp;lt;p&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;fields_for&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@subscription&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;ff&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;      &amp;lt;%= ff.collection_select :magazine_id, Magazine.find(:all), :id, :name, {}, :index =&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@subscription&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;    &amp;lt;% end %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&gt;p&amp;gt;&lt;br /&gt;  &amp;lt;% end %&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%= f.submit &#39;Save&#39; %&amp;gt;&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;span class=&quot;normal&quot;&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;subscription_4_magazine_id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;subscription[4][magazine_id]&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;101&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;PC&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Magazine&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&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=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;    &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;103&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;WIRED&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&gt;option&amp;gt;&lt;br /&gt;  &amp;lt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&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=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;    &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;102&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;IT&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Pro&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&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=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&gt;select&amp;gt;&lt;br /&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;  &lt;br /&gt;&lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;more&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;    &lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&#39;scrolling_div&#39;&gt;&lt;pre&gt;Processing ReadersController#update (for 127.0.0.1 at 2008-01-14 21:12:56) [PUT]&lt;br /&gt;Parameters: { &quot;commit&quot;             =&gt; &quot;Update&quot;,&lt;br /&gt;              &quot;reader&quot;             =&gt; { &quot;name&quot; =&gt; &quot;stephen chu&quot; },&lt;br /&gt;              &quot;subscriptions&quot;      =&gt; { &quot;4&quot; =&gt; { &quot;magazine_id&quot; =&gt; &quot;101&quot; },&lt;br /&gt;                                        &quot;6&quot; =&gt; { &quot;magazine_id&quot; =&gt; &quot;102&quot; }, &lt;br /&gt;                                        &quot;7&quot; =&gt; { &quot;magazine_id&quot; =&gt; &quot;103&quot; } },&lt;br /&gt;              &quot;authenticity_token&quot; =&gt; &quot;238ba79b8282882ba01d840352616c2cc79280f0&quot;,&lt;br /&gt;              &quot;action&quot;             =&gt; &quot;create&quot;,&lt;br /&gt;              &quot;controller&quot;         =&gt; &quot;readers&quot; }&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=&quot;http://www.stephenchu.com/2008/03/paramsfu-3-using-fieldsfor-and-index.html&quot;&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;update&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;subscriptions&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:subscription&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:subscription&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;values&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;save&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;flash&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:notice&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Reader was successfully updated.&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;flash&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:notice&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Failed.&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;collect&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;update_attributes&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;object&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:subscriptions&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;keys&lt;/span&gt;    &lt;span class=&quot;comment&quot;&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=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:subscriptions&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;values&lt;/span&gt;  &lt;span class=&quot;comment&quot;&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 &quot;Skinny Controller, Fat Model&quot; 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=&quot;http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html&quot;&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!</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/6529429523836691861/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/6529429523836691861' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/6529429523836691861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/6529429523836691861'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/03/paramsfu-5-update-multiple-models-in.html' title='params[:fu] #5 ) Update multiple models in &lt;tt&gt;update&lt;/tt&gt; action atomically.'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></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 &amp;lt;association_name&amp;gt;_ids=([...array of ids]) association proxy method.</title><content type='html'>In the &lt;a href=&quot;http://www.stephenchu.com/2008/03/paramsfu-3-using-fieldsfor-and-index.html&quot;&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&#39;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&#39;s check it out:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Reader&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;has_and_belongs_to_many&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:blogs&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;form_for&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; f.collection_select :blog_ids, Blog.find(:all), :id, :name, {}, :multiple &lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%= f.submit &#39;Create&#39; %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;normal&quot;&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;reader_blog_ids&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;multiple&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;multiple&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;reader[blog_ids][]&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Martin&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Fowler&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&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=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&gt;select&amp;gt; &lt;span class=&quot;normal&quot;&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=&#39;scrolling_div&#39;&gt;&lt;pre&gt;Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]&lt;br /&gt;Parameters: { &quot;commit&quot;             =&gt; &quot;Create&quot;,&lt;br /&gt;              &quot;reader&quot;             =&gt; { &quot;name&quot; =&gt; &quot;stephen chu&quot;&lt;br /&gt;                                        &quot;blog_ids&quot; =&gt; [&quot;1&quot;, &quot;2&quot;] },&lt;br /&gt;              &quot;authenticity_token&quot; =&gt; &quot;238ba79b8282882ba01d840352616c2cc79280f0&quot;,&lt;br /&gt;              &quot;action&quot;             =&gt; &quot;create&quot;,&lt;br /&gt;              &quot;controller&quot;         =&gt; &quot;readers&quot; }&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;blog_ids&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:blog_ids&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;blogs&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Blog&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&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!</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/3770960745050475939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/3770960745050475939' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/3770960745050475939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/3770960745050475939'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/03/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.'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></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&#39;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&#39;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&#39;s the code:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Reader&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:subscriptions&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:magazines&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:through&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:subscriptions&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;validates_associated&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:subscriptions&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;form_for&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;    &amp;lt;% fields_for :subscriptions do |ff| %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; ff.collection_select :magazine_id, Magazine.find(:all), :id, :name, {}, :index &lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;lt;/p&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;  &amp;lt;% end %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; f.submit &#39;Create&#39; %&amp;gt;&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;span class=&quot;normal&quot;&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;subscriptions_1_magazine_id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;subscriptions[1][magazine_id]&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;101&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;PC&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Magazine&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&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=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;103&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;WIRED&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&gt;option&amp;gt;&lt;br /&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;subscriptions_2_magazine_id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;subscriptions[2][magazine_id]&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;101&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;PC&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Magazine&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&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=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;103&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;WIRED&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&gt;option&amp;gt;&lt;br /&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;punct&quot;&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=&#39;scrolling_div&#39;&gt;&lt;pre&gt;Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]&lt;br /&gt;Parameters: { &quot;commit&quot;             =&gt; &quot;Create&quot;,&lt;br /&gt;              &quot;reader&quot;             =&gt; { &quot;name&quot; =&gt; &quot;stephen chu&quot; },&lt;br /&gt;              &quot;subscriptions&quot;      =&gt; { &quot;1&quot; =&gt; { &quot;magazine_id&quot; =&gt; &quot;101&quot; },&lt;br /&gt;                                        &quot;2&quot; =&gt; { &quot;magazine_id&quot; =&gt; &quot;102&quot; }, &lt;br /&gt;                                        &quot;3&quot; =&gt; { &quot;magazine_id&quot; =&gt; &quot;103&quot; } },&lt;br /&gt;              &quot;authenticity_token&quot; =&gt; &quot;238ba79b8282882ba01d840352616c2cc79280f0&quot;,&lt;br /&gt;              &quot;action&quot;             =&gt; &quot;create&quot;,&lt;br /&gt;              &quot;controller&quot;         =&gt; &quot;readers&quot; }&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@subscriptions&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;subscriptions&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;build&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:subscriptions&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;values&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;save&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;flash&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:success&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Good.&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;flash&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Bad.&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;{})&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;collect&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;build_record&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;set_belongs_to_association_for&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:subscriptions&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;values&lt;/span&gt;  &lt;span class=&quot;comment&quot;&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=&quot;http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html&quot;&gt;(back to the TOC of the params[:fu] series)&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/6542929948815344833/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/6542929948815344833' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/6542929948815344833'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/6542929948815344833'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/03/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.'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;form_for&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;fields_for&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:favourite_computer&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@computer&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;ff&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;    &amp;lt;%= ff.text_field :name %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;  &amp;lt;%= f.submit &#39;Create&#39; %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;normal&quot;&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=&quot;scrolling_div&quot;&gt;&lt;pre&gt;Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]&lt;br /&gt;Parameters: { &quot;commit&quot;             =&gt; &quot;Create&quot;,&lt;br /&gt;              &quot;reader&quot;             =&gt; { &quot;name&quot; =&gt; &quot;stephen chu&quot; },&lt;br /&gt;              &quot;favourite_computer&quot; =&gt; { &quot;name&quot; =&gt; &quot;macbook pro&quot; },&lt;br /&gt;              &quot;authenticity_token&quot; =&gt; &quot;238ba79b8282882ba01d840352616c2cc79280f0&quot;,&lt;br /&gt;              &quot;action&quot;             =&gt; &quot;create&quot;,&lt;br /&gt;              &quot;controller&quot;         =&gt; &quot;readers&quot; }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@computer&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;build_computer&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:favourite_computer&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;save&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;flash&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:success&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Good.&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;flash&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Bad.&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Reader&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;has_one&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:computer&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;validates_associated&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:computer&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&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=&quot;http://www.stephenchu.com/2008/03/introduction-to-rails-composedof.html&quot;&gt;blog entry&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html&quot;&gt;(back to the TOC of the params[:fu] series)&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/5909803697093839433/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/5909803697093839433' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/5909803697093839433'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/5909803697093839433'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/03/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.'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;form_for&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; f.text_field :birth_place %&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;submit&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;&amp;lt;% end %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&#39;scrolling_div&#39;&gt;&lt;pre&gt;Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]&lt;br /&gt;Parameters: { &quot;commit&quot;             =&gt; &quot;Create&quot;,&lt;br /&gt;              &quot;reader&quot;             =&gt; { &quot;name&quot; =&gt; &quot;stephen chu&quot;, &quot;birth_place&quot; =&gt; &quot;hong kong&quot; },&lt;br /&gt;              &quot;authenticity_token&quot; =&gt; &quot;238ba79b8282882ba01d840352616c2cc79280f0&quot;,&lt;br /&gt;              &quot;action&quot;             =&gt; &quot;create&quot;,&lt;br /&gt;              &quot;controller&quot;         =&gt; &quot;readers&quot;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&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&#39;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=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;comment&quot;&gt;# =&amp;gt; { &#39;first_name&#39; =&amp;gt; &#39;stephen&#39;, &#39;last_name&#39; =&amp;gt; &#39;chu&#39; }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;attribute&quot;&gt;@readers&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:conditions&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:search&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;])&lt;/span&gt;  &lt;span class=&quot;comment&quot;&gt;# =&amp;gt; { &#39;name&#39; =&amp;gt; &#39;chu&#39; }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;comment&quot;&gt;# =&amp;gt; { &#39;first_name&#39; =&amp;gt; &#39;martin&#39;, &#39;last_name&#39; =&amp;gt; &#39;fowler&#39; }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;attribute&quot;&gt;@reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;update_attributes&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:reader&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;])&lt;/span&gt;  &lt;span class=&quot;comment&quot;&gt;# =&amp;gt; { &#39;birth_place&#39; =&amp;gt; &#39;hong kong&#39; }&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=&quot;http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html&quot;&gt;(back to the TOC of the params[:fu] series)&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/4750190244110730864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/4750190244110730864' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/4750190244110730864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/4750190244110730864'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/03/paramfu-1-wrap-all-relevant-attributes.html' title='params[:fu] #1 ) Wrap all relevant attributes in a single params key.'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-7908492366812334297</id><published>2008-03-23T22:46:00.010-05:00</published><updated>2008-05-21T21:17:44.202-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails"/><title type='text'>Boost your Rails Controller params[:fu]</title><content type='html'>In many ways, in a Rails controller POST action, what gets POST-ed in &lt;tt&gt;params&lt;/tt&gt; are often overlooked. In many straight-forward tutorials, we slap a &lt;tt&gt;forms_for @customer&lt;/tt&gt; in our view, and then the &lt;tt&gt;params&lt;/tt&gt; will come out to be directly consumable by our ActiveRecord model. Unfortunately, many new Rails developers cannot see beyond the usefulness of hashing the posted parameters accordingly. In the next few blog posts, I intend to show you that if you take the time to assemble your &lt;tt&gt;params&lt;/tt&gt; the way Rails likes it, you can dramatically reduce the amount of code you have to write in your controller actions and models, simply because Rails takes care of it for you. The end result is getting it done in less code, fewer tests, less trouble.&lt;br /&gt;&lt;br /&gt;Here it goes, the params[:fu] series:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;params[:fu] # =&gt; &quot;1&quot; &lt;a href=&quot;http://www.stephenchu.com/2008/03/paramfu-1-wrap-all-relevant-attributes.html&quot;&gt;Wrap all relevant attributes in a single &lt;tt&gt;params&lt;/tt&gt; key.&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;params[:fu] # =&gt; &quot;2&quot; &lt;a href=&quot;http://www.stephenchu.com/2008/03/paramsfu-2-put-attributes-into.html&quot;&gt;Put attributes into a different params key using &lt;tt&gt;fields_for&lt;/tt&gt; if they belong to different models.&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;params[:fu] # =&gt; &quot;3&quot; &lt;a href=&quot;http://www.stephenchu.com/2008/03/paramsfu-3-using-fieldsfor-and-index.html&quot;&gt;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.&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;params[:fu] # =&gt; &quot;4&quot; &lt;a href=&quot;http://www.stephenchu.com/2008/03/paramsfu-4-use-magical-of-ids.html&quot;&gt;Use the magical &lt;tt&gt;&amp;lt;association_name&amp;gt;_ids=([...array of ids])&lt;/tt&gt; association proxy method.&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;params[:fu] # =&gt; &quot;5&quot; &lt;a href=&quot;http://www.stephenchu.com/2008/03/paramsfu-5-update-multiple-models-in.html&quot;&gt;Use ids to update multiple models in update action atomically.&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;update:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;These tips have been chosen as the 8th runner-up in the &lt;a href=&quot;http://railscasts.com/contest&quot;&gt;Railscasts 100th episode contest&lt;/a&gt;. And for that, let me add another &lt;tt&gt;parmas[:fu]&lt;/tt&gt; bonus to this list to all readers:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;params[:fu] # =&gt; &quot;bonus&quot; &lt;a href=&quot;http://www.stephenchu.com/2007/12/passing-multiple-parameters-in.html&quot;&gt;Passing multiple params in an observe_field, now that you know your &lt;tt&gt;parmas[:fu]&lt;/tt&gt;.&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Enjoy!</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/7908492366812334297/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/7908492366812334297' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/7908492366812334297'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/7908492366812334297'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html' title='Boost your Rails Controller params[:fu]'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-8771421414988107389</id><published>2008-03-12T21:14:00.010-05:00</published><updated>2008-05-21T21:22:00.652-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails"/><title type='text'>Rails composed_of &amp;amp;block conversion</title><content type='html'>If you had read Domain Driven Design, you will remember the author, Eric Evans, talks about various types of model objects in your domain model. Two of which are &lt;em&gt;Entity Objects&lt;/em&gt; and &lt;em&gt;Value Objects&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;Entity objects are those that we care about their identity. For example, on Amazon.com, two users obviously have different identities, despite the possibility that they have the same name. Value objects, on the other hand, are those that we only care about their internal states&#39; equality. Two money objects each has amount 100 and currency &#39;US&#39; are treated as equal in the entire application. These two types of objects are similar in that they can all be defined as a class, but their usage is quite different.&lt;br /&gt;&lt;br /&gt;Usually, a big bulk of your domain objects will be Entity objects. Almost all of your ActiveRecord::Base subclasses belong to that category, since each object has a different &#39;id&#39; to uniquely identify themselves.&lt;br /&gt;&lt;br /&gt;Rails provides &lt;tt&gt;composed_of&lt;/tt&gt; as a way for your Value Objects to be referenced by an ActiveRecord Entity object. Rails 2.0 even added some syntactic sugar to &lt;tt&gt;composed_of&lt;/tt&gt;, allowing you to pass a block for value object conversion. For Rails 1.x users, you can achieve the same with the plugin called &lt;tt&gt;composed_of_conversion&lt;/tt&gt;. Let&#39;s see how to beautify your Rails code.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;composed_of&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:balance&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:class_name&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:mapping&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;[%w(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;balance amount&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%w(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;currency currency&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;constant&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:balance&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:currency&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Money&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:amount&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:currency&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;currency&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@amount&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@currency&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;currency&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;AccountsController&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;create&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@account&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Account&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:account&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@account&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;save&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;ident&quot;&gt;flash&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:notice&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;New account created...&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;ident&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@author&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;ident&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:action&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Notice how my &lt;tt&gt;@account&lt;/tt&gt; creation does not require any additional Controller code for value object creation and assignment, keeping my controller away from snacks and candy (i.e. thin). But since I have redefined &lt;tt&gt;Account#balance&lt;/tt&gt; and &lt;tt&gt;Account#balance=&lt;/tt&gt; through the use of &lt;tt&gt;composed_of&lt;/tt&gt;, what should my &lt;tt&gt;form_for&lt;/tt&gt; fields in my view look like? The answer lies with a use of &lt;tt&gt;fields_for&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;form_for&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;attribute&quot;&gt;@account&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;% f.fields_for :balance, @account.balance do |ff| %&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;constant&quot;&gt;Balance&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; ff.text_field :balance %&amp;gt;&lt;br /&gt;  &amp;lt;/p&amp;gt;  &lt;br /&gt;  &amp;lt;p&amp;gt;&lt;br /&gt;    Currency: &amp;lt;%&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;ff&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;text_field&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:currency&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;  &amp;lt;/p&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;p&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; f.submit &amp;quot;Create&amp;quot; %&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;  &lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;span class=&quot;normal&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;POST Parameters:&lt;br /&gt;{ &lt;br /&gt;  &quot;commit&quot; =&gt; &quot;Create&quot;,&lt;br /&gt;  &quot;account&quot;   =&gt; { &quot;balance&quot; =&gt; { &quot;balance&quot;=&gt;&quot;400&quot;, &quot;currency&quot;=&gt;&quot;us&quot; } }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;By calling &lt;tt&gt;f.fields_for&lt;/tt&gt; on the form builder (and not just &lt;tt&gt;fields_for&lt;/tt&gt;), you are creating a new hash scope &#39;balance&#39; under &lt;tt&gt;params[:account]&lt;/tt&gt;. Since your &lt;tt&gt;composed_of&lt;/tt&gt; is called &lt;tt&gt;balance&lt;/tt&gt;, only the sub-hash &#39;balance&#39; will be used in your &lt;tt&gt;composed_of&lt;/tt&gt; block conversion. Then, your model will use the sub-hash to convert it into the &lt;tt&gt;Money&lt;/tt&gt; object you want.&lt;br /&gt;&lt;br /&gt;At this point, your &lt;tt&gt;Money&lt;/tt&gt; value object need not be confined with a constructor that takes parameters by ordinal position anymore. You can have it take a hash just like any ActiveRecord::Base classes, so that any future addition of parameters to &lt;tt&gt;Money&lt;/tt&gt; does not require changes to your conversion block. Just drop an html element using your &lt;tt&gt;fields_for&lt;/tt&gt; form builder and it will just work.&lt;br /&gt;&lt;br /&gt;Why do this? Now, you have a different class to handle money related operations, such as addition, subtraction, validations, money comparison, and even &lt;a href=&#39;http://www.stephenchu.com/2008/01/format-your-data-using-rails-composedof.html&#39;&gt;formatting your money amounts everywhere&lt;/a&gt;. Should you have another model that needs money, you know where to share code from.&lt;br /&gt;&lt;br /&gt;Also, do not get into thinking that a value object must abstract at least 2 columns on a table. It doesn&#39;t have to be. A value object can abstract just one column but contains &lt;a href=&quot;http://www.stephenchu.com/2008/05/rails-composedof-validation.html&quot;&gt;its own validation&lt;/a&gt; and formatting logic and just be fine.&lt;br /&gt;&lt;br /&gt;Value objects are very important in any application, even though a lot of times they are simple. Make good use of them and your code quality will improve. Now it&#39;s time to roll up your sleeves and clean up those value object creation code inside your controllers!</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/8771421414988107389/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/8771421414988107389' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/8771421414988107389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/8771421414988107389'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/03/introduction-to-rails-composedof.html' title='Rails composed_of &amp;amp;block conversion'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-3277177935837487678</id><published>2008-03-10T23:03:00.001-05:00</published><updated>2008-03-10T23:05:48.482-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails"/><title type='text'>A little #to_s can do wonder in DRY-ing up your views</title><content type='html'>Do you do this every day?&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Show&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Blog&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; @blog.name %&amp;gt;&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;lt;dl&amp;gt;&lt;br /&gt;  &amp;lt;dt&amp;gt;Author:&amp;lt;/dt&amp;gt;&lt;br /&gt;  &amp;lt;dd&amp;gt;&amp;lt;%&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@blog&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;full_name&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;lt;/dd&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&gt;dl&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;ul&amp;gt;&lt;br /&gt;  &amp;lt;% for comment in @blog.comments do %&amp;gt;&lt;br /&gt;    &amp;lt;li&amp;gt;Posted by &amp;lt;%= comment.created_by.full_name %&amp;gt;: &amp;lt;%= comment.description %&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;li&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;&amp;lt;/ul&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I am lazy. Having to spell out in my views what field to render for every object when it is that field 90% of the time in my application gives me too much typing. When I render a &lt;tt&gt;@comment&lt;/tt&gt;, you bet I want its description field most of the time; similarly, when I render a &lt;tt&gt;@user&lt;/tt&gt;, you bet I want his/her full_name.&lt;br /&gt;&lt;br /&gt;Don&#39;t forget all these erb tags at the end of day only render #to_s anyways, so you can just define your default field there to get rid of some typing as well:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Blog&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;to_s&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;name&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;to_s&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;first_name&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;last_name&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Comment&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;to_s&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;description&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now, let&#39;s see how our view looks, 90% of the time:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Show&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Blog&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; @blog %&amp;gt;&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;lt;dl&amp;gt;&lt;br /&gt;  &amp;lt;dt&amp;gt;Author:&amp;lt;/dt&amp;gt;&lt;br /&gt;  &amp;lt;dd&amp;gt;&amp;lt;%&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@blog&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;lt;/dd&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&gt;dl&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;ul&amp;gt;&lt;br /&gt;  &amp;lt;% for comment in @blog.comments do %&amp;gt;&lt;br /&gt;    &amp;lt;li&amp;gt;Posted by &amp;lt;%= comment.created_by %&amp;gt;: &amp;lt;%= comment %&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;li&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;&amp;lt;/ul&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;See, it&#39;s a lot DRY-er and less verbose.&lt;br /&gt;&lt;br /&gt;You will also notice that by doing this I eliminated a need to use a possible &lt;a href=&quot;http://ozmm.org/posts/try.html&quot;&gt;&lt;tt&gt;Object#try(:method)&lt;/tt&gt;&lt;/a&gt; case:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;dd&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; @blog.author.try(:full_name) %&amp;gt;&amp;lt;/dd&amp;gt;&lt;span class=&quot;normal&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;is no longer necessary, because a nil &lt;tt&gt;@blog.author&lt;/tt&gt; will automatically give an empty string on the view. The end result is we all get to be lazier every day :-)</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/3277177935837487678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/3277177935837487678' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/3277177935837487678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/3277177935837487678'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/03/little-tos-can-do-wonder-in-dry-ing-up.html' title='A little #to_s can do wonder in DRY-ing up your views'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-4685555223168998358</id><published>2008-01-11T03:58:00.000-06:00</published><updated>2008-01-11T18:53:45.407-06: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'>Format your data using Rails composed_of Value Objects</title><content type='html'>A requirement comes in as usual says, &quot;I want prices to be formatted like &#39;$2,000.00&#39; on all rhtml&#39;s.&quot;&lt;br /&gt;&lt;br /&gt;Your programming brain then associates the word &quot;formatted&quot; with &quot;presentation&quot;. Afterall, the price column is simply stored in a decimal column in the database. And off you go, wrapping &lt;tt&gt;number_to_currency(@book.price)&lt;/tt&gt; on all of your views, just like any tutorial says.&lt;br /&gt;&lt;br /&gt;It&#39;s all good until the same formatting is required for all emails being sent, all web services requests being issued, all Excel/CSV reports being generated, all everything. Since you took advantage of formatting the prices with an ActionView helper method &lt;tt&gt;number_to_currency&lt;/tt&gt;, you cannot do the same in places where ActionView helper methods are not available to you. You start creating wrapper classes around your &lt;tt&gt;Book&lt;/tt&gt; class, maybe a presenter, to keep this &quot;presentation&quot; logic outside of your pristine business domain model.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;BookPresenter&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;SimpleDelegator&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActionView&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Helpers&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;NumberHelper&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;price&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;nummber_to_currency&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;expr&quot;&gt;#{__getobj__.price}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;isbn&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;BooksController&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;show&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@book&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;BookPresenter&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;])&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;constant&quot;&gt;ISBN&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; @book.isbn %&amp;gt;&lt;br /&gt;  Price: &amp;lt;%&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;&amp;lt;/p&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;To present formatted data is extremely common on any applications, be it a price, date/time, name, address, SSN, ISBN, or even a baseball game score. A lot of these information are so simple that they can be stored in a single database column and be represented by a primitive type like &lt;tt&gt;String&lt;/tt&gt;, &lt;tt&gt;DateTime&lt;/tt&gt;, or &lt;tt&gt;Fixnum&lt;/tt&gt; in Ruby. Since they are so simple in nature, programmers think that all it takes to format them is wrap a helper method around it on the view. But this approach may backfire like the example above.&lt;br /&gt;&lt;br /&gt;Alternatively, one can create a &lt;em&gt;value object&lt;/em&gt; that has a &lt;tt&gt;#to_s&lt;/tt&gt; method that does all the formatting for you. That way, everywhere that needs formatting can ask a book object for it, and not have to wrap a presenter around it for something as formatting.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;composed_of&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:isbn&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:class_name&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;ISBN&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;composed_of&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:price&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:class_name&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;ISBN&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;to_s&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Money&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActionView&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Helpers&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;NumberHelper&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;to_s&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;nummber_to_currency&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@price&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;BooksController&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;show&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;attribute&quot;&gt;@book&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;constant&quot;&gt;ISBN&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; @book.isbn %&amp;gt;&lt;br /&gt;  Price: &amp;lt;%&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;&amp;lt;/p&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So what&#39;s the difference? The primarily difference is, every &lt;tt&gt;Book&lt;/tt&gt; object now knows how to format its information correctly, and now when a book needs to be used by various sources, be it on an rhtml page, a web service request, or an Excel/CSV report, those sources can interrogate a book object, but not &quot;first fetch a book object, then wrap it with a presenter&quot;.&lt;br /&gt;&lt;br /&gt;Some people will yell now, &quot;but aren&#39;t you putting presentation logic into your domain model?&quot; Yes, kinda. One of the toughest decisions that a developer always has to make is to distinguish between domain logic and non-domain logic. Now if I may ask, if my formatting is happening in multiple places, is this domain logic? I tend to say it is. Also, from experience, while it is extremely rare for me to put such presentation/formatting logic in an &lt;em&gt;Entity Object&lt;/em&gt; domain model class (e.g. Book) because such classes are almost always complicated enough in just domain behaviors, having formatting logic in these usually less complicated &lt;em&gt;Value Objects&lt;/em&gt; (e.g. Money) makes a lot of sense to me. And in Rails, &lt;tt&gt;composed_of&lt;/tt&gt; provides me that convenience to me almost effortlessly.&lt;br /&gt;&lt;br /&gt;The way Rails has these handy &lt;tt&gt;ActionView::Helpers&lt;/tt&gt; is super useful, but every once a while we must step back and ask ourselves the question, &quot;how can I use them beyond the tutorial way on my really complicated application?&quot;&lt;br /&gt;&lt;br /&gt;Tip #1: You can also have your &lt;tt&gt;#to_s&lt;/tt&gt; methods to take an additional parameter to customize the formatting behavior. Take a look at how &lt;tt&gt;DATE_FORMATS&lt;/tt&gt; are used in Time#to_s.&lt;br /&gt;&lt;br /&gt;Tip #2: You may not need a new class for the sake of using &lt;tt&gt;composed_of&lt;/tt&gt; every time you have a new formatting requirement either. Sometimes you may be able to get away with just extending the Ruby primitive types with a custom method/format, for example &lt;tt&gt;Price: &amp;lt;%= @book.price.to_s :money %&amp;gt;&lt;/tt&gt;.</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/4685555223168998358/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/4685555223168998358' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/4685555223168998358'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/4685555223168998358'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2008/01/format-your-data-using-rails-composedof.html' title='Format your data using Rails composed_of Value Objects'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-335689354293385992</id><published>2007-12-26T00:01:00.001-06:00</published><updated>2007-12-26T00:34:22.506-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="efficiency"/><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails"/><title type='text'>Go to mysql command shell using rake</title><content type='html'>Tired of typing &lt;tt&gt;mysql -u foo -pbar -h db01.firewalled.com baz_development&lt;/tt&gt; yet? Add to that when you have database environments galore like &lt;tt&gt;test&lt;/tt&gt;, &lt;tt&gt;QA&lt;/tt&gt;, &lt;tt&gt;selenium&lt;/tt&gt;, &lt;tt&gt;staging&lt;/tt&gt;, etc.&lt;br /&gt;&lt;br /&gt;Here&#39;s a rake task for you. Just point it to a Rails environment and execute and you are in the corresponding mysql command prompt.&lt;br /&gt;&lt;br /&gt;e.g. &lt;br /&gt;&lt;br /&gt;&lt;tt&gt;$ rake mysql RAILS_ENV=qa&lt;/tt&gt;&lt;br /&gt;&lt;pre class=&#39;ruby&#39;&gt;Loading mysql in RAILS_ENV=qa...&lt;br /&gt;    Executing: mysql -u foo -pbar -h localhost baz_qa_test&lt;br /&gt;&lt;br /&gt;Reading table information for completion of table and column names&lt;br /&gt;You can turn off this feature to get a quicker startup with -A&lt;br /&gt;&lt;br /&gt;Welcome to the MySQL monitor.  Commands end with ; or \g.&lt;br /&gt;Your MySQL connection id is 1&lt;br /&gt;Server version: 5.0.51 Source distribution&lt;br /&gt;&lt;br /&gt;Type &#39;help;&#39; or &#39;\h&#39; for help. Type &#39;\c&#39; to clear the buffer.&lt;br /&gt;&lt;br /&gt;mysql&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Code:&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;&lt;span class=&quot;ident&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Loads mysql command prompt&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;ident&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:mysql&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;escape&quot;&gt;\n\n&lt;/span&gt;Loading mysql in RAILS_ENV=&lt;span class=&quot;expr&quot;&gt;#{RAILS_ENV}&lt;/span&gt;...&lt;span class=&quot;escape&quot;&gt;\n&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;database_yml_path&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;expr&quot;&gt;#{RAILS_ROOT}&lt;/span&gt;/config/database.yml&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;database_yml&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;YAML&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;ERB&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;database_yml_path&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Could not find environment &lt;span class=&quot;expr&quot;&gt;#{RAILS_ENV}&lt;/span&gt; in database.yml&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;database_yml&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;database_yml&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;database_name&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;localhost&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;3306&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;socket&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;/tmp/mysql.sock&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Failed. Setup requires a user and database for environment &lt;span class=&quot;expr&quot;&gt;#{environment}&lt;/span&gt; in &#39;&lt;span class=&quot;expr&quot;&gt;#{database_yml_path}&lt;/span&gt;&#39;.&lt;span class=&quot;escape&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;expr&quot;&gt;#{database_yml.to_yaml}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;database_name&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;-u &lt;span class=&quot;expr&quot;&gt;#{username}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; -p&lt;span class=&quot;expr&quot;&gt;#{password}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;empty?&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; -h &lt;span class=&quot;expr&quot;&gt;#{host}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;empty?&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; &lt;span class=&quot;expr&quot;&gt;#{database_name}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;[]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;mysql &lt;span class=&quot;expr&quot;&gt;#{args}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;EOS&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;br /&gt;    Executing: &lt;span class=&quot;expr&quot;&gt;#{command.join(&#39; &#39;)}&lt;/span&gt;&lt;span class=&quot;escape&quot;&gt;\n&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;&lt;br /&gt;  EOS&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;system&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/335689354293385992/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/335689354293385992' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/335689354293385992'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/335689354293385992'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/12/go-to-mysql-command-shell-using-rake.html' title='Go to mysql command shell using rake'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-6241821505112011651</id><published>2007-12-17T02:04:00.000-06:00</published><updated>2007-12-17T02:22:39.989-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="efficiency"/><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails"/><title type='text'>Rid of those TextMate &quot;ctags timed out!&quot; error</title><content type='html'>If you use TextMate for your Ruby on Rails development, you must use the ctags bundle &lt;a href=&quot;http://gerd.knops.org/?p=7&quot;&gt;CTags.tmbundle&lt;/a&gt;. It gives you a keyboard shortcut CTRL-] to jump to files that defines the class/method your cursor is on. Similar to &quot;go to file and line&quot; in some IDE. Super handy.&lt;br /&gt;&lt;br /&gt;Trouble is when your project gets large, it gets longer and longer to update the &lt;tt&gt;tmtags&lt;/tt&gt; index file to a point where it exceeds the default 15 seconds timout and gives a &quot;ctags timed out!&quot; error.&lt;br /&gt;&lt;br /&gt;What you can do is edit the file &lt;tt&gt;~/Library/Application\ Support/TextMate/Pristine\ Copy/Bundles/CTags.tmbundle/Support/bin/tmctags&lt;/tt&gt; and increase the timeout &lt;tt&gt;$CtagsTimeout&lt;/tt&gt; value to something more reasonable. Problem solved.&lt;br /&gt;&lt;br /&gt;p.s. CTags suffers from another shortcoming, and that is it cannot index ruby methods named ending with a ? or !. Anyone knows of a fix/workaround? Please comment if you do!</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/6241821505112011651/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/6241821505112011651' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/6241821505112011651'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/6241821505112011651'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/12/rid-of-those-textmate-ctags-timed-out.html' title='Rid of those TextMate &quot;ctags timed out!&quot; error'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-744880096818773440</id><published>2007-12-14T00:15:00.000-06:00</published><updated>2007-12-14T02:28:21.615-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="agile"/><category scheme="http://www.blogger.com/atom/ns#" term="efficiency"/><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails"/><title type='text'>Why create database migrations, when you don&#39;t need to?</title><content type='html'>Rails out of the box supports database migration. It allows Rails programmers to be more Agile, do less database &lt;a href=&quot;http://en.wikipedia.org/wiki/Big_Design_Up_Front&quot;&gt;BDUF&lt;/a&gt; and instead change the database schema as the requirements change and the business wants it. Writing a migration is also as easy as running a command line &lt;tt&gt;script/generate migration&lt;/tt&gt; and then executing it with &lt;tt&gt;rake db:migrate&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;Database migration is useful for two reasons. Number one is to allow developers to change the database schema. This can be as simple as a single &lt;tt&gt;ALTER TABLE&lt;/tt&gt; statement to add a new column. Number two, more importantly, is to &lt;a href=&quot;http://www.stephenchu.com/2007/03/rails-migration-pitfalls.html&quot;&gt;migrate the all-important data exist in the database&lt;/a&gt;. But if your application is still under development for initial release, then migration may not be buying you too much good, because chances are no one cares how your table structure got to where it is now, and you may not have a whole lot of data.&lt;br /&gt;&lt;br /&gt;We all use migrations feverishly probably because most of the Rails books/references/tutorials begins with: &lt;em&gt;&quot;Let&#39;s start by creating a database migration, in it we insert some data into the newly created tables, and learn how to do XYZ.&quot;&lt;/em&gt; Thus, your &lt;tt&gt;db/migrate&lt;/tt&gt; folder is stuffed with migrations that does everything: create new tables; alter existing tables; create data for those tables; update data created from previous migrations; and perhaps all of the above, while without justifiable reasons on &lt;em&gt;what&lt;/em&gt; those migrations are trying to migrate. In practice, it is quite time-consuming for a developer to run 200+ migrations every time he blows away a database, which is not uncommon. Not only that, sometimes you have multiple migrations that basically cancel out each other&#39;s changes as our customers change their minds back and forth. As a result, you could be creating migrations left and right when there may not be any real beneficiaries: a database loaded with data.&lt;br /&gt;&lt;br /&gt;Sometimes your QA team may have their own data set that tests your app, and thus their loaded database is a beneficiary. While that is true, I prefer their data to be scripted and be freshly generated by ActiveRecord models (using create!) every time instead of keep migrating them, because as my application domain model expands, I want not just QA data but all datasets to be cleansed and validated by my model validations. There is no guarentee that after running a &lt;tt&gt;drop_column&lt;/tt&gt; migration the QA dataset does not violate any business logic. Keeping all these data valid while database migrations are being rapidly created is very hard.&lt;br /&gt;&lt;br /&gt;To take advantage of the fact that your development environment has nothing to lose until you have an initial release, while maximizing the benefits of keeping all your data valid (for all enviroments) the whole time while your app is under development, it&#39;s a simple steps 1-2-3:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step 1:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Have one migration file, &lt;tt&gt;001_release_one_schema.rb&lt;/tt&gt;, that captures all database object creations. For example, all your &lt;tt&gt;create_table&lt;/tt&gt;, &lt;tt&gt;create_index&lt;/tt&gt;, views, triggers (*yikes*), etc. After this migration, your database should contain all database objects for your Rails app but in a &quot;blank&quot;, data-less state.&lt;br /&gt;&lt;pre&gt;$ cat db/migrate/001_release_one_schema.rb&lt;/pre&gt;&lt;div class=&#39;ruby&#39;&gt;&lt;pre&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;ReleaseOneSchema&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Migration&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;self.up&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;ident&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;foos&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      (... and many others ...)&lt;br /&gt;    &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;self.down&lt;/span&gt;&lt;br /&gt;      (... ... ...)&lt;br /&gt;    &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;          &lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step 2:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Create a rake task to populate all &lt;strong&gt;reference data&lt;/strong&gt; that your application requires to run with. Reference data meaning all data that your application cannot change through its screens, but are essential for your app to run. For example, all currencies that are used to populate a drop-down on your app. A lot of drop-down lists data are reference data.&lt;br /&gt;&lt;pre&gt;$ cat lib/tasks/data.rake&lt;/pre&gt;&lt;div class=&#39;ruby&#39;&gt;&lt;pre&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:data&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Loads a default dataset of both reference and user data into database.&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:load&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:environment&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;br /&gt;                    &lt;span class=&quot;symbol&quot;&gt;:configuration&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;br /&gt;                    &lt;span class=&quot;symbol&quot;&gt;:reference_data&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;br /&gt;                    &lt;span class=&quot;symbol&quot;&gt;:user_data&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;private&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:configuration&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;constant&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;DATASET&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;slim&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&#39;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:reference_data&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;ident&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;expr&quot;&gt;#{RAILS_ROOT}&lt;/span&gt;/db/data/reference_data/&lt;span class=&quot;expr&quot;&gt;#{ENV[&#39;DATASET&#39;]}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;$ cat db/data/reference_data/slim.rb&lt;/pre&gt;&lt;div class=&#39;ruby&#39;&gt;&lt;pre&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@us_dollar&lt;/span&gt;  &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;US Dollar&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@yen&lt;/span&gt;        &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Yen&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;attribute&quot;&gt;@euro&lt;/span&gt;       &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;EURO&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step 3:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Create a rake task to populate all &lt;strong&gt;user data&lt;/strong&gt; that your app requires to run with. User data are data that a user can create/update within your application. They are also required for the Rails app to function properly the first day when it launches. For example, for your flashy Paypal application, the fees structure on how it charges its users. An application administrator is allowed to raise or drop fees in your app.&lt;br /&gt;    &lt;br /&gt;&lt;div class=&#39;ruby&#39;&gt;&lt;pre&gt;&lt;br /&gt;  &lt;span class=&quot;ident&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:data&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;private&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;ident&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:user_data&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;ident&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;expr&quot;&gt;#{RAILS_ROOT}&lt;/span&gt;/db/data/user_data/&lt;span class=&quot;expr&quot;&gt;#{ENV[&#39;DATASET&#39;]}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;$ cat db/data/user_data/slim.rb&lt;/pre&gt;&lt;div class=&#39;ruby&#39;&gt;&lt;pre&gt;&lt;br /&gt;  &lt;span class=&quot;constant&quot;&gt;Fee&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:amount&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1_000&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:currency&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@us_dollar&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;constant&quot;&gt;Fee&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:amount&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1_500&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:currency&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@yen&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;constant&quot;&gt;Fee&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:amount&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2_500&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:currency&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@euro&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;There are several benefits of managing your database schema and data this way:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It is easier and faster to re-populate your entire database to the latest schema from scratch with data, since there are no extraneous migrations.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Faster to locate and update data needed for application. They are always in your dataset generation scripts.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;No need to worry about outdated/removed ActiveRecord classes and declare them inside the migration file itself. They is no &quot;legacy&quot; ActiveRecord models.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;All data are valid all the time because they are created through Model.create! sanitized by your model validations.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Easy to specify datasets to load by preference for DEV (slim, loaded), BA (story sign-off), QA (scenario-based), or demo (full) environments. e.g. &lt;tt&gt;rake data:load DATASET=slim RAILS_ENV=qa&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;No worries about broken/incomplete migrations. Fewer code, fewer trouble.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Now, after your Rails app goes to a 1.0 production release, you should switch this back to the normal Rails database migration style. I suspect your application users won&#39;t be too happy if you blow away their data every time you roll out a minor update or a major release... (or not?)</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/744880096818773440/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/744880096818773440' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/744880096818773440'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/744880096818773440'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/12/why-create-database-migrations-when-you.html' title='Why create database migrations, when you don&#39;t need to?'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-1816987959902497846</id><published>2007-12-04T21:50:00.000-06:00</published><updated>2007-12-05T12:59:09.523-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".net"/><category scheme="http://www.blogger.com/atom/ns#" term="agile"/><category scheme="http://www.blogger.com/atom/ns#" term="pattern"/><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails"/><title type='text'>The last &quot;D&quot; in TDD means more than just &quot;Development&quot;</title><content type='html'>When asked &quot;Do you write tests?&quot;, a lot of developers these days will say &quot;of course&quot; as their answers. However, not everyone can admit to doing TDD (Test Driven Development) correctly. Test Driven Development says, a developer will write a test that fails first, then write code to make the test pass, and refactor when possible, and repeat. This is what most people&#39;s TDD rhythm is. For the most part this is fairly easy to do. But to reach the next level, one has to understand TDD as a tool: TDD means more than just test your own code. Here is a couple tips on what the last &quot;D&quot; means:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Discipline&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;It takes a great deal of discipline to even write a failing test before writing the actual code. Sometimes, we write a little seudo-code here, or move a method definition there, or changing code else where trying to see if more new code needs to be written after it, and sooner than you think you are writing the actual implementation of the methods you wanted to test (Test Afterwards Development anyone?). Other times you write the test, but you are too anxious to even run it and see it fails. And other times you want to jump into the actual code immediately when you see your new test fails, but failing for the unexpected reasons.&lt;br /&gt;&lt;br /&gt;Don&#39;t fall into these traps. If anything is true, &lt;i&gt;testing is hard&lt;/i&gt;, but it is at the same time rewarding and fun. What&#39;s also true is, &lt;i&gt;it will pay off&lt;/i&gt;. Write the failing test, draw up your list of tests you will need to write, and satisfy them one by one. Having discipline is the cornerstone of becoming a better programmer.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Design&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;It takes too long to write a test? Tests are running too slowly? Are your tests difficult to read? Are they too brittle and fail all the time? Hang in there! You ever had the feeling you saw code in the codebase that irks the living hell out of your mind written by someone else on your team? Well, it is time for you to get some of these feedback about your own code. Yay, your code sucks! Your tests &lt;a href=&quot;http://www.stephenchu.com/2007/01/use-your-test-classes-to-identify.html&quot;&gt;are telling you that&lt;/a&gt;! Let&#39;s address each of these one by one.&lt;br /&gt;&lt;br /&gt;Slow running tests? You shouldn&#39;t be hitting a database or web service in your unit tests, because you can mock/stub them out. Difficult to mock/stub it out? There probably is a better way to design your classes your tests are hitting. Ever heard of Inversion of Control (or Dependency Injection)? Master them. True TDD masters use them extensively.&lt;br /&gt;&lt;br /&gt;Unreadable tests? Is it because of too many mocks/stubs? Or is it the code is 500 lines long and doing high octane 720-double-backflip logic? Either way, you have to learn to like small objects. Check this &lt;a href=&quot;http://www.stephenchu.com/2006/08/not-enough-objects-anti-pattern.html&quot;&gt;blog post&lt;/a&gt; of mine out.&lt;br /&gt;&lt;br /&gt;Hard to test something? Tests too brittle? Perhaps you have encapsulation issues in your class design. If your classes are referencing 15 other neighbors, of course they are hard to mock/stub. Chances are, you have to spend time to debug your tests to find out what&#39;s wrong! Heard of Law of Demeter? Even if you have, take a look at this &lt;a href=&quot;http://www.pivotalblabs.com/articles/2007/08/05/lovely-demeter-meter-maid&quot;&gt;highly entertaining yet informative post&lt;/a&gt;. It might change your perspective a little.&lt;br /&gt;&lt;br /&gt;The bottom line is, TDD is a way to guide you to writing good code, but only if you know how to use it as a tool. Now that you know, hopefully you will have a new perspective next time you write a test.</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/1816987959902497846/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/1816987959902497846' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/1816987959902497846'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/1816987959902497846'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/12/last-d-in-tdd-means-more-than-just.html' title='The last &quot;D&quot; in TDD means more than just &quot;Development&quot;'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-7852348529582978590</id><published>2007-12-02T09:35:00.000-06:00</published><updated>2007-12-02T08:36:01.876-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails"/><title type='text'>Passing multiple parameters in observe_field</title><content type='html'>Sometimes you just want to pass multiple parameters on your ajax observe_field web request for processing. If you Google around, you will find out there are &lt;a href=&quot;http://wiki.rubyonrails.org/rails/pages/observe_field+-+Passing+Parameters&quot;&gt;multiple ways&lt;/a&gt; of doing it.&lt;br /&gt;&lt;br /&gt;And here is yet another way:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;    &lt;span class=&quot;ident&quot;&gt;observe_field&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:target_dom_id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;symbol&quot;&gt;:url&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;some_named_route_url&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;symbol&quot;&gt;:method&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:get&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;symbol&quot;&gt;:with&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Form.serializeElements($(&#39;text_field_one&#39;, &#39;text_field_two&#39;, ...))&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;where &#39;text_field_one&#39; and &#39;text_field_two&#39; are html form element ids.&lt;br /&gt;&lt;br /&gt;I like this because if my text fields are rendered through &lt;tt&gt;form_for&lt;/tt&gt; or &lt;tt&gt;fields_for&lt;/tt&gt; (e.g. &lt;tt&gt;form_for :product&lt;/tt&gt;), and my action does not want to explicitly specify what the posted parameters are upon processing (e.g. &lt;tt&gt;Product.create params[:product]&lt;/tt&gt;), then by using Form.serializeElements I can hide all the params inside the key &lt;tt&gt;params[:product]&lt;/tt&gt; still.</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/7852348529582978590/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/7852348529582978590' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/7852348529582978590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/7852348529582978590'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/12/passing-multiple-parameters-in.html' title='Passing multiple parameters in observe_field'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-509453354212238911</id><published>2007-11-27T23:50:00.000-06:00</published><updated>2007-11-27T23:11:38.273-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby/rails"/><title type='text'>Parsing time in custom format in Ruby</title><content type='html'>I am sure you have used Time#strftime (or Date, DateTime) to format your date/time into your own custom format for pretty time display. This is fine and great. But how do you parse a date or time in your own specific format in Ruby?&lt;br /&gt;&lt;br /&gt;There is a little method called DateTime#strptime. Its second parameter takes a string, containing basically all the options available to you in Time#strftime (eg. %m-%d-%Y) and return you a DateTime object. This method is strangely not available in Time class. Fortunately from there you can use the Rails methods #to_time to convert to the Time object you want.&lt;br /&gt;&lt;br /&gt;Usage:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;ruby&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;constant&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;strptime&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;12/25/2007 01:00 AM EST&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;,&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;%m/%d/%Y %I:%M %p %Z&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;)&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/509453354212238911/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/509453354212238911' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/509453354212238911'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/509453354212238911'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/11/parsing-time-in-custom-format-in-ruby.html' title='Parsing time in custom format in Ruby'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-1960501665882267987</id><published>2007-11-21T01:35:00.000-06:00</published><updated>2007-11-21T00:37:04.880-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="efficiency"/><title type='text'>Stop tangling those iPod earbuds!</title><content type='html'>You have an iPod? If you ever use their white trademarked earbuds, chances are every time you pull them out of your pockets you spend 40 seconds of your life untangling those stupid wire. It adds up.&lt;br /&gt;&lt;br /&gt;Ever notice there is a little sliding thing at the Y-split of your earbuds on the wire? I assume it is used to reduce the split between the two ear pieces when you are wearing them. If they are for that purpose I have never used them. But I did find a better use of it...&lt;br /&gt;&lt;br /&gt;Next time when you are done using them, slide that thing all the way to the ear pieces. This will make your Y-splitted earbuds into essentially one straight wire, and thus reduce a lot of unnecessary tangling. Hopefully this saves you some annoying faces in untangling the thing (maybe that&#39;s why those Apple billboards have no face on them...)</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/1960501665882267987/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/11933937/1960501665882267987' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/1960501665882267987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/1960501665882267987'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/11/stop-tangling-those-ipod-earbuds.html' title='Stop tangling those iPod earbuds!'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-2707280037157235285</id><published>2007-11-18T15:30:00.000-06:00</published><updated>2007-11-18T15:36:16.538-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="agile"/><title type='text'>Numbers don&#39;t lie, but your test coverage numbers might</title><content type='html'>We have learned all though our education that our decisions should base off of numbers. Can you support your family given however much you are making? What is the velocity of gravity? How many roses do you give your girlfriend on Valentine&#39;s Day (no satisfying answers ever...)&lt;br /&gt;&lt;br /&gt;The same happens in software. On all software projects, various flavors of metrics are gathered and read by various types of people on the team. Code coverage being one of them that is most commonly mentioned.&lt;br /&gt;&lt;br /&gt;But there is a misconception about code coverage: When the percentage is high, it is good; otherwise, it is not good.&lt;br /&gt;&lt;br /&gt;Then people try to find definition of &quot;high&quot;, some say 80% is a good number, others say 90%. Some even strive for 100%.&lt;br /&gt;&lt;br /&gt;But let&#39;s decode this message a little more thoroughly. If your number is low, this means your code is not very well tested. Clearly this is not desirable in a code base that you have to go in every day and make changes here and there - good luck in not breaking stuff. So, a low coverage number is bad.&lt;br /&gt;&lt;br /&gt;Now, a high coverage number means your code is well tested. But this number does not tell you a few things:&lt;br /&gt;&lt;br /&gt;1) The quality of your code. It does not tell you whether your objects are coupled like spaghetti; it does not tell you whether your code is doing crazily unreadable nested iterations mixing with multiple levels of recursions; it does not tell you whether you are violating encapsulation and separate of concerns; it does not tell you whether you are copy &amp; pasting code everywhere. If your code exhibits all of these symptoms, a 99.9% code coverage still means future changes to the code base is going to be a nightmare.&lt;br /&gt;&lt;br /&gt;2) The quality of your tests. Tests are also code that needs to be maintained. If it takes someone 30 seconds to make a single line of code change, but it breaks hundreds of tests and takes that person an additional hour to duplicate that fix in all failed tests, then the burden of maintaining those tests outweights the feedback of whether the system still works or not. Further, if tests are hard to read as they are long and mocks/stubs everywhere, and the test method names do not reflect what the test method body is doing, then having your tests unmaintained just adds test maintenance time.&lt;br /&gt;&lt;br /&gt;So how should you read the coverage number? You should read this number in combination with other metrics to gauge the health of the code base. For example, do defects constantly come from a feature area of the code when changes are being made? Are story estimates higher in certain areas of the code base but not others? Are stories in certain functional areas always being under-estimated during Planning Meeting?&lt;br /&gt;&lt;br /&gt;Code coverage is just a number. It does not tell you whether your code&#39;s flexibility to reflect the pace of your business requirements change. True productivity comes in with good code solving a particular problem well. Well tested code can still be bad.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/2707280037157235285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/2707280037157235285'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/11/numbers-dont-lie-but-your-test-coverage.html' title='Numbers don&#39;t lie, but your test coverage numbers might'/><author><name>Stephen Chu</name><uri>http://www.blogger.com/profile/07181510921622947661</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry></feed>