<?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: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>2012-01-30T01:29:48.754-06:00</updated><category term='pattern'/><category term='.net'/><category term='agile'/><category term='mysql'/><category term='ruby/rails'/><category term='humor'/><category term='efficiency'/><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?max-results=100'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><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='http://img2.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>100</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'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' 55", 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 "undo log entries" 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 "inserts/s" under the ROW OPERATIONS section.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-6593069220704055537?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=6593069220704055537' title='4 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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</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='ruby/rails'/><category scheme='http://www.blogger.com/atom/ns#' term='efficiency'/><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="http://gerd.knops.org/?p=7"&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 "go to definition" 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's a fix for you. The perl script that got executed underneath this little utility actually uses a TextMate variable &lt;span style="font-style:italic;"&gt;TM_CURRENT_WORD&lt;/span&gt;. This variable points at the "word" 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="font-style:italic;"&gt;Preferences / Text Editing&lt;/span&gt;, and change &lt;span style="font-style:italic;"&gt;Word Characters&lt;/span&gt; to include ! and ?.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_OeQDslkBljg/SN0dmv4NgpI/AAAAAAAAADg/7kEDBsZxxsc/s1600-h/ctags_result.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_OeQDslkBljg/SN0dmv4NgpI/AAAAAAAAADg/7kEDBsZxxsc/s400/ctags_result.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5250385292108333714" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-4031210489060497508?l=www.stephenchu.com' alt='' /&gt;&lt;/div&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.g?blogID=11933937&amp;postID=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='http://img2.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="ruby"&gt;&lt;pre&gt;&lt;span class="attribute"&gt;@posts&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:conditions&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="symbol"&gt;:user_id&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;This happens to new Rails developers quite a bit. This code retrieves correctly an array of &lt;tt&gt;@posts&lt;/tt&gt; and send them to the view for rendering. But what about it?&lt;br /&gt;&lt;br /&gt;The key thing here is that this can also be done in a better way:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="attribute"&gt;@posts&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;posts&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;When you use the former method to retrieve &lt;tt&gt;@posts&lt;/tt&gt;, you are actually thinking in database terms along the lines of &lt;em&gt;"in the database table '&lt;tt&gt;posts&lt;/tt&gt;' and therefore &lt;tt&gt;ActiveRecord&lt;/tt&gt; model '&lt;tt&gt;Post&lt;/tt&gt;', retrieve the rows whose foreign key column '&lt;tt&gt;user_id&lt;/tt&gt;' has the value &lt;tt&gt;@user.id&lt;/tt&gt;."&lt;/em&gt; &lt;tt&gt;ActiveRecord&lt;/tt&gt; is a pattern for accessing your data from objects, but you also have to combine it with the power of the object-oriented-ness of Ruby to create the chimes of beautiful code. Using Rails and Ruby does not make anyone a better programmer automagically. One can still write code in Java/C# in the same procedural style as if you were writing C. It is how to leverage the best of all worlds makes you a better problem solver.&lt;br /&gt;&lt;br /&gt;Therefore, think like an object bigot. Think in objects, and not in database foreign key column values. Whenever you see any Rails code in the pattern of this:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="symbol"&gt;:model_id&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@model&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Just stop. "Objects on Rails" sounds a lot better than "Ruby on Foreign Keys."&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-2462354306162455570?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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='http://img2.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="ruby"&gt;&lt;pre&gt;&lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Drink up your milk!&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@milk&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;expiration_date&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="number"&gt;2&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;days&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;from_now&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I think there is. In particular, the programmer can do better. It is about a programming concept that we have all heard and should be familiar with: &lt;strong&gt;encapsulation&lt;/strong&gt;. If you are into testing, which you ought to be, you can probably identify such encapsulation violations &lt;a href="http://www.stephenchu.com/2007/01/use-your-test-classes-to-identify.html"&gt;by how smelly your tests are&lt;/a&gt;, as I believe in &lt;a href="http://www.stephenchu.com/2007/12/last-d-in-tdd-means-more-than-just.html"&gt;Test Driven Design&lt;/a&gt;. Going back to the line of code. What's wrong with it?&lt;br /&gt;&lt;br /&gt;The pattern of this code is, you have code somewhere that yanks states (&lt;tt&gt;expiration_state&lt;/tt&gt;) out of an object (&lt;tt&gt;@milk&lt;/tt&gt;) and then interrogate against those states (&lt;tt&gt;&amp;lt; 2.days.from_now&lt;/tt&gt;). By no means this is a rule, as exceptions do exist. But, when this happens, your programming siren in your head should go off as if the CI build is broken: can this be a method on the object &lt;tt&gt;@milk&lt;/tt&gt; itself?&lt;br /&gt;&lt;br /&gt;Ruby is a powerful programming language. It allows you to do all sorts of fancy programming: dynamically altering classes, add methods to only the selected few object instances, while being duck-typed and object-oriented all at the same time. There are examples galore in Rails itself. But to enable all such magic, all of your &lt;tt&gt;ActiveRecord&lt;/tt&gt; domain models have getters and setters on all of their states (i.e. data). While that is convenient to access their states, sometimes you have to be careful. With great power comes great responsibility, and that responsibility comes down to you.&lt;br /&gt;&lt;br /&gt;You might not think this applies to you. But have you ever written code like this in Ruby?&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="constant"&gt;MyMailer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;deliver_product_returned_notification&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@product&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="constant"&gt;State&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Returned&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;total&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@line_items&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;sum&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;line_item&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;line_item&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;price&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt; &lt;span class="ident"&gt;line_item&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;quantity&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:admin&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:superuser&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;include?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;role&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now, let's look at these rewritten examples:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Drink up your milk!&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@milk&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;expiring?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="constant"&gt;MyMailer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;deliver_product_returned_notification&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@product&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;returned?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;total&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@line_items&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;sum&lt;/span&gt;&lt;span class="punct"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="symbol"&gt;:subtotal&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;superuser?&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Forget about the loops, the blocks, the hype, and everything about Ruby for a sec. Code is code. Not only does the code become more readable, when you try to enrich your domain by naming things correctly, you could also very well be opening up new business concepts that wasn't previously clear or accurate. If you have a domain model, you are modeling it against domain concepts so that your app can interact with. You'd better be sure it is right, or else lots of time will go wasted on code that solves only half of the business problems all the time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-4982606554165380166?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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='http://img2.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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-89965006330598668?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=89965006330598668' title='1 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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</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 "Skinny Controllers". To learn about, visit, check out the &lt;a href="http://www.stephenchu.com/2008/03/introduction-to-rails-composedof.html"&gt;Rails composed_of &amp;block conversion&lt;/a&gt; blog entry.)&lt;br /&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Using the same example in the previous entry, let's include our &lt;tt&gt;ActiveRecord::Validation&lt;/tt&gt; module into our &lt;tt&gt;Money&lt;/tt&gt; class like such:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Money&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="symbol"&gt;:Validations&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="ident"&gt;attr_reader&lt;/span&gt; &lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:currency&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="ident"&gt;validates_presence_of&lt;/span&gt;  &lt;span class="symbol"&gt;:currency&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_inclusion_of&lt;/span&gt; &lt;span class="symbol"&gt;:currency&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:in&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;USD&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;EUR&lt;/span&gt;&lt;span class="punct"&gt;'],&lt;/span&gt; &lt;span class="symbol"&gt;:if&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:currency?&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_presence_of&lt;/span&gt;     &lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_numericality_of&lt;/span&gt; &lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:if&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:balance?&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;currency&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="attribute"&gt;@currency&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;currency&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@errors&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Errors&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;new_record?&lt;/span&gt;&lt;br /&gt;    &lt;span class="constant"&gt;true&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;currency?&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;!&lt;/span&gt;&lt;span class="attribute"&gt;@currency&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;blank?&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;balance?&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;!&lt;/span&gt;&lt;span class="attribute"&gt;@balance&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;blank?&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.human_attribute_name&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;attr&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;attr&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;humanize&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;balance_before_type_cast&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@balance&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;  &lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;You will notice a couple things. One, I have to define the &lt;tt&gt;#new_record?&lt;/tt&gt; method. This method is defined on all &lt;tt&gt;ActiveRecord::Base&lt;/tt&gt; objects, but since our PORO object is not a record per se, we just stub it out. Also, we need to store a collection &lt;tt&gt;@errors&lt;/tt&gt; of type &lt;tt&gt;ActiveRecord::Errors&lt;/tt&gt;. &lt;br /&gt;&lt;br /&gt;Depending on what validation routine you will end up using in &lt;tt&gt;Money&lt;/tt&gt;, you may have to stub out different methods. For example, I am showing error messages with &lt;tt&gt;error_messages_for&lt;/tt&gt; (more on this later), and it requires stubbing out &lt;tt&gt;self.human_attribute_name&lt;/tt&gt; (as of Rails 2.0, but no longer needed &lt;a href="http://dev.rubyonrails.org/ticket/8760"&gt;in future Rails&lt;/a&gt;). Using &lt;tt&gt;validates_numericality_of&lt;/tt&gt; requires me to stub out &lt;tt&gt;balance_before_type_cast&lt;/tt&gt;. Also, the validation &lt;tt&gt;:if&lt;/tt&gt; conditions requires me to add the question-mark methods &lt;tt&gt;balance?&lt;/tt&gt; and &lt;tt&gt;currency?&lt;/tt&gt;. Remember, this approach does not give you all the validation magic. For example, &lt;tt&gt;validates_uniqueness_of&lt;/tt&gt; will not work because it assumes too much about your object being a normal AR model and needs access to a database. But in practice, your Value Objects should not need such validations, and in most cases they contain only simple one-off validations, and provide simple functionalities such as formatting like &lt;a href="http://www.stephenchu.com/2008/01/format-your-data-using-rails-composedof.html"&gt;this&lt;/a&gt; and &lt;a href="http://www.stephenchu.com/2008/03/little-tos-can-do-wonder-in-dry-ing-up.html"&gt;this&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;After all these, let's see our ActiveRecord &lt;tt&gt;Book&lt;/tt&gt; class.&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Book&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Bases&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;composed_of&lt;/span&gt; &lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:class_name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Money&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:mapping&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[%w(&lt;/span&gt;&lt;span class="string"&gt;balance balance&lt;/span&gt;&lt;span class="punct"&gt;),&lt;/span&gt; &lt;span class="punct"&gt;%w(&lt;/span&gt;&lt;span class="string"&gt;currency currency&lt;/span&gt;&lt;span class="punct"&gt;)]&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;     &lt;span class="constant"&gt;Money&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;span class="punct"&gt;],&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:currency&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_presence_of&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_associated&lt;/span&gt;  &lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The &lt;tt&gt;composed_of&lt;/tt&gt; conversion block remains. You will notice the &lt;tt&gt;validates_associated :balance&lt;/tt&gt; line as well. This tells your book instances that they should not be persisted should there be any &lt;tt&gt;balance&lt;/tt&gt; validations failing, just like any normal validations you would write. By default, any failing &lt;tt&gt;balance&lt;/tt&gt; validation will add an error message &lt;em&gt;'Balance is invalid'&lt;/em&gt; in your &lt;tt&gt;@book#errors&lt;/tt&gt; collection. If you want to suppress that message from showing up, you can pass in option &lt;tt&gt;:message =&gt; nil&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;So, to put it all together, here is the view and the controller code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="scrolling_div textmate-source"&gt;&lt;span class="text text_html text_html_ruby"&gt;&lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;h1&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;New book&lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;h1&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%=&lt;/span&gt; error_messages_for &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;object&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;[&lt;/span&gt;&lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;book&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;book&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;balance&lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;]&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%&lt;/span&gt; form_for&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;book&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby"&gt;do &lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby"&gt;|&lt;/span&gt;&lt;span class="variable variable_other variable_other_block variable_other_block_ruby"&gt;f&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby"&gt;|&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Name: &lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%=&lt;/span&gt; f&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;text_field &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;name&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%&lt;/span&gt; f&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;fields_for &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;balance&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;book&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;balance &lt;span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby"&gt;do &lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby"&gt;|&lt;/span&gt;&lt;span class="variable variable_other variable_other_block variable_other_block_ruby"&gt;ff&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby"&gt;|&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Balance: &lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%=&lt;/span&gt; ff&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;text_field &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;balance&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Currency: &lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%=&lt;/span&gt; ff&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;text_field &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;currency&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%=&lt;/span&gt; f&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;submit &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;Create&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_tag meta_tag_block meta_tag_block_any meta_tag_block_any_html"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="entity entity_name entity_name_tag entity_name_tag_block entity_name_tag_block_any entity_name_tag_block_any_html"&gt;p&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_tag punctuation_definition_tag_html"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="source source_ruby source_ruby_rails source_ruby_rails_embedded source_ruby_rails_embedded_html"&gt;&lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;&amp;lt;%=&lt;/span&gt; link_to &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;'&lt;/span&gt;Back&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; books_path &lt;span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To show error messages from multiple objects on the view, I am using the view helper method &lt;tt&gt;error_messages_for(*args)&lt;/tt&gt;. The &lt;tt&gt;:object&lt;/tt&gt; option actually allows you to pass an array of objects (c'mon, you should know this trick about &lt;tt&gt;ActiveRecord&lt;/tt&gt; by now. If not, check it out &lt;a href="http://www.stephenchu.com/2008/03/paramsfu-3-using-fieldsfor-and-index.html"&gt;here&lt;/a&gt; and &lt;a href="http://www.stephenchu.com/2008/03/paramsfu-5-update-multiple-models-in.html"&gt;here&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@book&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Book&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:book&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@book&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:notice&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Book was successfully created.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;redirect_to&lt;/span&gt; &lt;span class="attribute"&gt;@book&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;render&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Again, a skinny, thin, sexy-looking controller action.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-3716593703045725703?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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='http://img2.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 "thing" you are CRUD-ing on. However, all too often we are tasked to show some pages that we aren't doing any CRUD operations on. Just a few examples:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Dashboard page after user logs in&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;The "Forgot your password?" page&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Tabs or subtabs of multiple lists&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;List page showing multiple lists of different entities&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;A normal dashboard page isn't something you would normally CRUD on. It is more like a place holder page that a user sees the first thing after s/he logs in, showing many items of all sorts valuable to a user. In many Rails app, depending on what those items are, the dashboard page will end up being rendered by one of the items' &lt;tt&gt;index&lt;/tt&gt; action. Worse yet, next time when you actually need that &lt;tt&gt;index&lt;/tt&gt; page by that model, you have to call it something else, maybe 'list', and then play with the routes to get it wired up correctly.&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;CustomersController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="comment"&gt;# The dashboard page&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;index&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@customers&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Customer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@products&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Product&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@tasks_of_the_day&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Task&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="comment"&gt;# A list of customers&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;list&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@customers&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Customer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="constant"&gt;ActionController&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Routing&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Routes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;draw&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;resources&lt;/span&gt; &lt;span class="symbol"&gt;:customers&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:products&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:tasks&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;with_options&lt;/span&gt; &lt;span class="symbol"&gt;:controller&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;customer&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;r&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;r&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;dashboard&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;/customers/list&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;list&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Here's a suggestion: how about put it in &lt;tt&gt;views/dashboards/show.html.erb&lt;/tt&gt;, and, while you are at it, give it a &lt;tt&gt;DashboardsController&lt;/tt&gt;? Then, put it in your &lt;tt&gt;routes.rb&lt;/tt&gt; as a (singular) &lt;tt&gt;map.resource :dashboard&lt;/tt&gt;, like such:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;DashboardsController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;show&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@customers&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Customer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@products&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Product&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@tasks_of_the_day&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Task&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="constant"&gt;ActionController&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Routing&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Routes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;draw&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;resources&lt;/span&gt; &lt;span class="symbol"&gt;:customers&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:products&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;tasks&lt;/span&gt;  &lt;br /&gt;  &lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;resource&lt;/span&gt; &lt;span class="symbol"&gt;:dashboard&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;By rendering the dashboard page in a completely different controller, you now have a very readable &lt;tt&gt;GET dashboard_path&lt;/tt&gt; named route (&lt;tt&gt;GET: http://localhost:3000/dashboards&lt;/tt&gt;), and you will not contaminate the &lt;tt&gt;index&lt;/tt&gt; action of your other models' controllers with instance variables of all kinds. You also have a more readable &lt;tt&gt;routes.rb&lt;/tt&gt; file.&lt;br /&gt;&lt;br /&gt;One of the examples above for non-RESTful pages is the "Forgot My Password" page. Can you think of a good way to do it the Rails REST-ful way? Please go to &lt;a href="http://www.stephenchu.com/2007/11/seeing-rails-resources-clearly.html"&gt;Seeing Rails Resources Clearly&lt;/a&gt; to share some of your thoughts.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-613937363191220353?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=613937363191220353' title='3 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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</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="http://www.flickr.com/photos/nealford/291110547/"&gt;Neal's...&lt;/a&gt;), except on an every day basis.&lt;br /&gt;&lt;br /&gt;If you want your rake tasks to be more manageable, here are a few tips:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1) Namespace your rake tasks.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Namespaces in rake is nice. They make sure all tasks in your namespace are cohesive as a area of work. Still, your job is to keep your rake tasks not too crowded in a single namespace. For example, the &lt;tt&gt;db&lt;/tt&gt; namespace that Rails provides very easily become crowded with tasks of all sorts, because, well, on a Rails application what you do almost always is related to your database: drop/create/migrate your database, &lt;a href="http://www.stephenchu.com/2007/12/why-create-database-migrations-when-you.html"&gt;importing reference/application data&lt;/a&gt;, database integration points with other applications, db related background tasks, backups... Phew! How about create a new namespace for &lt;tt&gt;import&lt;/tt&gt;, &lt;tt&gt;integration&lt;/tt&gt;, &lt;tt&gt;background&lt;/tt&gt;, and &lt;tt&gt;backup&lt;/tt&gt; for each of them?&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2) Name your rake files by their namespaces.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Even if you namespace all your rake tasks, still, you are likely to end up with a bunch of &lt;tt&gt;.rake&lt;/tt&gt; files in &lt;tt&gt;lib/tasks&lt;/tt&gt;. But, now that you have your tasks grouped by their meaningful and not-so-crowded namespaces, you should name your files by your namespace. For example, &lt;tt&gt;db.rake&lt;/tt&gt;, &lt;tt&gt;data.rake&lt;/tt&gt;, &lt;tt&gt;import.rake&lt;/tt&gt;, &lt;tt&gt;integration.rake&lt;/tt&gt;, and &lt;tt&gt;backup.rake&lt;/tt&gt;. Can you guess where that &lt;tt&gt;rake db:awesome&lt;/tt&gt; is implemented? You certainly don't know what it does, but you got that right, in &lt;tt&gt;lib/tasks/db.rake&lt;/tt&gt;. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;3) Try not to nest more than 2 namespaces.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;This tip is not a rule, the point being don't over do it. Sometimes, that third-level namespace really makes sense, For example, &lt;tt&gt;db:create:all&lt;/tt&gt; from Rails creates databases for all environments. It isn't for everything, though. For example, I prefer &lt;tt&gt;rake data:load&lt;/tt&gt; better than &lt;tt&gt;rake db:data:load&lt;/tt&gt;. Not only is it shorter, but also the namespace &lt;tt&gt;db&lt;/tt&gt; is simply implied. Short nested-ness forces you to name your namespaces more descriptively, and thus easier to navigate to their implementation.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;4) Move your rake file and its external ruby files into a sub-folder, named by that namespace.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;When your rake tasks are complicated enough, you obviously will use objects to do the job for you. When that happens, instead of having &lt;tt&gt;lib/tasks/integration.rake&lt;/tt&gt;, you will instead want to have a sub-folder &lt;tt&gt;lib/tasks/integration/&lt;/tt&gt;, and put files like &lt;tt&gt;integration.rake&lt;/tt&gt; and &lt;tt&gt;integration_awesome_klass.rb&lt;/tt&gt; in it. There really is no need to contaminate your &lt;tt&gt;lib&lt;/tt&gt; folder with rake specific files.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Shameless Plug: Still using NAnt? Check out how to organize your NAnt scripts as well, on &lt;a href="http://www.stephenchu.com/2006/03/pragmatic-nant-scripting.html"&gt;this blog post&lt;/a&gt;.&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-4713401589862405098?l=www.stephenchu.com' alt='' /&gt;&lt;/div&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.g?blogID=11933937&amp;postID=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='http://img2.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't, if you know how to take advantage of it. Knowing what you know about Rails params, let's take a look at today's topic: the &lt;tt&gt;update&lt;/tt&gt; action.&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;form_for&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="attribute"&gt;@subscription&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="attribute"&gt;@subscriptions&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;    &amp;lt;p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;fields_for&lt;/span&gt; &lt;span class="attribute"&gt;@subscription&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;ff&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;      &amp;lt;%= ff.collection_select :magazine_id, Magazine.find(:all), :id, :name, {}, :index =&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@subscription&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;    &amp;lt;% end %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;p&amp;gt;&lt;br /&gt;  &amp;lt;% end %&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%= f.submit 'Save' %&amp;gt;&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;span class="normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;generates:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt; &lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;subscription_4_magazine_id&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;subscription[4][magazine_id]&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;101&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;PC&lt;/span&gt; &lt;span class="constant"&gt;Magazine&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt; &lt;br /&gt;    &amp;lt;option value=&amp;quot;102&amp;quot;&amp;gt;IT Pro&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;    &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;103&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="ident"&gt;selected&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;selected&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;WIRED&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt;&lt;br /&gt;  &amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;p&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;p&amp;gt;     &lt;br /&gt;  &amp;lt;select id=&amp;quot;subscription_5_magazine_id&amp;quot; name=&amp;quot;subscription[5][magazine_id]&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;option value=&amp;quot;101&amp;quot;&amp;gt;PC Magazine&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;    &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;102&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;IT&lt;/span&gt; &lt;span class="constant"&gt;Pro&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt; &lt;br /&gt;    &amp;lt;option value=&amp;quot;103&amp;quot; selected=&amp;quot;selected&amp;quot;&amp;gt;WIRED&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;select&amp;gt;&lt;br /&gt;&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;  &lt;br /&gt;&lt;span class="punct"&gt;...&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="ident"&gt;more&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;    &lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class='scrolling_div'&gt;&lt;pre&gt;Processing ReadersController#update (for 127.0.0.1 at 2008-01-14 21:12:56) [PUT]&lt;br /&gt;Parameters: { "commit"             =&gt; "Update",&lt;br /&gt;              "reader"             =&gt; { "name" =&gt; "stephen chu" },&lt;br /&gt;              "subscriptions"      =&gt; { "4" =&gt; { "magazine_id" =&gt; "101" },&lt;br /&gt;                                        "6" =&gt; { "magazine_id" =&gt; "102" }, &lt;br /&gt;                                        "7" =&gt; { "magazine_id" =&gt; "103" } },&lt;br /&gt;              "authenticity_token" =&gt; "238ba79b8282882ba01d840352616c2cc79280f0",&lt;br /&gt;              "action"             =&gt; "create",&lt;br /&gt;              "controller"         =&gt; "readers" }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;See the pattern? The POST-ed parameters are of the same structure as in one of our &lt;a href="http://www.stephenchu.com/2008/03/paramsfu-3-using-fieldsfor-and-index.html"&gt;last example&lt;/a&gt;, hash of hashes. The sub-hashes are keying off of the subscription id, because this time around we are updating existing subscriptions. So last time we used &lt;tt&gt;params[:subscriptions].values&lt;/tt&gt;, what would it look like this time? Let take a look.&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;update&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:id&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;attributes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;subscriptions&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;update&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:subscription&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;keys&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:subscription&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;values&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:notice&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Reader was successfully updated.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;redirect_to&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:notice&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Failed.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Again, another ActiveRecord model method utilizes the array of hashes pattern! The &lt;tt&gt;update&lt;/tt&gt; method source code on RDoc looks like it is just updating one at a time. But a peek at the source code says otherwise:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;update&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;attributes&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Array&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;idx&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;-&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;collect&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;idx&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;update&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;attributes&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;idx&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;object&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;object&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;update_attributes&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;attributes&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;object&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Again, the &lt;tt&gt;update&lt;/tt&gt; method recognizes array! So, where to get the arrays that we will use in our controller action? They come from &lt;tt&gt;.keys&lt;/tt&gt; and &lt;tt&gt;.values&lt;/tt&gt; of course:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;keys&lt;/span&gt;    &lt;span class="comment"&gt;# =&amp;gt; [ &amp;quot;4&amp;quot;, &amp;quot;6&amp;quot;, &amp;quot;7&amp;quot; ]&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;values&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; [ { &amp;quot;magazine_id&amp;quot; =&amp;gt; &amp;quot;101&amp;quot; }, { &amp;quot;magazine_id&amp;quot; =&amp;gt; &amp;quot;102&amp;quot; }, ... ]&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So in essence, our controller code is free from all those ugly params-munging activities. Remember, controller actions should not shuffle around their params, or otherwise it fails to abide the "Skinny Controller, Fat Model" principle, and they will stink.&lt;br /&gt;&lt;br /&gt;Now, if you are thinking about by using &lt;tt&gt;update&lt;/tt&gt;, we run the risk of not atomically saving all of our models should any of our models fail validation, you are correct. This is where &lt;tt&gt;rescue_from&lt;/tt&gt; in controller saves the day. Just transact our &lt;tt&gt;update&lt;/tt&gt; action using AR transaction, and re-render the edit page should it catches &lt;tt&gt;ActiveRecord::RecordInvalid&lt;/tt&gt; error, you should be able to make your &lt;tt&gt;update&lt;/tt&gt; action atomic. Given how lean our controller action looks like, having a transaction block that wraps around our code is not so much a nuisance anymore.&lt;br /&gt;&lt;br /&gt;This also wraps up our &lt;a href="http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html"&gt;&lt;tt&gt;params[:fu]&lt;/tt&gt; series&lt;/a&gt;. Remember, how you assemble your views form elements have a lot to do with how thin and skinny your controllers look like. Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-6529429523836691861?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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='http://img2.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 &lt;association_name&gt;_ids=([...array of ids]) association proxy method.</title><content type='html'>In the &lt;a href="http://www.stephenchu.com/2008/03/paramsfu-3-using-fieldsfor-and-index.html"&gt;last blog entry&lt;/a&gt;, we looked at an example of having to build a new parent model and build its join models all in two lines of code. But if you are trying to just assign a list of &lt;tt&gt;has_many&lt;/tt&gt; or &lt;tt&gt;has_and_belongs_to_many&lt;/tt&gt; models, you don't have to use the &lt;tt&gt;fields_for&lt;/tt&gt; and &lt;tt&gt;index&lt;/tt&gt; trick. You can just use an association proxy method. Let's check it out:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Reader&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;has_and_belongs_to_many&lt;/span&gt; &lt;span class="symbol"&gt;:blogs&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;form_for&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; f.collection_select :blog_ids, Blog.find(:all), :id, :name, {}, :multiple &lt;/span&gt;&lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%= f.submit 'Create' %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;generates:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt; &lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;reader_blog_ids&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="ident"&gt;multiple&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;multiple&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;reader[blog_ids][]&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;Martin&lt;/span&gt; &lt;span class="constant"&gt;Fowler&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt;&lt;br /&gt;  &amp;lt;option value=&amp;quot;2&amp;quot;&amp;gt;Stephen Chu&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;select&amp;gt; &lt;span class="normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class='scrolling_div'&gt;&lt;pre&gt;Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]&lt;br /&gt;Parameters: { "commit"             =&gt; "Create",&lt;br /&gt;              "reader"             =&gt; { "name" =&gt; "stephen chu"&lt;br /&gt;                                        "blog_ids" =&gt; ["1", "2"] },&lt;br /&gt;              "authenticity_token" =&gt; "238ba79b8282882ba01d840352616c2cc79280f0",&lt;br /&gt;              "action"             =&gt; "create",&lt;br /&gt;              "controller"         =&gt; "readers" }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;And now, in my controller action, the creation of the reader only takes one line of code:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;... thanks to the following &lt;tt&gt;has_many&lt;/tt&gt; (or &lt;tt&gt;habtm&lt;/tt&gt;) association assignment method:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;blog_ids&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[...many ids...]&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So, stop doing any more params-munging code in your controller action like these:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:blog_ids&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;blogs&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;Blog&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Get on the Rails bandwagon!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-3770960745050475939?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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='http://img2.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's take a look at a slightly more complicated example, but one that occurs on almost every single Rails project out there: saving multiple models on one POST. Let's say your &lt;tt&gt;new&lt;/tt&gt; form allows you to create a new reader and attach 3 subscriptions to it. Here's the code:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Reader&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;has_many&lt;/span&gt; &lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;has_many&lt;/span&gt; &lt;span class="symbol"&gt;:magazines&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:through&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_associated&lt;/span&gt; &lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;form_for&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="number"&gt;3&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;index&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;    &amp;lt;% fields_for :subscriptions do |ff| %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; ff.collection_select :magazine_id, Magazine.find(:all), :id, :name, {}, :index &lt;/span&gt;&lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;index&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&amp;lt;/p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;% end %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; f.submit 'Create' %&amp;gt;&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;span class="normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;generates:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt; &lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;subscriptions_1_magazine_id&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;subscriptions[1][magazine_id]&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;101&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;PC&lt;/span&gt; &lt;span class="constant"&gt;Magazine&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt;&lt;br /&gt;  &amp;lt;option value=&amp;quot;102&amp;quot;&amp;gt;IT Pro&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;103&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;WIRED&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt;&lt;br /&gt;&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt; &lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;subscriptions_2_magazine_id&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;subscriptions[2][magazine_id]&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;101&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;PC&lt;/span&gt; &lt;span class="constant"&gt;Magazine&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt;&lt;br /&gt;  &amp;lt;option value=&amp;quot;102&amp;quot;&amp;gt;IT Pro&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;option&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;103&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;WIRED&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;option&amp;gt;&lt;br /&gt;&amp;lt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;... (and more)&lt;br /&gt;&lt;br /&gt;&lt;div class='scrolling_div'&gt;&lt;pre&gt;Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]&lt;br /&gt;Parameters: { "commit"             =&gt; "Create",&lt;br /&gt;              "reader"             =&gt; { "name" =&gt; "stephen chu" },&lt;br /&gt;              "subscriptions"      =&gt; { "1" =&gt; { "magazine_id" =&gt; "101" },&lt;br /&gt;                                        "2" =&gt; { "magazine_id" =&gt; "102" }, &lt;br /&gt;                                        "3" =&gt; { "magazine_id" =&gt; "103" } },&lt;br /&gt;              "authenticity_token" =&gt; "238ba79b8282882ba01d840352616c2cc79280f0",&lt;br /&gt;              "action"             =&gt; "create",&lt;br /&gt;              "controller"         =&gt; "readers" }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;By using the &lt;tt&gt;:index&lt;/tt&gt; html option in our form builder generated fields, we are essentially inserting a unique index key to the posted value of that field. Why should we care? Well, here is how the controller code would look like:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@subscriptions&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;subscriptions&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;build&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;values&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:success&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Good.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:error&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Bad.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I just persisted a reader with &lt;em&gt;multiple&lt;/em&gt; subscriptions, and the notable differences I added in the create action, was these characters: &lt;tt&gt;params[:subscriptions]&lt;strong&gt;.values&lt;/strong&gt;&lt;/tt&gt;. Nothing much changed from the last &lt;tt&gt;has_one :computer&lt;/tt&gt; example besides association related differences. There is no looping, map/collect-ing, gsub-ing, etc., in my action while creating and attaching these multiple subscriptions onto the new reader. The controller action is just doing its same-old routine: receives posted parameters, and shove them into the corresponding models. So how is this done? &lt;br /&gt;&lt;br /&gt;Looking at the source of the &lt;tt&gt;#build&lt;/tt&gt; method on the association proxy classes (e.g. &lt;tt&gt;HasManyAssociation&lt;/tt&gt;), you will notice something interesting:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;build&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;attributes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{})&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;attributes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Array&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;attributes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;collect&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;attr&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;build&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;attr&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;build_record&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;attributes&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;record&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;set_belongs_to_association_for&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;record&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The method recognizes array! If you pass in an array of hashes, it will process them one by one! So how do we get an array of hashes? In our case the way to get array of hashes is by calling &lt;tt&gt;Hash#values&lt;/tt&gt;, because they are organized in sub-hashes. Calling &lt;tt&gt;.values&lt;/tt&gt; will yield us the following array:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:subscriptions&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;values&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; [ { &amp;quot;magazine_id&amp;quot; =&amp;gt; &amp;quot;101&amp;quot; }, { &amp;quot;magazine_id&amp;quot; =&amp;gt; &amp;quot;102&amp;quot; }, ... ]&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;By organizing your &lt;tt&gt;params&lt;/tt&gt; on your view in ways that they can be directly consumed by your models, you end up with a lot less code to write. No more bastardizing your action code!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html"&gt;(back to the TOC of the params[:fu] series)&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-6542929948815344833?l=www.stephenchu.com' alt='' /&gt;&lt;/div&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.g?blogID=11933937&amp;postID=6542929948815344833' title='8 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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</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="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;form_for&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;fields_for&lt;/span&gt; &lt;span class="symbol"&gt;:favourite_computer&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="attribute"&gt;@computer&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;ff&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;    &amp;lt;%= ff.text_field :name %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;%= f.submit 'Create' %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="scrolling_div"&gt;&lt;pre&gt;Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]&lt;br /&gt;Parameters: { "commit"             =&gt; "Create",&lt;br /&gt;              "reader"             =&gt; { "name" =&gt; "stephen chu" },&lt;br /&gt;              "favourite_computer" =&gt; { "name" =&gt; "macbook pro" },&lt;br /&gt;              "authenticity_token" =&gt; "238ba79b8282882ba01d840352616c2cc79280f0",&lt;br /&gt;              "action"             =&gt; "create",&lt;br /&gt;              "controller"         =&gt; "readers" }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@computer&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;build_computer&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:favourite_computer&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:success&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Good.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:error&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Bad.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Reader&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;has_one&lt;/span&gt; &lt;span class="symbol"&gt;:computer&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_associated&lt;/span&gt; &lt;span class="symbol"&gt;:computer&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Here, I am on a page that creates the reader and its favorite computer. Two models sounds hard? Not really, if you key your params to a different key for each of the model, your controller is still nice and thin. You do need to add one line to the action to create the computer though, but that maps nicely to the fact that you are operating on two models from the page post. I would not want to add more than one line of code to create one additional model, so forget those params-munging/looping.&lt;br /&gt;&lt;br /&gt;Also, you can see that I am using &lt;tt&gt;validates_associated&lt;/tt&gt; here. The point being I do not want to call &lt;tt&gt;save&lt;/tt&gt; or &lt;tt&gt;valid?&lt;/tt&gt; once for each model in my action, making my controller code fat.&lt;br /&gt;&lt;br /&gt;Also, &lt;tt&gt;fields_for&lt;/tt&gt; is not only just a helper method, but also a method available on your &lt;tt&gt;FormBuilder&lt;/tt&gt; as well (you know, the &lt;tt&gt;f&lt;/tt&gt; in your &lt;tt&gt;form_for&lt;/tt&gt; is an instance of a &lt;tt&gt;FormBuilder&lt;/tt&gt;. To see an example of how to call it off of a form builder, check out this &lt;a href="http://www.stephenchu.com/2008/03/introduction-to-rails-composedof.html"&gt;blog entry&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html"&gt;(back to the TOC of the params[:fu] series)&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-5909803697093839433?l=www.stephenchu.com' alt='' /&gt;&lt;/div&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.g?blogID=11933937&amp;postID=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='http://img2.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="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;form_for&lt;/span&gt; &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; f.text_field :birth_place %&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;submit&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Create&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;&amp;lt;% end %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class='scrolling_div'&gt;&lt;pre&gt;Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]&lt;br /&gt;Parameters: { "commit"             =&gt; "Create",&lt;br /&gt;              "reader"             =&gt; { "name" =&gt; "stephen chu", "birth_place" =&gt; "hong kong" },&lt;br /&gt;              "authenticity_token" =&gt; "238ba79b8282882ba01d840352616c2cc79280f0",&lt;br /&gt;              "action"             =&gt; "create",&lt;br /&gt;              "controller"         =&gt; "readers"}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;create!&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;By doing this, you eliminate the need to explicitly spell out each parameters posted, like the ugly &lt;tt&gt;Reader.new :name =&gt; params[:name], :birth_place =&gt; params[:birth_place]&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;Notice, Active Record is extremely hash-happy. Let's take a few examples and see how it is so hash-hungry (or rather, hash-happy).&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="attribute"&gt;@reader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; { 'first_name' =&amp;gt; 'stephen', 'last_name' =&amp;gt; 'chu' }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="attribute"&gt;@readers&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:conditions&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:search&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; { 'name' =&amp;gt; 'chu' }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;attributes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; { 'first_name' =&amp;gt; 'martin', 'last_name' =&amp;gt; 'fowler' }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="attribute"&gt;@reader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;update_attributes&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reader&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; { 'birth_place' =&amp;gt; 'hong kong' }&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Notice the find with condition one. No one says I cannot have a hash or &lt;tt&gt;form_for&lt;/tt&gt; that must map to a database table!&lt;br /&gt;&lt;br /&gt;All of the above will work in just one line of code, and I am sure you can find more examples as well. Remember, hashes are one of the most underrated Rails toolset to reduce code you need to write. Stay tuned on how we can optimize the use of these AR one-liners.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.stephenchu.com/2008/03/boost-your-controller-paramsfu.html"&gt;(back to the TOC of the params[:fu] series)&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-4750190244110730864?l=www.stephenchu.com' alt='' /&gt;&lt;/div&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.g?blogID=11933937&amp;postID=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='http://img2.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; "1" &lt;a href="http://www.stephenchu.com/2008/03/paramfu-1-wrap-all-relevant-attributes.html"&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; "2" &lt;a href="http://www.stephenchu.com/2008/03/paramsfu-2-put-attributes-into.html"&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; "3" &lt;a href="http://www.stephenchu.com/2008/03/paramsfu-3-using-fieldsfor-and-index.html"&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; "4" &lt;a href="http://www.stephenchu.com/2008/03/paramsfu-4-use-magical-of-ids.html"&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; "5" &lt;a href="http://www.stephenchu.com/2008/03/paramsfu-5-update-multiple-models-in.html"&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="http://railscasts.com/contest"&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; "bonus" &lt;a href="http://www.stephenchu.com/2007/12/passing-multiple-parameters-in.html"&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!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-7908492366812334297?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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='http://img2.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;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' equality. Two money objects each has amount 100 and currency 'US' 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 'id' 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's see how to beautify your Rails code.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Account&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;composed_of&lt;/span&gt; &lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:class_name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Money&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:mapping&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[%w(&lt;/span&gt;&lt;span class="string"&gt;balance amount&lt;/span&gt;&lt;span class="punct"&gt;),&lt;/span&gt; &lt;span class="punct"&gt;%w(&lt;/span&gt;&lt;span class="string"&gt;currency currency&lt;/span&gt;&lt;span class="punct"&gt;)]&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;    &lt;span class="constant"&gt;Money&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:balance&lt;/span&gt;&lt;span class="punct"&gt;],&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:currency&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Money&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;attr_reader&lt;/span&gt; &lt;span class="symbol"&gt;:amount&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:currency&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;amount&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;currency&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@amount&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="attribute"&gt;@currency&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;amount&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;currency&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;AccountsController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@account&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Account&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:account&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@account&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save&lt;/span&gt;&lt;br /&gt;      &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:notice&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;New account created...&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;      &lt;span class="ident"&gt;redirect_to&lt;/span&gt; &lt;span class="attribute"&gt;@author&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;      &lt;span class="ident"&gt;render&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;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="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="ident"&gt;form_for&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@account&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;% f.fields_for :balance, @account.balance do |ff| %&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="constant"&gt;Balance&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;  &lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&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="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;ff&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;text_field&lt;/span&gt; &lt;span class="symbol"&gt;:currency&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;  &amp;lt;/p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&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="normal"&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;  "commit" =&gt; "Create",&lt;br /&gt;  "account"   =&gt; { "balance" =&gt; { "balance"=&gt;"400", "currency"=&gt;"us" } }&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 'balance' 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 'balance' 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='http://www.stephenchu.com/2008/01/format-your-data-using-rails-composedof.html'&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't have to be. A value object can abstract just one column but contains &lt;a href="http://www.stephenchu.com/2008/05/rails-composedof-validation.html"&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's time to roll up your sleeves and clean up those value object creation code inside your controllers!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-8771421414988107389?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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='http://img2.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="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;h1&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;Show&lt;/span&gt; &lt;span class="punct"&gt;-&lt;/span&gt; &lt;span class="constant"&gt;Blog&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&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="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@blog&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;author&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;full_name&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&amp;lt;/dd&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;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="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;li&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;&amp;lt;/ul&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;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'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="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Blog&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;to_s&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;name&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;User&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;to_s&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;first_name&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt; &lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="ident"&gt;last_name&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Comment&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;to_s&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;description&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now, let's see how our view looks, 90% of the time:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;h1&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;span class="constant"&gt;Show&lt;/span&gt; &lt;span class="punct"&gt;-&lt;/span&gt; &lt;span class="constant"&gt;Blog&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&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="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@blog&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;author&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&amp;lt;/dd&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="regex"&gt;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="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;li&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;&amp;lt;/ul&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;See, it'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="http://ozmm.org/posts/try.html"&gt;&lt;tt&gt;Object#try(:method)&lt;/tt&gt;&lt;/a&gt; case:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;dd&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; @blog.author.try(:full_name) %&amp;gt;&amp;lt;/dd&amp;gt;&lt;span class="normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;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 :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-3277177935837487678?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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='http://img2.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, "I want prices to be formatted like '$2,000.00' on all rhtml's."&lt;br /&gt;&lt;br /&gt;Your programming brain then associates the word "formatted" with "presentation". 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'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 "presentation" logic outside of your pristine business domain model.&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Book&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;BookPresenter&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;SimpleDelegator&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;ActionView&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Helpers&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;NumberHelper&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;price&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;nummber_to_currency&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{__getobj__.price}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;isbn&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;...&lt;/span&gt; &lt;span class="punct"&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;BooksController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;show&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@book&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;BookPresenter&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="constant"&gt;Book&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;param&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:id&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="constant"&gt;ISBN&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; @book.isbn %&amp;gt;&lt;br /&gt;  Price: &amp;lt;%&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@book&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;price&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;&amp;lt;/p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/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="ruby"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Book&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;composed_of&lt;/span&gt; &lt;span class="symbol"&gt;:isbn&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:class_name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;ISBN&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;composed_of&lt;/span&gt; &lt;span class="symbol"&gt;:price&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:class_name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Money&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;ISBN&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;to_s&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;...&lt;/span&gt; &lt;span class="punct"&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Money&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;ActionView&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Helpers&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;NumberHelper&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;to_s&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;nummber_to_currency&lt;/span&gt; &lt;span class="attribute"&gt;@price&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;BooksController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;show&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@book&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Book&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="ident"&gt;param&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:id&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="constant"&gt;ISBN&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; @book.isbn %&amp;gt;&lt;br /&gt;  Price: &amp;lt;%&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@book&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;price&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;&amp;lt;/p&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So what'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 "first fetch a book object, then wrap it with a presenter".&lt;br /&gt;&lt;br /&gt;Some people will yell now, "but aren't you putting presentation logic into your domain model?" 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, "how can I use them beyond the tutorial way on my really complicated application?"&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;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-4685555223168998358?l=www.stephenchu.com' alt='' /&gt;&lt;/div&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.g?blogID=11933937&amp;postID=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='http://img2.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='ruby/rails'/><category scheme='http://www.blogger.com/atom/ns#' term='efficiency'/><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'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='ruby'&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 'help;' or '\h' for help. Type '\c' 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="ruby"&gt;&lt;pre&gt;&lt;span class="ident"&gt;desc&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Loads mysql command prompt&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:mysql&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\n\n&lt;/span&gt;Loading mysql in RAILS_ENV=&lt;span class="expr"&gt;#{RAILS_ENV}&lt;/span&gt;...&lt;span class="escape"&gt;\n&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;database_yml_path&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{RAILS_ROOT}&lt;/span&gt;/config/database.yml&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;database_yml&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;YAML&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;load&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;ERB&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;read&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;database_yml_path&lt;/span&gt;&lt;span class="punct"&gt;)).&lt;/span&gt;&lt;span class="ident"&gt;result&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;raise&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Could not find environment &lt;span class="expr"&gt;#{RAILS_ENV}&lt;/span&gt; in database.yml&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;database_yml&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="constant"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;config&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;database_yml&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="constant"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;username&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;config&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;username&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;password&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;config&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;password&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;database_name&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;config&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;database&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;host&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;config&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;host&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;localhost&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;port&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;config&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;port&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="number"&gt;3306&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;socket&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;config&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;socket&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;/tmp/mysql.sock&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;raise&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Failed. Setup requires a user and database for environment &lt;span class="expr"&gt;#{environment}&lt;/span&gt; in '&lt;span class="expr"&gt;#{database_yml_path}&lt;/span&gt;'.&lt;span class="escape"&gt;\n\n&lt;/span&gt;&lt;span class="expr"&gt;#{database_yml.to_yaml}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;username&lt;/span&gt; &lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="ident"&gt;database_name&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;args&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;-u &lt;span class="expr"&gt;#{username}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;args&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt; -p&lt;span class="expr"&gt;#{password}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;password&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;strip&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;empty?&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;args&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt; -h &lt;span class="expr"&gt;#{host}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;host&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;strip&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;empty?&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;args&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt; &lt;span class="expr"&gt;#{database_name}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;command&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;command&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;mysql &lt;span class="expr"&gt;#{args}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="constant"&gt;EOS&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;    Executing: &lt;span class="expr"&gt;#{command.join(' ')}&lt;/span&gt;&lt;span class="escape"&gt;\n&lt;/span&gt;&lt;/span&gt;&lt;span class="constant"&gt;&lt;br /&gt;  EOS&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;system&lt;/span&gt; &lt;span class="ident"&gt;command&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt; &lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-335689354293385992?l=www.stephenchu.com' alt='' /&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.g?blogID=11933937&amp;postID=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='http://img2.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='ruby/rails'/><category scheme='http://www.blogger.com/atom/ns#' term='efficiency'/><title type='text'>Rid of those TextMate "ctags timed out!" error</title><content type='html'>If you use TextMate for your Ruby on Rails development, you must use the ctags bundle &lt;a href="http://gerd.knops.org/?p=7"&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 "go to file and line" 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 "ctags timed out!" 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!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-6241821505112011651?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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='http://img2.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='ruby/rails'/><category scheme='http://www.blogger.com/atom/ns#' term='efficiency'/><title type='text'>Why create database migrations, when you don'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="http://en.wikipedia.org/wiki/Big_Design_Up_Front"&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="http://www.stephenchu.com/2007/03/rails-migration-pitfalls.html"&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;"Let's start by creating a database migration, in it we insert some data into the newly created tables, and learn how to do XYZ."&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'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'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 "blank", data-less state.&lt;br /&gt;&lt;pre&gt;$ cat db/migrate/001_release_one_schema.rb&lt;/pre&gt;&lt;div class='ruby'&gt;&lt;pre&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;ReleaseOneSchema&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Migration&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.up&lt;/span&gt;&lt;br /&gt;      &lt;span class="ident"&gt;create_table&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;foos&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;      &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      (... and many others ...)&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.down&lt;/span&gt;&lt;br /&gt;      (... ... ...)&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;          &lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&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='ruby'&gt;&lt;pre&gt;&lt;br /&gt;  &lt;span class="ident"&gt;namespace&lt;/span&gt; &lt;span class="symbol"&gt;:data&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;desc&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Loads a default dataset of both reference and user data into database.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:load&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt; &lt;span class="symbol"&gt;:environment&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;                    &lt;span class="symbol"&gt;:configuration&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;                    &lt;span class="symbol"&gt;:reference_data&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;                    &lt;span class="symbol"&gt;:user_data&lt;/span&gt; &lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="ident"&gt;private&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:configuration&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;      &lt;span class="constant"&gt;ENV&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;DATASET&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt; &lt;span class="punct"&gt;||=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;slim&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:reference_data&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;      &lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{RAILS_ROOT}&lt;/span&gt;/db/data/reference_data/&lt;span class="expr"&gt;#{ENV['DATASET']}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;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='ruby'&gt;&lt;pre&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@us_dollar&lt;/span&gt;  &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Currency&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;create!&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;US Dollar&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@yen&lt;/span&gt;        &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Currency&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;create!&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Yen&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@euro&lt;/span&gt;       &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Currency&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;create!&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;EURO&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;...&lt;/span&gt; &lt;span class="punct"&gt;...&lt;/span&gt; &lt;span class="punct"&gt;...&lt;/span&gt;&lt;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='ruby'&gt;&lt;pre&gt;&lt;br /&gt;  &lt;span class="ident"&gt;namespace&lt;/span&gt; &lt;span class="symbol"&gt;:data&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="ident"&gt;private&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:user_data&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;      &lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{RAILS_ROOT}&lt;/span&gt;/db/data/user_data/&lt;span class="expr"&gt;#{ENV['DATASET']}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;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='ruby'&gt;&lt;pre&gt;&lt;br /&gt;  &lt;span class="constant"&gt;Fee&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;create!&lt;/span&gt; &lt;span class="symbol"&gt;:amount&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;1_000&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:currency&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@us_dollar&lt;/span&gt;&lt;br /&gt;  &lt;span class="constant"&gt;Fee&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;create!&lt;/span&gt; &lt;span class="symbol"&gt;:amount&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;1_500&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:currency&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@yen&lt;/span&gt;&lt;br /&gt;  &lt;span class="constant"&gt;Fee&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;create!&lt;/span&gt; &lt;span class="symbol"&gt;:amount&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;2_500&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:currency&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@euro&lt;/span&gt;&lt;br /&gt;  &lt;span class="punct"&gt;...&lt;/span&gt; &lt;span class="punct"&gt;...&lt;/span&gt; &lt;span class="punct"&gt;...&lt;/span&gt;&lt;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 "legacy" 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't be too happy if you blow away their data every time you roll out a minor update or a major release... (or not?)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-744880096818773440?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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&apos;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='http://img2.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='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><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 "D" in TDD means more than just "Development"</title><content type='html'>When asked "Do you write tests?", a lot of developers these days will say "of course" 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'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 "D" 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'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'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="http://www.stephenchu.com/2007/01/use-your-test-classes-to-identify.html"&gt;are telling you that&lt;/a&gt;! Let's address each of these one by one.&lt;br /&gt;&lt;br /&gt;Slow running tests? You shouldn'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="http://www.stephenchu.com/2006/08/not-enough-objects-anti-pattern.html"&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's wrong! Heard of Law of Demeter? Even if you have, take a look at this &lt;a href="http://www.pivotalblabs.com/articles/2007/08/05/lovely-demeter-meter-maid"&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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-1816987959902497846?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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='http://img2.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="http://wiki.rubyonrails.org/rails/pages/observe_field+-+Passing+Parameters"&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="ruby"&gt;&lt;pre&gt;    &lt;span class="ident"&gt;observe_field&lt;/span&gt; &lt;span class="symbol"&gt;:target_dom_id&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class="symbol"&gt;:url&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;some_named_route_url&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class="symbol"&gt;:method&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:get&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class="symbol"&gt;:with&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Form.serializeElements($('text_field_one', 'text_field_two', ...))&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;where 'text_field_one' and 'text_field_two' 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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-7852348529582978590?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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='http://img2.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="ruby"&gt;&lt;pre&gt;  &lt;span class="constant"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;strptime&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;12/25/2007 01:00 AM EST&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;%m/%d/%Y %I:%M %p %Z&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-509453354212238911?l=www.stephenchu.com' alt='' /&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.g?blogID=11933937&amp;postID=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='http://img2.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's why those Apple billboards have no face on them...)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-1960501665882267987?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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.g?blogID=11933937&amp;postID=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='http://img2.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'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'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 "high", some say 80% is a good number, others say 90%. Some even strive for 100%.&lt;br /&gt;&lt;br /&gt;But let'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'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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-2707280037157235285?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</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&apos;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='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-7247286113451417103</id><published>2007-11-15T20:02:00.000-06:00</published><updated>2007-12-02T08:38:47.676-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'>Seeing Rails Resources Clearly</title><content type='html'>REST-fulness is probably one of the hot topics you will face when you walk into the Rails world today. One of the terms you will notice when you dig deeper into Rails REST-ful routes is "resources". In Rails, after running the scaffold resource generator, you will get a database table migration, a domain model class, a controller, and 4 CRUD views. This is the skeleton that Rails provides for you to build upon.&lt;br /&gt;&lt;br /&gt;However, a lot of Rails developers don't know how to bend this structure when it comes to non-CRUD operations (eg. search for something, reset password, login, various ajax actions etc.)&lt;br /&gt;&lt;br /&gt;I have a solution for you: &lt;i&gt;Routes Driven Development&lt;/i&gt; - Let your named routes guide you. Here's how.&lt;br /&gt;&lt;br /&gt;As an example, suppose you have a Rails app that an admin can create/remove users that access your application. You have a database table named Users, which has a password column.&lt;br /&gt;&lt;br /&gt;In your routes.rb, with a single line of:&lt;br /&gt;&lt;br /&gt;  map.resources :users&lt;br /&gt;&lt;br /&gt;You automatically get these academically REST-ful urls:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;  &lt;span class="constant"&gt;GET&lt;/span&gt;    &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;users&lt;/span&gt;          &lt;span class="comment"&gt;# =&amp;gt; users_url&lt;/span&gt;&lt;br /&gt;  &lt;span class="constant"&gt;POST&lt;/span&gt;   &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;users&lt;/span&gt;&lt;br /&gt;  &lt;span class="constant"&gt;GET&lt;/span&gt;    &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;users&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;      &lt;span class="comment"&gt;# =&amp;gt; new_user_url&lt;/span&gt;&lt;br /&gt;  &lt;span class="constant"&gt;GET&lt;/span&gt;    &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;users&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="symbol"&gt;:id&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;edit&lt;/span&gt; &lt;span class="comment"&gt;# =&amp;gt; edit_user_url&lt;/span&gt;&lt;br /&gt;  &lt;span class="constant"&gt;GET&lt;/span&gt;    &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;users&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="symbol"&gt;:id&lt;/span&gt;      &lt;span class="comment"&gt;# =&amp;gt; user_url&lt;/span&gt;&lt;br /&gt;  &lt;span class="constant"&gt;PUT&lt;/span&gt;    &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;users&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="symbol"&gt;:id&lt;/span&gt;      &lt;br /&gt;  &lt;span class="constant"&gt;DELETE&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;users&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="symbol"&gt;:id&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Then, there comes a requirement that says "From the login page, a user who forgot his password can go to a page to see directions on how to retrieve his forgotten password." We are all familiar with this concept, right?&lt;br /&gt;&lt;br /&gt;The next thing you know, the developer jumps into the code:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;UsersController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActionController&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;index...&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;show...&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;new...&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;edit...&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create...&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;update...&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;destroy...&lt;/span&gt;&lt;br /&gt;    &lt;br /&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;forgot_password&lt;/span&gt;&lt;br /&gt;      &lt;span class="comment"&gt;# new code here&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;    &lt;br /&gt;And in your routes.rb:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;  &lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;resouces&lt;/span&gt; &lt;span class="symbol"&gt;:users&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;forgot_password_url&lt;/span&gt;    &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;/users/forgot_password&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:controller&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;users&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;forgot_password&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:method&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:get&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;        &lt;br /&gt;Oops. And you just made a wrong move in building upon the Rails skeleton. You put the action in the wrong controller.&lt;br /&gt;&lt;br /&gt;Most developers make the assumption that a Rails "resource" is synonymous to a database table. This observation is made because this is how most people would start their Rails application, as in our case our Users concept. With this in mind, since password is a column rather than a table in the database, it should not have its own controller, and thus no model, nor view, etc. Still, we got to jam the code somewhere, and therefore the Users controller, the table on which it belongs. But this observation of one-table, one-controller is not always true.&lt;br /&gt;&lt;br /&gt;Instead, I would keep my users controller free from password related operations and do this:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;PasswordsController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActionController&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;forgot&lt;/span&gt;&lt;br /&gt;      &lt;span class="comment"&gt;# new code here&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;resource&lt;/span&gt; &lt;span class="symbol"&gt;:password&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:member&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="symbol"&gt;:forgot&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:get&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;  &lt;br /&gt;This gives me an url of:&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;  &lt;span class="constant"&gt;GET&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;passwords&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;forgot&lt;/span&gt;    &lt;span class="comment"&gt;# =&amp;gt; Which gives you forgot_password_url&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;My named route of forgot_password_url is telling me that I am doing the right thing, because it is extremely readable. Had I not been creating a new passwords controller and put the action in my users controller, using the same technique I used to define my named route, I would end up with a not only incorrect but also nasty forgot_password_user_url(:id) by the naming convention (&amp;lt;action&amp;gt;_&amp;lt;controller&amp;gt;_url). Definitely not what I want to pass to any link_to() or button_to().&lt;br /&gt;&lt;br /&gt;If you realize, you just created a controller (and its view) with no database table backing it what-so-ever. It doesn't even have a model class. But then again, how much more complicated can it be when the customer is asking for a page that shows directions?&lt;br /&gt;&lt;br /&gt;By putting actions in the wrong controller, your controller actions will lack cohesiveness (i.e. actions don't operate on the right thing) which inhibits sharing, you will also run into problems in sharing views (pages, partials, rjs, helpers) like "why am I rendering a partial that is in an obscure folder?", etc. In short, a lot of bad code.&lt;br /&gt;&lt;br /&gt;Using this routes driven technique to drive your actions and controllers, can you identify which action/controller combination is correct for a 'Login' button?&lt;br /&gt;&lt;br /&gt;&lt;div class="ruby"&gt;&lt;pre&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="ident"&gt;create_session_url&lt;/span&gt;  &lt;span class="comment"&gt;# =&amp;gt; :controller =&amp;gt; 'sessions', :action =&amp;gt; 'create', :method =&amp;gt; :post&lt;/span&gt;&lt;br /&gt;&lt;span class="number"&gt;2&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="ident"&gt;login_user_url&lt;/span&gt;      &lt;span class="comment"&gt;# =&amp;gt; :controller =&amp;gt; 'users',    :action =&amp;gt; 'login',  :method =&amp;gt; :post&lt;/span&gt;&lt;br /&gt;&lt;span class="number"&gt;3&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="ident"&gt;login_url&lt;/span&gt;           &lt;span class="comment"&gt;# =&amp;gt; map.login '/login', :controller =&amp;gt; 'whatever', :action =&amp;gt; 'anything', :method =&amp;gt; :post&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The true answer is, it doesn't really matter, as long as your controller is responsible for the correct "resource", and the named route is screamingly readable to anyone using it.. No matter which controller you put your actions in, you can always fall back to creating a named route for it using &lt;tt&gt;map.connect&lt;/tt&gt; like option (3) above. The bottom line is, Rails "resources" might not be what you and others think it is.&lt;br /&gt;&lt;br /&gt;As homework, go read more about &lt;a href='http://api.rubyonrails.org/classes/ActionController/Resources.html'&gt;Rails resources&lt;/a&gt;. Not only can you specify :member, but learn how to use other options like :new, :collection, and nested routes. Also, there is also a singular "resource" method and a plural "resources" method. Get familiar with them. This rdoc page is IMHO pretty poorly documented, but hopefully this article gives you a reason to explore them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-7247286113451417103?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/7247286113451417103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=7247286113451417103' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/7247286113451417103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/7247286113451417103'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/11/seeing-rails-resources-clearly.html' title='Seeing Rails Resources Clearly'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-7987819950741127620</id><published>2007-03-20T20:27:00.000-05:00</published><updated>2007-11-27T23:11:38.274-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='humor'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby/rails'/><title type='text'>Extreme Rails programming</title><content type='html'>What is considered as extreme Rails programming? How do you know you are coding in a zone where no man has gone before you?&lt;br /&gt;&lt;br /&gt;That's the feeling of you standing on top of Mount Everest, with the world underneath your feet. That's the feeling when history is in the making before your eyes.&lt;br /&gt;&lt;br /&gt;So how do you get there?&lt;br /&gt;&lt;br /&gt;Got dumped by your girlfriend because you love Rails more than her? Uh-uh.&lt;br /&gt;&lt;br /&gt;Solving a problem no one has thought of before in Ruby on Rails? Negative.&lt;br /&gt;&lt;br /&gt;Writing Rails code on the moon? Nah.&lt;br /&gt;&lt;br /&gt;Today I looked over a programming pair's shoulders, and I immediately realized that they were in a programming zone where no one ever has gone before them. So I took a picture as proof.&lt;br /&gt;&lt;br /&gt;And it is...&lt;br /&gt;&lt;br /&gt;Writing a Rails app in &lt;strong&gt;&lt;a href="http://en.wikipedia.org/wiki/Vim_%28text_editor%29"&gt;vim&lt;/a&gt;&lt;/strong&gt;, with a &lt;strong&gt;&lt;a href="http://en.wikipedia.org/wiki/Dvorak_Simplified_Keyboard"&gt;Dvorak&lt;/a&gt;&lt;/strong&gt; keyboard.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_OeQDslkBljg/RgCPs5EGaiI/AAAAAAAAAAM/MVjxnO_ExL8/s1600-h/Dvorak_VIM.JPG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: clear; cursor: pointer;" src="http://bp3.blogger.com/_OeQDslkBljg/RgCPs5EGaiI/AAAAAAAAAAM/MVjxnO_ExL8/s320/Dvorak_VIM.JPG" alt="" id="BLOGGER_PHOTO_ID_5044189584049990178" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is real, and it's priceless.&lt;br /&gt;&lt;br /&gt;So do you know what key you press to yank a line?&lt;br /&gt;&lt;br /&gt;(Thanks to &lt;a href="http://muness.blogspot.com/"&gt;Muness Alrubaie&lt;/a&gt; and &lt;a href="http://davidvollbracht.com/"&gt;David Vollbracht&lt;/a&gt; for this shot)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-7987819950741127620?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/7987819950741127620/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=7987819950741127620' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/7987819950741127620'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/7987819950741127620'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/03/extreme-rails-programming.html' title='Extreme Rails programming'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_OeQDslkBljg/RgCPs5EGaiI/AAAAAAAAAAM/MVjxnO_ExL8/s72-c/Dvorak_VIM.JPG' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-776182307212320935</id><published>2007-03-06T23:27:00.000-06:00</published><updated>2007-11-27T23:11:38.275-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby/rails'/><title type='text'>Rails migration pitfalls</title><content type='html'>When we create a migration, we are essentially creating a "delta" SQL script, whose intent is to change the state of the database at a given time, including its schema and its data, from point A to point B. Rails migration scripts even allow you to rollback from B to A. A lot of developers got the delta of the schema part right, because it is the most intriguing part, but they fail to recognize the data part in their migrations.&lt;br /&gt;&lt;br /&gt;Take a look at the following migration:&lt;br /&gt;&lt;br /&gt;&lt;div class='ruby'&gt;&lt;span class='keyword' style='margin: 0px'&gt;class &lt;/span&gt;&lt;span class='class' style='margin: 0px'&gt;MoveColumnFooFromTableAToTableB&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;&amp;lt;&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;ActiveRecord&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;::&lt;/span&gt;&lt;span class='constant' style='margin: 0px'&gt;Migration&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;self.up&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;remove_column&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:table_a&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;add_column&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:table_b&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:string&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;self.down&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;add_column&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:table_a&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:string&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;remove_column&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:table_b&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This migration drops column "foo" from Table A, and adds column "foo" to Table B. From the schema point of view, the migration achieves what it's supposed to do - moving a column. But this delta script is flawed from the data point of view.&lt;br /&gt;&lt;br /&gt;Imaging your application is at 1.0, users start inserting 20MM rows into your Table A. This migration script is part of your release 1.5 upgrade. You run this script against your table A. Poof! The column is moved, but you just lost *all* of your data in the column "foo" forever. Table B now has a "foo" column with no data in it. Things went wrong, so you rollback, right? Try it. Still, your Table A now contains a "foo" column with all NULL values in it. The data are gone.&lt;br /&gt;&lt;br /&gt;Worse yet, your boss is standing behind you, giving you 15 minutes to fix the whole mess. "Database migration sucks...", you mumbled.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;This migration fails to migrate the data from point A to point B.&lt;/em&gt; So, what should developers have done differently? Well, here's one way to do it:&lt;br /&gt;&lt;br /&gt;&lt;div class='ruby'&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;self.up&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;add_column&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:table_b&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:string&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;execute&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&amp;quot;&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;UPDATE table_b INNER JOIN table_a ON table_b.id = table_a.id SET table_b.foo = table_a.foo&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;&amp;quot;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;remove_column&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:table_a&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;self.down&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;add_column&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:table_a&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:string&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;execute&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&amp;quot;&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;UPDATE table_a INNER JOIN table_b ON table_a.id = table_b.id SET table_a.foo = table_b.foo&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;&amp;quot;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;remove_column&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:table_b&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now with this delta script, which accounts for data migration as well, will do what it is intended to migrate: schema and data.&lt;br /&gt;&lt;br /&gt;Here is another gotcha situation:&lt;br /&gt;&lt;br /&gt;&lt;div class='ruby'&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;self.up&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;add_column&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:table_a&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:null&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;true&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;When you have a table that contains rows in it, like our 20MM row table, after this migration, the 20MM rows will contain NULL in column "foo". Your database will complain null column constraint violated after this migration.&lt;br /&gt;&lt;br /&gt;So be careful when you are performing these migrations. My advice is, do a sanity check on all your migrations by running them against a database with tables populated with data. It actually may not be a bad idea to run a &lt;a href="http://en.wikipedia.org/wiki/Continuous_integration"&gt;CI&lt;/a&gt; build on any migration check-ins, to tick off all migrations, against a database full of data, if your migrations are meant to migrate data.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-776182307212320935?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/776182307212320935/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=776182307212320935' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/776182307212320935'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/776182307212320935'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/03/rails-migration-pitfalls.html' title='Rails migration pitfalls'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-3824737920239929272</id><published>2007-03-06T22:46:00.000-06:00</published><updated>2007-11-27T23:11:38.275-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby/rails'/><title type='text'>Enhance Array#collect to become magical</title><content type='html'>Do you do this often?&lt;br /&gt;&lt;br /&gt;&lt;div class='ruby'&gt;&lt;span class='ident' style='margin: 0px'&gt;customers&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;collect&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;{&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;|&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;customer&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;|&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;customer&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;name&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class='ident' style='margin: 0px'&gt;customers&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;collect&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;{&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;|&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;customer&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;|&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;[&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;customer&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;name&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;customer&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;id&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;]&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;}&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Array#collect is indeed very powerful. But I still find myself to repeatedly declare a variable to keep a reference of the elements I am iterating. I could care less if I call it |customer| or |c|.&lt;br /&gt;&lt;br /&gt;What if I enhance the Array class to do the following:&lt;br /&gt;&lt;br /&gt;&lt;div class='ruby'&gt;&lt;span class='ident' style='margin: 0px'&gt;customers&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;collect_name&lt;/span&gt;&lt;br /&gt;&lt;span class='ident' style='margin: 0px'&gt;customers&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;collect_name_and_id&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;DRY-ness... Inspiration came from ActiveRecord's magic #find method.&lt;br /&gt;&lt;br /&gt;&lt;div class='ruby'&gt;&lt;span class='keyword' style='margin: 0px'&gt;class &lt;/span&gt;&lt;span class='class' style='margin: 0px'&gt;Array&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;method_missing&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;method_sym&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;*&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;args&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;if&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;collect_by_method?&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;method_sym&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;attributes&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;fetch_collect_attributes&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;method_sym&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;if&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;attributes&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;size&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;==&lt;/span&gt;&amp;nbsp;&lt;span class='number' style='margin: 0px'&gt;1&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;block&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;lambda&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;{&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;|&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;element&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;|&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;element&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;send&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;attributes&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;first&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;else&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;block&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;lambda&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;{&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;|&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;element&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;|&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;attributes&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;collect&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;{&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;|&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;attribute&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;|&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;element&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;send&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:&amp;quot;&lt;/span&gt;&lt;span class='expr' style='margin: 0px'&gt;#{attribute}&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;&amp;quot;&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;}&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;self&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;collect&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&amp;amp;&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;block&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;else&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;super&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;private&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;collect_by_method?&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;method_sym&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;method_sym&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;to_s&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=~&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;/&lt;/span&gt;&lt;span class='regex' style='margin: 0px'&gt;^collect_&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;/&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;fetch_collect_attributes&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;method_sym&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;attributes&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;method_sym&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;to_s&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;gsub&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(/&lt;/span&gt;&lt;span class='regex' style='margin: 0px'&gt;collect_&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;/,&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;'&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;').&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;split&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(/&lt;/span&gt;&lt;span class='regex' style='margin: 0px'&gt;_and_&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;/)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;raise&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;ArgumentError&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;&amp;quot;&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Array#collect_* requires at least one method name after it. eg. #collect_id&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;&amp;quot;&lt;/span&gt;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;if&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;attributes&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;empty?&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;attributes&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And of course, code is no good without tests:&lt;br /&gt;&lt;br /&gt;&lt;div class='ruby'&gt;&lt;span class='keyword' style='margin: 0px'&gt;class &lt;/span&gt;&lt;span class='class' style='margin: 0px'&gt;ArrayTest&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;&amp;lt;&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;Test&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;::&lt;/span&gt;&lt;span class='constant' style='margin: 0px'&gt;Unit&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;::&lt;/span&gt;&lt;span class='constant' style='margin: 0px'&gt;TestCase&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_collect_raises_exception_with_no_parameter&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;assert_raise&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;ArgumentError&lt;/span&gt;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;[].&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;collect_&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_collect_with_one_parameter&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;array&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;[]&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;array&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;&amp;lt;&amp;lt;&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;TestStruct&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;new&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:id&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;span class='number' style='margin: 0px'&gt;1&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;'&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Foo 1&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;')&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;array&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;&amp;lt;&amp;lt;&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;TestStruct&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;new&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:id&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;span class='number' style='margin: 0px'&gt;2&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;'&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Foo 2&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;')&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;assert_equal&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;['&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Foo 1&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;',&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;'&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Foo 2&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;'],&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;array&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;collect_foo&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_collect_with_multiple_parameter&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;array&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;[]&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;array&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;&amp;lt;&amp;lt;&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;TestStruct&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;new&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:id&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;span class='number' style='margin: 0px'&gt;1&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;'&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Foo 1&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;',&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:bar&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;'&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Bar 1&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;')&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;array&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;&amp;lt;&amp;lt;&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;TestStruct&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;new&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:id&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;span class='number' style='margin: 0px'&gt;2&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;,&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:foo&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;'&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Foo 2&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;',&lt;/span&gt;&amp;nbsp;&lt;span class='symbol' style='margin: 0px'&gt;:bar&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;'&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Bar 2&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;')&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;assert_equal&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;[&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;['&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Foo 1&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;',&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;'&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Bar 1&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;'],&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;['&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Foo 2&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;',&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;'&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;Bar 2&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;']&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;],&lt;/span&gt;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;array&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;collect_foo_and_bar&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_collect_does_not_interfere_default_method_missing&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;assert_raise&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;NoMethodError&lt;/span&gt;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;[].&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;foo&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-3824737920239929272?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/3824737920239929272/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=3824737920239929272' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/3824737920239929272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/3824737920239929272'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/03/enhance-arraycollect-to-become-magical.html' title='Enhance Array#collect to become magical'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-7775433121088423680</id><published>2007-01-13T21:28:00.000-06:00</published><updated>2007-11-27T23:11:38.275-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby/rails'/><title type='text'>Introduction video to ActiveResources RESTful Rails controller actions</title><content type='html'>The world is talking about &lt;a href="http://tomayko.com/articles/2004/12/12/rest-to-my-wife"&gt;REST&lt;/a&gt;. Just when you are reading voraciously about Ruby and Rails, what would be nicer than having DHH to tell you what the next version of Rails' vision is regarding the whole REST idea?&lt;br /&gt;&lt;br /&gt;Consider how you would write 4 named routes for the following ActiveController actions today:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;POST /people/create&lt;br /&gt; GET /people/show/1&lt;br /&gt;POST /people/update/1&lt;br /&gt;POST /people/destroy/1&lt;br /&gt;&lt;br /&gt;ActionController::Routing::Routes.draw do |map|&lt;br /&gt;  map.with_options(:controller =&gt; 'people') do |people|&lt;br /&gt;    people.connect 'people/create', :action =&gt; 'create'&lt;br /&gt;    people.connect 'people/show/:id', :action =&gt; 'show'&lt;br /&gt;    people.connect 'people/update/:id', :action =&gt; 'update'&lt;br /&gt;    people.connect 'people/destroy/:id', :action =&gt; 'destroy'&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;How many times have you done CRUD on a model? With ActiveResources, all your need is 1 line of route setting:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;  POST /people&lt;br /&gt;   GET /people/1&lt;br /&gt;   PUT /people/1&lt;br /&gt;DELETE /people/1&lt;br /&gt;&lt;br /&gt;ActionController::Routing::Routes.draw do |map|&lt;br /&gt;  map.resources :person&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Convention over configuration can buy you a lot of coding. Rails does just that here (and more). Enjoy.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://link.brightcove.com/services/link/bcpid292659793/bclid293829524/bctid292663317"&gt;Presentation&lt;/a&gt; (&lt;a href="http://www.loudthinking.com/lt-files/worldofresources.pdf"&gt;pdf&lt;/a&gt;)&lt;br /&gt;There is also a ActiveResources &lt;a href="http://topfunky.com/clients/peepcode/REST-cheatsheet.pdf"&gt;cheatsheet&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-7775433121088423680?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/7775433121088423680/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=7775433121088423680' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/7775433121088423680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/7775433121088423680'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/01/introduction-video-to-activeresources.html' title='Introduction video to ActiveResources RESTful Rails controller actions'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-116804427341604363</id><published>2007-01-05T18:44:00.000-06:00</published><updated>2007-01-05T18:48:43.896-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='efficiency'/><title type='text'>Migrate Thunderbird mailboxes from Windows to Mac</title><content type='html'>When I moved from my Windows machine to a Mac I had a bunch of mail in my Thunderbird that I wanted to migrate over. These are emails going back to the days of the war in online email services. As it turns out it isn't as easy as copying the mailboxes over from Windows to my shiny Mac. MozBackup didn't work for me because it is only for Windows-to-Windows. I found a solution after some soul searching from Google... Turns out it's almost as easy as copying over your mailboxes...&lt;p&gt;&lt;/p&gt;1) Install the T-Bird extension &lt;a href='http://nic-nac-project.de/~kaosmos/mboximport-en.html'&gt;MBoxInput&lt;/a&gt;&lt;br&gt;This is the beauty in open standard. Mailboxes in T-Bird are stored in MBox standard, therefore they are portable to whichever email client you wish to use. Install this onto your Mac T-Bird.&lt;p&gt;&lt;/p&gt;2) Copy your mailboxes from your Windows T-Bird profile folder to your Mac&lt;br&gt;They are in your %APPDATA%\Thunderbird\Profiles folder. I used my good-ole USB drive...&lt;p&gt;&lt;/p&gt;3) On your Mac, right click on your Folders menu, there will be a "Import/Export" item. Select it, and then choose the mailbox in your Profile folder/sub-folder you want to import (eg. Inbox)&lt;br&gt;If you have multiple email accounts set up, you can right click on the account before selecting Import/Export. The mail will then be imported into the appropriate account as a subfolder.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-116804427341604363?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/116804427341604363/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=116804427341604363' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/116804427341604363'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/116804427341604363'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/01/migrate-thunderbird-mailboxes-from.html' title='Migrate Thunderbird mailboxes from Windows to 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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-116780807290379929</id><published>2007-01-03T01:06:00.000-06:00</published><updated>2007-01-03T01:09:17.560-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='humor'/><title type='text'>Canadians love iPod...</title><content type='html'>I mean, &lt;a href="http://maps.google.com/maps?f=q&amp;amp;hl=en&amp;amp;q=medicine+hat,+alberta&amp;amp;ie=UTF8&amp;amp;z=16&amp;amp;ll=50.010083,-110.113006&amp;amp;spn=0.009432,0.026951&amp;amp;t=k&amp;amp;om=1"&gt;you gotta see this&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Read the story &lt;a href="http://www.smh.com.au/news/web/grans-canyon-sensation/2006/11/13/1163266420400.html"&gt;"the iPod-wearing native American"&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-116780807290379929?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/116780807290379929/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=116780807290379929' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/116780807290379929'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/116780807290379929'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/01/canadians-love-ipod.html' title='Canadians love iPod...'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-116694598325420630</id><published>2007-01-01T23:20:00.000-06:00</published><updated>2007-11-27T23:11:38.275-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'>Use your test classes to identify smelly code</title><content type='html'>How can you tell if you have smelly code? Your test classes actually can tell you a lot about it. Here are a few unit test anti-patterns:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Big test class&lt;/span&gt;&lt;br /&gt;Hopefully you have one unit test class per class/module/whatever-that-contains-behaviors you write code for. When your test class starts becoming unwieldily difficult to read and understand, your Class Under Test* is doing too much (i.e. &lt;a href="http://en.wikipedia.org/wiki/God_object"&gt;God Object&lt;/a&gt;). So what is difficult to read? It is different for everyone. For me, when your unit test class stretches more than 4-5 page scrolls, it is starting to become too big. Think small, think manageable.&lt;br /&gt;&lt;br /&gt;*By Class Under Test, I mean the class (e.g. class Laptop) that is to be tested by the test class (e.g. class LaptopTest &amp;lt; Test::Unit::TestCase).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Prefix test method names for categorization purposes&lt;/span&gt;&lt;br /&gt;When you have to categorize your test methods in your test class by prefixing some text for grouping to improve readability, it is an indication your Class Under Test is doing too much:&lt;br /&gt;&lt;br /&gt;&lt;pre class='ruby'&gt;&lt;span class='keyword' style='margin: 0px'&gt;class &lt;/span&gt;&lt;span class='class' style='margin: 0px'&gt;EBayChristmasShopperTest&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;&amp;lt;&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;Test&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;::&lt;/span&gt;&lt;span class='constant' style='margin: 0px'&gt;Unit&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;::&lt;/span&gt;&lt;span class='constant' style='margin: 0px'&gt;TestCase&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_browse_fetch_pricing_details_for_item...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_browse_froogle_compare_item_prices...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_browse_amazon_compare_item_prices...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_auction_overbid_item...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_auction_underbid_item...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_payment_paypal_purchase_item...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_payment_paypal_buy_it_now...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_payment_credit_card_purchase_item...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_payment_credit_card_one_click_checkout...&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Instead of one EBayChristmasShopper object doing all of these, how about have Shopper to delegate some of these functionalities (browsing, auction, etc.) to other smaller objects (like ItemBrowser, Auctioneer)? This way you can write more well-intended unit tests for ItemBrowser and Auctioneer, and significantly simplify our EBayChristmasShopper class.&lt;br /&gt;&lt;br /&gt;To me, one good way to categorize your unit test methods is to group them by the names of your public methods:&lt;br /&gt;&lt;br /&gt;&lt;pre class='ruby'&gt;&lt;span class='keyword' style='margin: 0px'&gt;class &lt;/span&gt;&lt;span class='class' style='margin: 0px'&gt;IPodTest&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;&amp;lt;&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;Test&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;::&lt;/span&gt;&lt;span class='constant' style='margin: 0px'&gt;Unit&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;::&lt;/span&gt;&lt;span class='constant' style='margin: 0px'&gt;TestCase&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_prev_skips_to_previous_song...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_prev_and_hold_rewinds_during_playback...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_menu_displays_song_list_during_playback...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_play_starts_playback...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_play_pauses_playback_when_playing...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_play_resumes_playback_when_paused...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_next_skips_to_next_song...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_next_and_hold_fast_forwards_during_playback...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class='keyword' style='margin: 0px'&gt;class &lt;/span&gt;&lt;span class='class' style='margin: 0px'&gt;IPod&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;prev...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;menu...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;play...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;next...&lt;/span&gt;&lt;br /&gt;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;This way, each of your public methods can have a group of unit tests responsible for its behavior. In addition, it's easy to find out which method is missing tests (psssst....). But most importantly, you can easily see and logically group functionalities together, and refactor them into new classes should your Class Under Test becomes hideous to look at.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Mocks/Stubs frenzy&lt;/span&gt;&lt;br /&gt;All too often, developers try to write code to make things work the way they want, at the expense of the complicating the fundamentals of object-oriented programming: object-to-object communication. Sure, the code works, but the tests tell you a different picture about how you get it to work. Take a look at the following example:&lt;br /&gt;&lt;br /&gt;&lt;pre class='ruby'&gt;&lt;span class='keyword' style='margin: 0px'&gt;class &lt;/span&gt;&lt;span class='class' style='margin: 0px'&gt;DriverTest&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;&amp;lt;&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;Test&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;::&lt;/span&gt;&lt;span class='constant' style='margin: 0px'&gt;Unit&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;::&lt;/span&gt;&lt;span class='constant' style='margin: 0px'&gt;TestCase&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;def &lt;/span&gt;&lt;span class='method' style='margin: 0px'&gt;test_drive_vehicle_forward_moves_appropriate_car_parts&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;car&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;Car&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;new&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;car&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;radio&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;expects&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:off&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;car&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;clutch&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;expects&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:shift_to_drive&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;car&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;acceleration_paddle&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;expects&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:push&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;car&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;steering_wheel&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;expects&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:turn&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;).&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;with&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:straight&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;car&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;ignition&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;expects&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:engine_on&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;car&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;timing_belt&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;expects&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:run&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;car&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;muffler&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;expects&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:stinks&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;car&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;battery&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;expects&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='symbol' style='margin: 0px'&gt;:charging&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;driver&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&lt;/span&gt;&amp;nbsp;&lt;span class='constant' style='margin: 0px'&gt;Driver&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;new&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;(&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;car&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;driver&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;driving_directions&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;=&lt;/span&gt;&amp;nbsp;&lt;span class='punct' style='margin: 0px'&gt;'&lt;/span&gt;&lt;span class='string' style='margin: 0px'&gt;FORWARD&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;'&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class='ident' style='margin: 0px'&gt;driver&lt;/span&gt;&lt;span class='punct' style='margin: 0px'&gt;.&lt;/span&gt;&lt;span class='ident' style='margin: 0px'&gt;drive&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class='keyword' style='margin: 0px'&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Did I lose you at the mock call for my car radio? Driving forward, in my mind, only needs half of those moving parts (ignition, clutch, steering wheel, acceleration). Others? I don't care (at least not when I only want to move the car forward as a driver). Sure the code works, but an overdosage of Mocks and Stubs is a good indicator that you are breaking encapsulation and/or coupling your class with too many other objects.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Long test methods&lt;/span&gt;&lt;br /&gt;This indicates your Class Under Test requires a lot of set up in order to be useful, or your object is very coupled with the rest of the world (remember someone once said decoupling is good?), or your Class Under Test is very long and procedural. Remember the test_drive_vehicle_forward above?&lt;br /&gt;&lt;br /&gt;Reduce the size of your test methods by re-thinking your object-object communication path. Maybe introducing an additional object to do all the dirty set up work would help decoupling the Class Under Test from the rest of the world immensely. Maybe this is a common object-coupling design problem that someone already has a design pattern to solve it succinctly, and all you need is google for it. There are always ways around it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-116694598325420630?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/116694598325420630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=116694598325420630' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/116694598325420630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/116694598325420630'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2007/01/use-your-test-classes-to-identify.html' title='Use your test classes to identify smelly code'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-116604914925333317</id><published>2006-12-13T16:27:00.000-06:00</published><updated>2007-11-27T23:11:38.276-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby/rails'/><title type='text'>Specifying ruby 1.8.4 to install using DarwinPorts</title><content type='html'>Recently I had to install ruby 1.8.4 onto my Mac. I have always been using DarwinPorts (v1.320 ) and it has served me well. But this time it is giving a little trouble. On the port tree (port list ruby) DarwinPorts gives me ruby 1.8.5, which as we all know breaks script/breakpointer for now. It does not give me the option to specify a previous ruby version to do port install, as if 1.8.5 is the only ruby that it recognizes.&lt;br /&gt;&lt;br /&gt;I suppose you could compile ruby from source by hand, but we developers are just hard heads and refuse to give in...&lt;br /&gt;&lt;br /&gt;Fortunately, there is a solution. (Thanks to Marc Selig)&lt;br /&gt;&lt;br /&gt;1) Find out the svn revision number of the Portfile that has 1.8.4 by looking at:&lt;br /&gt;http://trac.macosforge.org/projects/macports/log/trunk/dports/lang/ruby/Portfile&lt;br /&gt;In my case it is 16709.&lt;br /&gt;&lt;br /&gt;2) Set up a local port repository. In the file /opt/local/etc/ports/sources.conf, add the line:&lt;br /&gt;file:///Users/Shared/dports&lt;br /&gt;&lt;br /&gt;3) Install the port into your local repository.&lt;br /&gt;cd /Users/Shared/dports&lt;br /&gt;svn co --revision 16709 http://svn.macports.org/repository/macports/trunk/dports/lang/ruby/ lang/ruby/&lt;br /&gt;portindex /Users/Shared/dports&lt;br /&gt;&lt;br /&gt;4) Now you should be able to see ruby @1.8.4 in addition to @1.8.5 by running "port list". Run "port install ruby @1.8.4" and you are up and running.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-116604914925333317?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/116604914925333317/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=116604914925333317' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/116604914925333317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/116604914925333317'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/12/specifying-ruby-184-to-install-using.html' title='Specifying ruby 1.8.4 to install using DarwinPorts'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-115829538805693816</id><published>2006-09-24T23:42:00.000-05:00</published><updated>2007-11-27T23:11:38.276-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby/rails'/><category scheme='http://www.blogger.com/atom/ns#' term='efficiency'/><title type='text'>Enhance your irb experience on Windows</title><content type='html'>You can extend the functionality of your irb on Windows by creating a file .irbrc in your %userprofile% directory. Put some Ruby code in there, and they will get loaded when you launch your irb, as well as your script/console. I have put in some code snippets in there to aid my irb experience.&lt;br /&gt;&lt;br /&gt;(You may have issues trying to create a file with its name beginning with a period on Windows Explorer. Try to run the command "notepad .irbrc" in your cmd.exe)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Tab Completion&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;require 'irb/completion'&lt;br /&gt;ARGV.concat [ "--readline", "--prompt-mode", "simple" ]&lt;/pre&gt;Do you miss intellisense? If you do this, then in irb, type in like "text".s[tab][tab] will give you a list of methods in String that starts with s.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ruby Interactive (ri) reference in irb&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;def ri(*names)&lt;br /&gt;system(%{echo | ri #{names.collect { |name| name.to_s }.join(" ")}})&lt;br /&gt;end&lt;/pre&gt;Then you can type ri 'String.strip' to see the Ruby index. The argument has to be a string though.&lt;br /&gt;&lt;br /&gt;Remember, on a Windows machine, you have to put "echo | " in front of your ri line or else your irb will just return 'false'. I know it's counterintuitive that it isn't at the end, but it works.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Clear screen&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;def cls&lt;br /&gt;system('cls')&lt;br /&gt;end&lt;/pre&gt;I always wanted clear screen...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Console messed up with long irb lines&lt;/span&gt;&lt;br /&gt;If you have a line of Ruby code that is way too long, and you want to go back and make changes, on Windows console it is not very friendly as it will almost guarentee to mess up your edits and disorient your typings. The only way I Googled on how to fix this is to take away the "--readline" switch from the Tab Completion tip above, and replace it with "--noreadline". But then you lose Tab Completion of course. I haven't found a workaround for it yet, but in the mean time I will happily just use Cygwin bash shell =)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-115829538805693816?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/115829538805693816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=115829538805693816' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115829538805693816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115829538805693816'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/09/enhance-your-irb-experience-on-windows.html' title='Enhance your irb experience on Windows'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-115820473657255745</id><published>2006-09-13T21:25:00.000-05:00</published><updated>2007-11-27T23:11:38.276-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'>Ruby constructor dependency injection... or not?</title><content type='html'>Dependency injection has proven to be something a black belt unit tester must know about if you are serious about unit testing. If you have written some sort of unit tests, would you be jealous if I tell you for some of us, running all these unit tests takes &lt;a href="http://jayfields.blogspot.com/2006/09/rubyrails-unit-testing-in-less-than-1.html"&gt;sub-1 second&lt;/a&gt;? In C# and Java, actively practising Dependency Injection makes mocking and stubbing out dependencies much easier, and thus tests become easy to write, and run fast because they do not need to make time consuming calls. In fact, constructor injection is one of my favourite design techniques:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Consolas; font-size: 11pt; color: black; background: white;"&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; &lt;span style="color: teal;"&gt;Laptop&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;private&lt;/span&gt; IBattery battery;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; Laptop(IBattery battery)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;this&lt;/span&gt;.battery = battery;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; PowerOn()&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (battery.Meter &amp;gt;= 10)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;// Booting Vista...&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Then to unit test Laptop, you could use NMock like such:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Consolas; font-size: 11pt; color: black; background: white;"&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; [Test]&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; PowerOnFailsWhenBatteryIsTooLow()&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; mocks = &lt;span style="color: blue;"&gt;new&lt;/span&gt; Mockery();&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; mockBattery = mocks.NewMock&amp;lt;IBattery&amp;gt;();&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Expect.Once.On(mockBattery).Method(&lt;span style="color: maroon;"&gt;"Meter"&lt;/span&gt;).Will(Return.Value(9));&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: teal;"&gt;Laptop&lt;/span&gt; laptop = &lt;span style="color: blue;"&gt;new&lt;/span&gt; &lt;span style="color: teal;"&gt;Laptop&lt;/span&gt;(mockBattery);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; laptop.PowerOn();&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Assert.AreEqual(&lt;span style="color: maroon;"&gt;"Off"&lt;/span&gt;, laptop.PowerStatus);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; mocks.VerifyAllExpectationsHaveBeenMet();&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;It may not be worth it to mock out Battery, but think about a lengthy web service class.&lt;br /&gt;&lt;br /&gt;That's all true in C# and Java. Now in Ruby, I don't even need to constructor inject my Battery instance to unit test my Laptop class. I can just as easily unit test my Laptop class without having to inject my mock Battery:&lt;br /&gt;&lt;br /&gt;&lt;pre class="ruby"&gt;&lt;span class="keyword" style="margin: 0px;"&gt;class &lt;/span&gt;&lt;span class="class" style="margin: 0px;"&gt;Laptop&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword" style="margin: 0px;"&gt;def &lt;/span&gt;&lt;span class="method" style="margin: 0px;"&gt;initialize&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute" style="margin: 0px;"&gt;@battery&lt;/span&gt; &lt;span class="punct" style="margin: 0px;"&gt;=&lt;/span&gt; &lt;span class="constant" style="margin: 0px;"&gt;Battery&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;.&lt;/span&gt;&lt;span class="ident" style="margin: 0px;"&gt;new&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword" style="margin: 0px;"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword" style="margin: 0px;"&gt;def &lt;/span&gt;&lt;span class="method" style="margin: 0px;"&gt;power_on&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword" style="margin: 0px;"&gt;if&lt;/span&gt; &lt;span class="attribute" style="margin: 0px;"&gt;@battery&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;.&lt;/span&gt;&lt;span class="ident" style="margin: 0px;"&gt;meter&lt;/span&gt; &lt;span class="punct" style="margin: 0px;"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="number" style="margin: 0px;"&gt;10&lt;/span&gt;&lt;br /&gt;      &lt;span class="comment" style="margin: 0px;"&gt;# Booting Max OS X&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword" style="margin: 0px;"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword" style="margin: 0px;"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword" style="margin: 0px;"&gt;def &lt;/span&gt;&lt;span class="method" style="margin: 0px;"&gt;power_status&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword" style="margin: 0px;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword" style="margin: 0px;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword" style="margin: 0px;"&gt;class &lt;/span&gt;&lt;span class="class" style="margin: 0px;"&gt;LaptopTest&lt;/span&gt; &lt;span class="punct" style="margin: 0px;"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant" style="margin: 0px;"&gt;Test&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;::&lt;/span&gt;&lt;span class="constant" style="margin: 0px;"&gt;Unit&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;::&lt;/span&gt;&lt;span class="constant" style="margin: 0px;"&gt;TestCase&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword" style="margin: 0px;"&gt;def &lt;/span&gt;&lt;span class="method" style="margin: 0px;"&gt;test_power_on_fails_when_battery_too_low&lt;/span&gt;&lt;br /&gt;    &lt;span class="constant" style="margin: 0px;"&gt;Battery&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;.&lt;/span&gt;&lt;span class="ident" style="margin: 0px;"&gt;any_instance&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;.&lt;/span&gt;&lt;span class="ident" style="margin: 0px;"&gt;expects&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;(&lt;/span&gt;&lt;span class="symbol" style="margin: 0px;"&gt;:meter&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;).&lt;/span&gt;&lt;span class="ident" style="margin: 0px;"&gt;returns&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;(&lt;/span&gt;&lt;span class="number" style="margin: 0px;"&gt;9&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident" style="margin: 0px;"&gt;laptop&lt;/span&gt; &lt;span class="punct" style="margin: 0px;"&gt;=&lt;/span&gt; &lt;span class="constant" style="margin: 0px;"&gt;Laptop&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;.&lt;/span&gt;&lt;span class="ident" style="margin: 0px;"&gt;new&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident" style="margin: 0px;"&gt;laptop&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;.&lt;/span&gt;&lt;span class="ident" style="margin: 0px;"&gt;power_on&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident" style="margin: 0px;"&gt;assert_equal&lt;/span&gt; &lt;span class="symbol" style="margin: 0px;"&gt;:off&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;,&lt;/span&gt; &lt;span class="ident" style="margin: 0px;"&gt;laptop&lt;/span&gt;&lt;span class="punct" style="margin: 0px;"&gt;.&lt;/span&gt;&lt;span class="ident" style="margin: 0px;"&gt;power_status&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword" style="margin: 0px;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword" style="margin: 0px;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Basically I am mocking using Ruby Stubba/Mocha, but I don't even need to write an extra constructor to inject the Class Under Test's dependencies! With no interface IBattery, nothing! This is some cool trickery of programming in a dynamic language like Ruby, and I am discovering these things every day with my colleagues!&lt;br /&gt;&lt;br /&gt;I know you are going to say "well, I can use reflection to do the same thing...", and I will tell you, sure, you try to do it in a readable manner and with one line of code. I didn't say you can't do it with C# or Java, I am just saying, this is how I can do it with one line of highly readable Ruby. Happy programming.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-115820473657255745?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/115820473657255745/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=115820473657255745' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115820473657255745'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115820473657255745'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/09/ruby-constructor-dependency-injection.html' title='Ruby constructor dependency injection... or not?'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-115751429262447083</id><published>2006-09-05T22:10:00.000-05:00</published><updated>2006-09-05T22:59:35.530-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='efficiency'/><title type='text'>The most revolutionary usernames and passwords, ever</title><content type='html'>Haven't you written the authentication piece for your app? Isn't it annoying after it is implemented in your app, every time you try to access your app's functionality during development, you have to start logging in every time by typing in some dev-only user id with a fake password? Having a good set of tests helps alot in terms of not having to go through the app starting from login while new features are still undergoing development, but there are still many cases where you have to debug something starting from the first login page of your application.&lt;br /&gt;&lt;br /&gt;Over time, I have developed one way to mitigate this nuisance, by creating a few dev usernames and passwords of the following pattern:&lt;br /&gt;&lt;br /&gt;Users:&lt;br /&gt;+ dave&lt;br /&gt;+ fred&lt;br /&gt;+ cat&lt;br /&gt;+ red&lt;br /&gt;&lt;br /&gt;Passwords:&lt;br /&gt;+ create&lt;br /&gt;+ database&lt;br /&gt;+ case&lt;br /&gt;+ ware&lt;br /&gt;+ vase&lt;br /&gt;+ card&lt;br /&gt;+ garbage&lt;br /&gt;+ beta&lt;br /&gt;&lt;br /&gt;Now the question is, what is the pattern? Answer me in 10 seconds...&lt;br /&gt;&lt;br /&gt;That's right, I can finish typing all of them with my left hand, while my right hand still holding my mouse and not have to switch from my mouse to my keyboard to type the password, then my mouse to hit the 'Login' button.&lt;br /&gt;&lt;br /&gt;Did you get the pattern in 10 seconds?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-115751429262447083?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/115751429262447083/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=115751429262447083' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115751429262447083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115751429262447083'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/09/most-revolutionary-usernames-and.html' title='The most revolutionary usernames and passwords, ever'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-115630528842510576</id><published>2006-08-24T20:12:00.000-05:00</published><updated>2006-08-24T19:35:25.423-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><title type='text'>Size does matter</title><content type='html'>When it comes to managing an Agile project team, one question usually arises for planners and managers is how big should a story really be. For the most people a story should be sized at the minimum as something that can be completed within an iteration by someone (or a programming pair). Each story is tagged with a difficulty level, whether you measure using 1-2-3 ideal development days or gummy bears, for best-guess estimates on how long it will take to implement the solution for the story.&lt;br /&gt;&lt;br /&gt;I think while it looks trivial, story sizes is very important to an Agile team to manage business wishes of the application and development delivery of business values.&lt;br /&gt;&lt;br /&gt;On some projects, a story could be one small thing on one screen (eg. enter customer address). Others, it spans multiple screens (eg. enter, edit, and delete customer info). For me, I would much prefer they being as small as they can be. For something like entering, editing, and customer info, strive to break things out to something meaningfully small, like:&lt;br /&gt;- enter customer name&lt;br /&gt;- enter customer address&lt;br /&gt;- enter customer billing info&lt;br /&gt;- edit customer name&lt;br /&gt;- edit customer address&lt;br /&gt;- edit customer billing info&lt;br /&gt;- delete a customer&lt;br /&gt;&lt;br /&gt;Small story has a few advantages:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;More tailored to business needs&lt;/span&gt;&lt;br /&gt;Suppose you have a specific need for a desktop computer, like you are a professional graphics designer or gamer. Would you be more satisfied for your purchase if you spend your budget at an online store for one of their 20 brand computers, or custom build it with hardware parts you get to play with one by one to address your specific needs? Is 1GB RAM gonna be enough? How about 2GB? Or 1GB plus more video card memory is good? If you purchase your computer parts by parts, chances are you will be buying something much more tailored to what you need now, at the same time pay for something that can accomodate tomorrow's change better (eg. a motherboard with more RAM slots than normal). The smaller your application's features are, the more flexibility you have. But don't go overboard and purchase your parts at microscopic level (buying transistors) which will cause you nightmare.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;You can need it later, or YAGNI at all&lt;/span&gt;&lt;br /&gt;You are building your dream vehicle from scratch. You have a tight budget, and a tight deadline. Do these constraints sound familiar? We are all working in this competitive business environment every day. So if you are building this car you need, can it satisfy your need with only 2 wheels like a motorbike? Does it still address your needs if it has three wheels? Do you need this car to have a 5th backup tire? Of course all these depends on what you use your car for. You might only need 2 wheels and as light weight as possible for a marathon or endurance race, or you might absolutely need a 5th wheel if you use it in the Africa dessert where there is no concrete. But the tight budget and deadline does not change, what changes is the delivery and whether the users' needs are satisfied. So, between one story that says "build 5 wheels for the vehicle" and five stories that says "add 1 additional wheel to the vehicle", what would you pick? Prioritization is key.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;More consistent and better measured velocity&lt;/span&gt;&lt;br /&gt;As previously mentioned, stories should be sized as something that can be completed in one iteration. Based on last iteration's performance, if you are planning for 3 stories to play the following iteration, constituting a 1-point, 2-point, and 3-point story. If the team were 99% complete with it but was unable to complete that 3-point story, your team's velocity dropped from a 6 down to a 3. This is a pretty big change in velocity. Now imagine you redo your iteration with 8 stories, same scope, a total of 20 points based on last iteration velocity. At the end of the iteration if your team cannot deliver one 2-point story and one 3-point story (note: much smaller scale), you team's velocity is 15. Your tracking of your development team's progress can be more accurately measured simply by splitting your stories up.&lt;br /&gt;But with smaller stories it does come at a cost. There are some disadvantages.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Code is better, analysis is easier&lt;/span&gt;&lt;br /&gt;Smaller stories encourage team members to complete something faster, meaning they get a chance to go back and refactor code easier and more often.  It also allows programming pair to switch pairing partner more often to get more understanding of the code base. Smaller stories can also be defined in much more granularly fashion. Things that business do not understand yet can be split out to another story in order to get things going. Better tested and better understanding the code and stories, obviously makes your application more robust and easier to change.&lt;br /&gt;&lt;br /&gt;With smaller stories, one must also understand there are drawbacks too. Here are a few I can think of:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Story explosion&lt;/span&gt;&lt;br /&gt;Most people use an Excel spreadsheet to manage their day-to-day story progress. Having a spreadsheet of 60 stories is alright, now consider representing the same story list with 180 stories. If you are organizationally challenged, you will quickly run into issues with your tracking. How to categorize such information? Well, in today's blogsphere world there is something called tagging. You can tag a story with multiple tags, and later click on a tag topic (eg. UI) and a list of things you previously tagged will show up. This allows someone to quickly sort and filter the obviously larger story list. I don't know if there is a product out there that supports such information sorting and filtering yet, but I would start looking there if I were to solve this issue. &lt;a href="http://del.icio.us/"&gt;Del.icio.us&lt;/a&gt; is a good place to explore tagging.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Story selections&lt;/span&gt;&lt;br /&gt;When there are more stories to choose from, playing the right stories becomes more tricky. It is common to have groups of stories that are tied to an area that is under heavy business analysis and are subject to change completely, and groups of stories that are fairly stable and not prone to change. But only picking stories that are stable and easy to complete is not the correct way to play stories. Whichever stories to play should depend on the complexity of the technical solution&lt;br /&gt;and business value that can be recouped from that solution. The application is meaningless to the business if out of the 180 stories, 90 are complete, 60 are still under intense business analysis and are subject to change, and 30 are ready to go, while the business value generated by those 90 completed stories are trivial. Get down to the dirty aspect of the problem the application is trying to solve, and start flushing them out. Without a defined problem to solve, any application is way to expensive to build.&lt;br /&gt;&lt;br /&gt;So should you go with diet size story or super-sized story? This question is like asking how diverse your investment portfolio should be. It depends. Now that you know why size does matter, apply your thinker's head to see whether it applies to the project you are on!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-115630528842510576?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/115630528842510576/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=115630528842510576' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115630528842510576'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115630528842510576'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/08/size-does-matter.html' title='Size does matter'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-115578371240588011</id><published>2006-08-18T00:19:00.000-05:00</published><updated>2006-08-17T23:29:38.536-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pattern'/><title type='text'>The "Not-Enough-Objects" anti-pattern</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Definition:&lt;/span&gt;&lt;br /&gt;This is a pattern that every single programmer in the world had used at one point or another in their programming careers. Classes become unnecessarily bloated and multi-talented in their ability to single-handedly accomplish almost anything and everything an application requires.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Root Causes:&lt;br /&gt;&lt;/span&gt;Being lazy to create new classes for new but not aligned behaviors into existing objects; being ignorant on good objects commuication and interactions; lack of concern of each objects' goal of existence; failure to realize the importance of programming to interfaces; putting off code refactoring forever (the "kitchen sink" syndrome &lt;sup&gt;&lt;u&gt;1&lt;/u&gt;&lt;/sup&gt;); embraces code babies&lt;sup&gt;&lt;u&gt;2&lt;/u&gt;&lt;/sup&gt; as if they are the next American Idols; failure to effectively organize, categorize, and classify application functionalities.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;UML:&lt;/span&gt;&lt;br /&gt;eg.&lt;div&gt;&lt;table border=1&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align="center"&gt;Customer&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;+ OrderSoda() ~20 LOC&lt;br /&gt;+ Sleep() ~100 LOC&lt;br /&gt;+ RPCCallTolMom() ~250 LOC&lt;br /&gt;+ PlayTVGames() ~500 LOC&lt;br /&gt;+ WebServiceCallToWalkMyDog() ~3,000 LOC&lt;br /&gt;+ RepeatRoutineEveryDay() ~275 LOC&lt;br /&gt;  .&lt;br /&gt;  .&lt;br /&gt;  .&lt;br /&gt;  (many more methods)&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Symtoms:&lt;/span&gt;&lt;br /&gt;When it appears in your code, they make your code become demoralizingly hard to read or comprehend; extremely difficult to touch code without introducing new errors; code become very brittle; code starts to look like POOP (Procedural Object-Oriented Programming); exponentially increase your programmers' after-hour caffeine consumption to life-threatening level against others (e.g. "I will kill the xxx if I knew who wrote this").&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;How to Avoid:&lt;/span&gt;&lt;br /&gt;Avoid 1,000 line classes and/or 200 line methods; avoid class behaviors that are mutually exclusive; avoid class states that are mutually exclusive; avoid classes to have multiple responsibilities in favor of delegating to new objects that the classes use; avoid too many if-else statements in favor of object hierarchy (see Strategy); effectively categorize or organize your classes/namespace/file/folder structures into meaningful and humanitarian fashion; encourage collective code-ownership; frequently communicate with team members the intent of each object; feverishly unit test the heck out of each class and object using mocks and stubs; use TDD as test-driven &lt;span style="font-style: italic;"&gt;design&lt;/span&gt; to think how other objects will use the object you are unit testing; pair-program with someone who reminds you of code quality is of utmost importance.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Short List of Skills Required to Avoid:&lt;/span&gt;&lt;br /&gt;1. Programming to abstraction and not implementation&lt;br /&gt;2. Object encapsulation&lt;br /&gt;3. "Tell, Don't Ask" and the Law (or guideline) of Demeter (&lt;a href="http://www.pragmaticprogrammer.com/articles/jan_03_enbug.pdf#search=%22ask%20tell%22"&gt;link&lt;/a&gt;), go back to #2 if you do not understand&lt;br /&gt;4. Short methods/classes with well-intended names in favor of plethora of comments&lt;br /&gt;5. Unit testing&lt;br /&gt;6. All your unit tests run in sub-10 secs. because you understand Mock vs. Stub (&lt;a href="http://www.martinfowler.com/articles/mocksArentStubs.html"&gt;link&lt;/a&gt;)&lt;br /&gt;7. Dependency Injection (&lt;a href="http://www.martinfowler.com/articles/injection.html"&gt;link&lt;/a&gt;)&lt;br /&gt;8. Decoupling and cohesiveness (related to object-to-object communications)&lt;br /&gt;9. Knowledge of design patterns of all types&lt;br /&gt;10. Knowledge of anti-patterns&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;After-Thought:&lt;/span&gt;&lt;br /&gt;Have we understood why good programmers are rare?&lt;br /&gt;&lt;br /&gt;(1) kitchen sink syndrome: If it is clean, it stays clean. Once dirty dishes goes in and no one bothers cleaning up, dishes will start piling up and they never get cleaned.&lt;br /&gt;(2) code babies: A snippet or chunk of code, or application functionality that is given birth by its genetically related programmer that no one else in the planet understands or dares to understand.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-115578371240588011?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/115578371240588011/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=115578371240588011' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115578371240588011'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115578371240588011'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/08/not-enough-objects-anti-pattern.html' title='The &quot;Not-Enough-Objects&quot; anti-pattern'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-115548557138258160</id><published>2006-08-13T10:45:00.000-05:00</published><updated>2006-08-13T11:39:14.986-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='efficiency'/><title type='text'>Time is money... Stop using your Quick Launch or Start menu</title><content type='html'>Do you have a list of 30+ icons of applications you use daily in you Quick Launch taskbar and you turned on its auto-hide feature? Do you try to actively manage your &lt;span style="font-style: italic;"&gt;Start/Programs&lt;/span&gt; folders to make it easier for you to find and launch applications? Are you running out of keyboard shortcuts to launch your favourite applications? If you say yes to all these questions, chances are you are a power keyboard-driven user of your computer. It's simply a breeze without having to move your hands away from the keyboard and do everything on your computer.&lt;br /&gt;&lt;br /&gt;To add more ammunition to you keyboard freaks, download and try &lt;a href="http://colibri.leetspeak.org/"&gt;Colibri&lt;/a&gt;. This is an application that lets you launch your apps by simply typing in a pattern that matches your apps' names! I mapped it to the F12 key on my laptop. When I want to launch Firefox, I press F12, then type in "fox", and hit enter and it is launched; When I want to launch MSN Messenger, I press F12, type in "mess", hit enter and it is launched...&lt;br /&gt;&lt;br /&gt;It's pretty much similar to Google Desktop, but in GD after typing I have to use the up and down arrows multiple times to nav away from other doc types to get to app's exe. Colibri is strictly for app launching. If I type in "mess" for MSN Messenger, it will remember that you have used it to launch MSN Messenger, so next time rather than suggesting you to launch Y! Messenger it will default to MSN Messenger, while letting you to arrow down to select Y! Messenger should you want to.&lt;br /&gt;&lt;br /&gt;I rarely use my Quick Launch or my Start menu anymore...&lt;br /&gt;&lt;br /&gt;Bonus Tip 1: Did you know shift-F10 opens up your mouse right-click menu?&lt;br /&gt;Bonus Tip 2: Turn on your "Begin finding when you begin typing" feature in your Firefox. I use this to "type-and-hit-enter" to surf the web =P&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-115548557138258160?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/115548557138258160/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=115548557138258160' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115548557138258160'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115548557138258160'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/08/time-is-money-stop-using-your-quick.html' title='Time is money... Stop using your Quick Launch or Start menu'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-115121318620270568</id><published>2006-07-19T01:05:00.000-05:00</published><updated>2006-07-19T00:30:13.740-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='pattern'/><title type='text'>Applying PicoContainer.NET with Presentation Patterns - Part II</title><content type='html'>(&lt;a href="http://stephenchu.blogspot.com/2006/07/applying-picocontainernet-with.html"&gt;Part I&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;Let's think about what Pico is good at in practical sense. I think Pico is great at wiring dependencies together. That means, it is good at wiring dependencies starting from your Presentation Model/Presenter layer and back, because these are all classes that you have complete control over. For your Views, at least if you develop in Visual Studio .NET, the restriction on their default constructors conflicts with the way Pico works. There are ways around it (setter injection as said in Part I), but they are ugly.&lt;br /&gt;&lt;br /&gt;But what if I don't register my Views into my Pico container like other classes? Then the question becomes: how can my View get a reference to its dependent Presentation Model/Presenter?&lt;br /&gt;&lt;br /&gt;Before I show you my solution, I have to say one more thing. The method &lt;span style="font-style: italic;"&gt;InitializeComponent()&lt;/span&gt; synonymously refer controls as components for a reason. Every control in .NET is a component. What is a component? It is something you put in a container. Every container has many components. So yes, I am telling you that these controls/widgets are all being put in a container (not Pico) in the .NET control hierarchy framework. So what does each control being a component in this .NET container hierarchy enable me to do? It allows you to have access to the functionalities from other components in the same container. Let me rephrase: each View, being a component, can have access to other classes like a Presentation Model/Presenter, so long as they are all in the same .NET container hierarchy framework. Didn't I just say that this container framework is not Pico? Yes, I did, but it doesn't mean we have to use only one over the other. We can use them both together to each of their strengths to achieve the best of both worlds. Let me explain.&lt;br /&gt;&lt;br /&gt;We have already said using Pico container to host everything starting from the Presentation Model layer is a good thing. So here is some example code on how to do this:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Consolas; font-size: 11pt; color: black; background: white;"&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: teal;"&gt;IMutablePicoContainer&lt;/span&gt; createMutablePicoContainer()&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: teal;"&gt;IMutablePicoContainer&lt;/span&gt; pico = &lt;span style="color: blue;"&gt;new&lt;/span&gt; &lt;span style="color: teal;"&gt;DefaultPicoContainer&lt;/span&gt;();&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pico.RegisterComponentImplementation(&lt;span style="color: blue;"&gt;typeof&lt;/span&gt;(&lt;span style="color: teal;"&gt;IPageOnePM&lt;/span&gt;), &lt;span style="color: blue;"&gt;typeof&lt;/span&gt;(&lt;span style="color: teal;"&gt;PageOnePM&lt;/span&gt;));&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pico.RegisterComponentImplementation(&lt;span style="color: blue;"&gt;typeof&lt;/span&gt;(&lt;span style="color: teal;"&gt;IPageTwoPM&lt;/span&gt;), &lt;span style="color: blue;"&gt;typeof&lt;/span&gt;(&lt;span style="color: teal;"&gt;PageTwoPM&lt;/span&gt;));&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pico.RegisterComponentImplementation(&lt;span style="color: blue;"&gt;typeof&lt;/span&gt;(&lt;span style="color: teal;"&gt;IWebService&lt;/span&gt;), &lt;span style="color: blue;"&gt;typeof&lt;/span&gt;(&lt;span style="color: teal;"&gt;WebService&lt;/span&gt;));&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; pico;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;In order to allow the Views to have access to other components (Presentation Model/Presenter), we have to create a .NET container. It is just a class that subclass from System.ComponentModel.Container. I am going to call this an ApplicationContainer to avoid being confused with our Pico container.&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Consolas; font-size: 11pt; color: black; background: white;"&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;internal&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; &lt;span style="color: teal;"&gt;ApplicationContainer&lt;/span&gt; : System.ComponentModel.&lt;span style="color: teal;"&gt;Container&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;// ...&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;To put our Views into this ApplicationContainer, you instantiate an instance of it, and put the Form object that you will start your application with into it like this:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Consolas; font-size: 11pt; color: black; background: white;"&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; [&lt;span style="color: teal;"&gt;STAThread&lt;/span&gt;]&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; Main() &lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: teal;"&gt;ApplicationContainer&lt;/span&gt; applicationContainer = &lt;span style="color: blue;"&gt;new&lt;/span&gt; &lt;span style="color: teal;"&gt;ApplicationContainer&lt;/span&gt;();&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: teal;"&gt;MainForm&lt;/span&gt; form = &lt;span style="color: blue;"&gt;new&lt;/span&gt; &lt;span style="color: teal;"&gt;MainForm&lt;/span&gt;();&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; applicationContainer.Add(form);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: teal;"&gt;Application&lt;/span&gt;.Run(form);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;From our Views, the method that they will use to get access to other components is called &lt;span style="font-style: italic;"&gt;GetService(Type serviceType)&lt;/span&gt;. When this method is called from within our Views, if our Views are put in a System.ComponentModel.Container, by default the method will ask for its containing container to traverse all registered components and see who can provide this "service" to it. If it finds such component, the container will return it, and now the requesting component gets a reference to that object. How does the container traverse its registered components and decide to give the component requesting a service the service that it asks for? Well, interestingly a System.ComponentModel.Container also has its own &lt;span style="font-style: italic;"&gt;GetService()&lt;/span&gt; method to do just that. Now, since we have our own subclass ApplicationContainer, what if we override its &lt;span style="font-style: italic;"&gt;GetService()&lt;/span&gt;, and while our ApplicationContainer object receives a request for service from any of its components, we can instruct it also look to see if Pico has the stuff that the requesting component has. More concretely, when a View uses &lt;span style="font-style: italic;"&gt;GetService(typeof(MyPresentationModel)&lt;/span&gt; to get its Presentation Model/Presenter dependency, ApplicationContainer will ask for a Pico container that has already been fully registered to return an instance of that class if it is found, like such:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Consolas; font-size: 11pt; color: black; background: white;"&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;internal&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; &lt;span style="color: teal;"&gt;ApplicationContainer&lt;/span&gt; : System.ComponentModel.&lt;span style="color: teal;"&gt;Container&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;  &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: teal;"&gt;IMutablePicoContainer&lt;/span&gt; _pico = createMutablePicoContainer();&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;protected&lt;/span&gt; &lt;span style="color: blue;"&gt;override&lt;/span&gt; &lt;span style="color: blue;"&gt;object&lt;/span&gt; GetService(&lt;span style="color: teal;"&gt;Type&lt;/span&gt; service)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;object&lt;/span&gt; instance = _pico.GetComponentInstanceOfType(service);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (instance != &lt;span style="color: blue;"&gt;null&lt;/span&gt;)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; instance;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; &lt;span style="color: blue;"&gt;base&lt;/span&gt;.GetService (service);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;To sum it all up, you need to do the following to get things to work:&lt;br /&gt;1. Create an ApplicationContainer class subclassing System.ComponentModel.Container.&lt;br /&gt;2. Add your starting Form into the ApplicationContainer instance, prior to starting it.&lt;br /&gt;3. Set up a Pico container within the ApplicationContainer instance.&lt;br /&gt;4. Register all dependencies that your Presentation Model/Presenter classes will need in your Pico container. You do not need to register your Views.&lt;br /&gt;5. Override the ApplicationContainer's GetService() method, make it to look further into its already setup Pico container for anything that it should return.&lt;br /&gt;&lt;br /&gt;Now from your Views, you can gain access to its dependency, in this case a Presentation Model/Presenter, by calling:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Consolas; font-size: 11pt; color: black; background: white;"&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; PageOneView_Load(&lt;span style="color: blue;"&gt;object&lt;/span&gt; sender, System.&lt;span style="color: teal;"&gt;EventArgs&lt;/span&gt; e)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;// Add this View to ApplicationContainer. Otherwise we have to instantiate&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;// each and every view in public static void Main() and do the adding there.&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;base&lt;/span&gt;.FindForm().Container.Add(&lt;span style="color: blue;"&gt;this&lt;/span&gt;);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;// This GetService() call will now find what we need in ApplicationContainer.&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: teal;"&gt;IPageOnePM&lt;/span&gt; service = (&lt;span style="color: teal;"&gt;IPageOnePM&lt;/span&gt;)&lt;span style="color: blue;"&gt;base&lt;/span&gt;.GetService(&lt;span style="color: blue;"&gt;typeof&lt;/span&gt;(&lt;span style="color: teal;"&gt;IPageOnePM&lt;/span&gt;));&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And modify your PresentationModel's constructor to include an additional parameter to take in the web service class. Then your class can start using the web service functionality, while in unit testing you can mock/stub it out!&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Consolas; font-size: 11pt; color: black; background: white;"&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;interface&lt;/span&gt; &lt;span style="color: teal;"&gt;IWebService&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; &lt;span style="color: teal;"&gt;WebService&lt;/span&gt; : &lt;span style="color: teal;"&gt;IWebService&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; &lt;span style="color: teal;"&gt;PageOnePM&lt;/span&gt; : &lt;span style="color: teal;"&gt;PresentationModel&lt;/span&gt;, &lt;span style="color: teal;"&gt;IPageOnePM&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: teal;"&gt;IWebService&lt;/span&gt; webservice;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; PageOnePM(&lt;span style="color: teal;"&gt;IWebService&lt;/span&gt; webservice)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;this&lt;/span&gt;.webservice = webservice;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;// ApplicationContainer class&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; IMutablePicoContainer createMutablePicoContainer()&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; IMutablePicoContainer pico = &lt;span style="color: blue;"&gt;new&lt;/span&gt; DefaultPicoContainer();&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;// ...&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pico.RegisterComponentImplementation(&lt;span style="color: blue;"&gt;typeof&lt;/span&gt;(IWebService), &lt;span style="color: blue;"&gt;typeof&lt;/span&gt;(WebService));&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;// ...&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; pico;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So now, we have eliminated the child user control not knowing how to get a Presentation Model/Presenter problem, because a user control is also, well, a component in the same ApplicationContainer. We have also solved the ugly setter injecting child user controls' dependencies problem. You also did not modify a single View default constructor! We can now happily use our Visual Studio .NET IDE to do WYSIWYG design and manage good OO design, plus Inversion of Control-ing our dependencies.&lt;br /&gt;&lt;br /&gt;After-thought: Though the title of these two posts say Presentation Patterns, in my mind they are more geared towards just for Presentation Model, due to in my opinion the difference in directional references between Presentation Model and Model-View-Presenter. I mentioned briefly about this in my earlier post &lt;a href="http://stephenchu.blogspot.com/2006/01/few-findings-about-presentation-model.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;By the way, note that now each page &lt;span style="font-style: italic;"&gt;can&lt;/span&gt; have access to multiple Presentation Model objects, instead of the what-it-used-to-be 1-to-1 relationship between a page and its Presentation Model, so one can do something similar to Ruby on Rails where each View can make calls to &lt;span style="font-weight: bold;"&gt;multiple&lt;/span&gt; Controllers which operates on various Models to complete the desired action! This makes code-sharing between Presentation Models much easier, and also each Presentation Model can be named not after their page but by application functionality!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-115121318620270568?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/115121318620270568/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=115121318620270568' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115121318620270568'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115121318620270568'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/07/applying-picocontainernet-with_19.html' title='Applying PicoContainer.NET with Presentation Patterns - Part II'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-115118338545760400</id><published>2006-07-19T01:02:00.000-05:00</published><updated>2006-07-20T16:14:17.550-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='pattern'/><title type='text'>Applying PicoContainer.NET with Presentation Patterns - Part I</title><content type='html'>There is a couple favourite presentation-layer design patterns that I have been consistently using to build .NET applications. In particular, they are &lt;a href="http://www.martinfowler.com/eaaDev/PresentationModel.html"&gt;Presentation Model&lt;/a&gt; and &lt;a href="http://www.martinfowler.com/eaaDev/ModelViewPresenter.html"&gt;Model-View-Presenter&lt;/a&gt;. Both are extremely handy when it comes to making the code-behind of your View more testable by delegating its responsibilities to another layer of code. From that layer, you can start chipping in various flavors of dependency injections and stubs and mocks to start going all-out unit testing assault to your code.&lt;br /&gt;&lt;br /&gt;In many cases, on a per View basis (like a page or user control), there is a Presentation Model class, or a Presenter class, sitting behind it ready to receive a call from its corresponding View, then execute the behavior called upon.&lt;br /&gt;&lt;br /&gt;PicoContainer.NET (Pico from here on) encourages more decoupled object-oriented class design by helping you to manage your classes' dependencies. As a quick example, suppose you have a class iPod (you know what it is, right?) and a class Battery. Obviously iPod depends on a battery. However, the iPod class at construction time creates its own Battery instance to use and cannot take any other battery types (to the dismay of iPod users). Now your iPod stopped working and you have to figure out why. If you need to test your iPod, you have to be extremely creative to figure out where the problem is related to its battery or not somehow. How do you know if the problem lies in the iPod, or its battery? This is why good code minimize dependencies. Suppose, however, your iPod receives a Battery instance through its constructor. Then in the unit tests of your iPod class, you can then mock/stub the battery out and start exclusively interrogating and verifying the behaviors of your iPod class. iPod now can be injected with a mock Battery instance, and hence your iPod class is more testable, and its tests are more "unit" and not touching other parts of your code. You can then start writing more automated unit tests that run in milliseconds easily.&lt;br /&gt;&lt;br /&gt;The question now is who then is responsible for passing a Battery instance into iPod. That's where Pico shines. If, as mentioned, your iPod has such a constructor that takes in the Battery dependency, using Pico as a container, you register your iPod and Battery class into the container, when you ask the container for an iPod instance, the Battery instance (since it is also registered) will be instantiated automagically and you can now start using your iPod object, without having to hard-code your iPod class to instantiate its own Battery.&lt;br /&gt;&lt;br /&gt;Because Pico is such a great tool, I have used it in various .NET projects of mine. Since Pico uses a registration scheme, whatever you put into a Pico container you get its dependency-wiring functionality for free, many things tend to be put into it. Afterall, Pico encourages good design, right? As a result, Views, Presentation Models, Presenters, etc., are all registered into Pico container. The Pico container is set up at &lt;span style="font-style: italic;"&gt;public static void Main&lt;/span&gt;() time, and when you run &lt;span style="font-style: italic;"&gt;Application.Start(form)&lt;/span&gt; you pass it into your &lt;span style="font-style: italic;"&gt;form&lt;/span&gt; object. From there all of your Views can programmatically ask for its dependencies. Life is good.&lt;br /&gt;&lt;br /&gt;Why are Views being put into the container? Because every View needs a delegating Presentation Model or Presenter to handle its behavior, meaning, every View "depends" on a Presentation Model or Presenter. As a result, they all get registered into Pico in order for the &lt;span style="font-style: italic;"&gt;form&lt;/span&gt; to get navigate the user to the correct View.&lt;br /&gt;&lt;br /&gt;Life is good - until stuff happens. Since constructor injection is the preferred way to inject dependencies, if your View "depends" on something, then your View would need a constructor passing in its dependencies before Pico can start wiring dependencies on your behalf. Every .NET programmer knows that, every View (regardless you use a Form or User Control) has a default parameter-less constructor that has a default line &lt;span style="font-style: italic;"&gt;InitializeComponent()&lt;/span&gt;. This is because the Visual Studio .NET IDE uses this constructor to support WYSIWYG at design time. Tampering with or getting rid of this constructor and/or the &lt;span style="font-style: italic;"&gt;InitializeComponent()&lt;/span&gt; method call will get your IDE trouble in supporting editing your View at design time.&lt;br /&gt;&lt;br /&gt;A second problem that would arise is that, consider I have a User Control. It is also a View, albeit being used and instantiated by its parent View (maybe another User Control). Since this child user control instance is created by the auto-generated code from the parent Views &lt;span style="font-style: italic;"&gt;InitializeComponent()&lt;/span&gt; method call, you cannot modify this child user control's default constructor to take in its "dependency" Presentation Model/Presenter, and then hope that its parent Form can somehow inject the child control's dependencies inside its &lt;span style="font-style: italic;"&gt;InitializeComponent()&lt;/span&gt; method. Even if you can modify these auto-generated code, your IDE design-time support is now in jeapardy. Because of this problem, some Pico programmers would buck the constructor injection coding consistency, pass in the child user control's dependencies into the parent View's constructor, and start using setter injection to inject the child user control's dependencies, after the parent Form's &lt;span style="font-style: italic;"&gt;InitializeComponents()&lt;/span&gt; method call. That way at least you can leave the child user control's default constructor alone and continue to use VS's strong IDE support, while managing your child control's dependencies.&lt;br /&gt;&lt;br /&gt;What to do? Now comes &lt;a href="http://stephenchu.blogspot.com/2006/07/applying-picocontainernet-with_19.html"&gt;Part II&lt;/a&gt; - My attempt to get the best sides of all worlds: Use Pico to manage dependencies; and have VS.NET IDE WYSIWYG support.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-115118338545760400?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/115118338545760400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=115118338545760400' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115118338545760400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115118338545760400'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/07/applying-picocontainernet-with.html' title='Applying PicoContainer.NET with Presentation Patterns - Part I'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-115273872553208139</id><published>2006-07-12T16:04:00.000-05:00</published><updated>2007-11-27T23:11:38.277-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby/rails'/><title type='text'>Ruby sugar you can't find in C#</title><content type='html'>Coming as a C# programmer to learn Ruby, as I journey from a compiling language to a dynamically typed language, of course there are some concepts in Ruby that are easier (or harder) for me to grasp than others. I want to write out loud some of these interesting bits and pieces for others who are also interested in trying out Ruby.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;String vs. Symbol&lt;/span&gt;&lt;br /&gt;In Ruby a string can be single-quoted or double-quoted (eg. "foo"). A symbol is a variable name prefixed with a colon (eg. :foo). Symbols somtimes are used pretty much synonymously with strings. The reason they exist is that by using symbols, in memory they only exist as a single copy per symbol no matter how many times you refer to them in your code. String however will create a new copy of the string in memory every time you refer to them, even though they are exactly the same. This in C# is equivalent to string interning, and is the difference between using between a &lt;span style="font-style: italic;"&gt;StringBuilder&lt;/span&gt; and regular string concatenation. So don't be baffled when you see them next time.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Methods vs. Messages&lt;/span&gt;&lt;br /&gt;In Ruby, everything is an object, including a number and a string. Ruby programmers like to think that when you are calling a method on an object, you are sending a message to that object in hope of it doing something for you. One of the cool features in Ruby is that one can call methods that are not defined at programming time but define and call them at runtime. For example:&lt;br /&gt;&lt;pre class="ruby"&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Foo&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;programming_time_method&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;programming time method&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;define_more_methods&lt;/span&gt;&lt;br /&gt;    &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;class&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;class_eval&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;      &lt;span class="ident"&gt;define_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:new_born_method&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;i was born!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Now if you do:&lt;br /&gt;&lt;pre class="ruby"&gt;&lt;span class="ident"&gt;f&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Foo&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;programming_time_method&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;respond_to?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:new_born_method&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="comment"&gt;#Does f have method by that name?&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;define_more_methods&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;respond_to?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:new_born_method&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="comment"&gt;#Does f have method by that name, now?&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new_born_method&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;then you get results:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;ruby test.rb&lt;br /&gt;programming time method&lt;br /&gt;false&lt;br /&gt;true&lt;br /&gt;i was born!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;One can do much more fancier stuff with Ruby too. Read on.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Class Methods vs. Static Methods&lt;/span&gt;&lt;br /&gt;In C# most developers I worked with frowned upon static methods. They are flat-out evil. You cannot effectively mock method calls of them, and that they encourage non-OO style procedural programming. Pretty much all of the arguments that they are good for something are trumped by the hard-to-test reason. Introducing class methods in Ruby:&lt;br /&gt;&lt;br /&gt;&lt;pre class="ruby"&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Customer&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.class_method&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;class method getting called&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="constant"&gt;Customer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;class_method&lt;/span&gt;&lt;br /&gt;&gt;ruby test.rb&lt;br /&gt;class method getting called&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;At first glance they act just like static methods, you do not need an instance but just the class type and you can start making calls on them. However, they are no longer un-mockable. The unit testing framework that comes with Ruby also contains a mock framework. It can mock class methods. All of a sudden class methods, or should I say methods that associate to a class, deserves another look. In fact, Ruby on Rails uses class methods extensively for some of its magical operations. For example, if my Customer class inherits from Model, and has an id and a name in the database, I automatically get these magic methods for free - yes, free!&lt;br /&gt;&lt;br /&gt;&lt;pre class="ruby"&gt;&lt;span class="constant"&gt;Customer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find_all&lt;/span&gt;                &lt;span class="comment"&gt;# returns the list of all Customer instances from database&lt;/span&gt;&lt;br /&gt;&lt;span class="constant"&gt;Customer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find_by_id&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;2&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;           &lt;span class="comment"&gt;# returns the Customer instance that contains id=2 in database&lt;/span&gt;&lt;br /&gt;&lt;span class="constant"&gt;Customer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find_by_name&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;STEPHEN&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="comment"&gt;# returns the list of all Customer instances whose name is STEPHEN&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;When you add a column to your table in the database, you get a free &lt;span style="font-style: italic;"&gt;find&lt;/span&gt; method without coding. That's Rails.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Method-missing&lt;/span&gt;&lt;br /&gt;Any message (aka method) you send (aka call) to an object, if that object does not yet have that method defined, it will go into its method_missing() method, which you can override. From that point, one can be fairly creative given the dynamic nature of what Ruby allows a programmer to do. In fact, method_missing() plays a pretty important role in constructing a DSL (Domain Specific Language).&lt;br /&gt;&lt;br /&gt;&lt;pre class="ruby"&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Customer&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;method_missing&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;method_symbol&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;method_symbol&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;customer&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Customer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;customer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;pays&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;me&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;five&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;dollars&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&gt;ruby test.rb&lt;br /&gt;pays&lt;br /&gt;me&lt;br /&gt;five&lt;br /&gt;dollars&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;More Ruby sugar will follow. Stay tuned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-115273872553208139?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/115273872553208139/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=115273872553208139' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115273872553208139'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115273872553208139'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/07/ruby-sugar-you-cant-find-in-c.html' title='Ruby sugar you can&apos;t find in C#'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-115103361069491248</id><published>2006-06-22T21:30:00.000-05:00</published><updated>2007-11-27T23:11:38.277-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby/rails'/><title type='text'>How to use GMail SMTP server to send emails in Rails ActionMailer</title><content type='html'>Recently I had to use Ruby to send emails. I don't want to go through the hassle of setting up a mail server on my build box, and since I use GMail's hosted webmail service I am thinking I might be able to use their smtp mail server to do it.&lt;br /&gt;&lt;br /&gt;Rail's ActionMailer was simply the automatic choice, since I am building a rails app.&lt;br /&gt;&lt;br /&gt;Turns out GMail supports only SSL SMTP mailing service, meaning if you cannot create a SSL connection to its SMTP server, you cannot send email through them. My Rails and Ruby (1.84) version do not yet support creating a SSL SMTP connection through Net::SMTP. DHH writes about how to do so through installing msmtp &lt;a href="http://wiki.rubyonrails.org/rails/pages/HowToSendEmailsWithActionMailer"&gt;here&lt;/a&gt;, but we developers just obviously love options =)&lt;br /&gt;&lt;br /&gt;The dynamic nature of Ruby allows me to enhance the functionality of that class. I found the following code from a couple Japanese posts &lt;a href="http://d.hatena.ne.jp/zorio/20060416"&gt;here&lt;/a&gt; and &lt;a href="http://d.hatena.ne.jp/zorio/20060417"&gt;here&lt;/a&gt; on the web (fairly low Google ranking). Pasting them in my /vendor/plugins as follow:&lt;br /&gt;&lt;br /&gt;&lt;pre class="ruby"&gt;&lt;br /&gt;$ cat vendor/plugins/action_mailer_tls/init.rb&lt;br /&gt;&lt;span class="ident"&gt;require_dependency&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;smtp_tls&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="ruby"&gt;&lt;br /&gt;$ cat vendor/plugins/action_mailer_tls/lib/smtp_tls.rb&lt;br /&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;openssl&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;net/smtp&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="constant"&gt;Net&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;SMTP&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;class_eval&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;private&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;do_start&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;helodomain&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;secret&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;authtype&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;raise&lt;/span&gt; &lt;span class="constant"&gt;IOError&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;SMTP session already started&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@started&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;check_auth_args&lt;/span&gt; &lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;secret&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;authtype&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;user&lt;/span&gt; &lt;span class="keyword"&gt;or&lt;/span&gt; &lt;span class="ident"&gt;secret&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="ident"&gt;sock&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;timeout&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@open_timeout&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="constant"&gt;TCPSocket&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;open&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@address&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="attribute"&gt;@port&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@socket&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Net&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;InternetMessageIO&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;sock&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@socket&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;read_timeout&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;60&lt;/span&gt; &lt;span class="comment"&gt;#@read_timeout&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@socket&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;debug_output&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;STDERR&lt;/span&gt; &lt;span class="comment"&gt;#@debug_output&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="ident"&gt;check_response&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;critical&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;recv_response&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt; &lt;span class="punct"&gt;})&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;do_helo&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;helodomain&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;raise&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;openssl library not installed&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="keyword"&gt;defined?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;OpenSSL&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;starttls&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;ssl&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;OpenSSL&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;SSL&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;SSLSocket&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;sock&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;ssl&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;sync_close&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;ssl&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;connect&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@socket&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Net&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;InternetMessageIO&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;ssl&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@socket&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;read_timeout&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;60&lt;/span&gt; &lt;span class="comment"&gt;#@read_timeout&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@socket&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;debug_output&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;STDERR&lt;/span&gt; &lt;span class="comment"&gt;#@debug_output&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;do_helo&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;helodomain&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="ident"&gt;authenticate&lt;/span&gt; &lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;secret&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;authtype&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;user&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@started&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;ensure&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="attribute"&gt;@started&lt;/span&gt;&lt;br /&gt;      &lt;span class="comment"&gt;# authentication failed, cancel connection.&lt;/span&gt;&lt;br /&gt;        &lt;span class="attribute"&gt;@socket&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;close&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="keyword"&gt;not&lt;/span&gt; &lt;span class="attribute"&gt;@started&lt;/span&gt; &lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="attribute"&gt;@socket&lt;/span&gt; &lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="keyword"&gt;not&lt;/span&gt; &lt;span class="attribute"&gt;@socket&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;closed?&lt;/span&gt;&lt;br /&gt;      &lt;span class="attribute"&gt;@socket&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;do_helo&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;helodomain&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;     &lt;span class="keyword"&gt;begin&lt;/span&gt;&lt;br /&gt;      &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@esmtp&lt;/span&gt;&lt;br /&gt;        &lt;span class="ident"&gt;ehlo&lt;/span&gt; &lt;span class="ident"&gt;helodomain&lt;/span&gt;&lt;br /&gt;      &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;        &lt;span class="ident"&gt;helo&lt;/span&gt; &lt;span class="ident"&gt;helodomain&lt;/span&gt;&lt;br /&gt;      &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;rescue&lt;/span&gt; &lt;span class="constant"&gt;Net&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;ProtocolError&lt;/span&gt;&lt;br /&gt;      &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@esmtp&lt;/span&gt;&lt;br /&gt;        &lt;span class="attribute"&gt;@esmtp&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;br /&gt;        &lt;span class="attribute"&gt;@error_occured&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;retry&lt;/span&gt;&lt;br /&gt;      &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;      &lt;span class="keyword"&gt;raise&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;starttls&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;getok&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;STARTTLS&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;quit&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;begin&lt;/span&gt;&lt;br /&gt;      &lt;span class="ident"&gt;getok&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;QUIT&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;rescue&lt;/span&gt; &lt;span class="constant"&gt;EOFError&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;So now, in your ActionMailer::Base's server settings if you have:&lt;pre  class="ruby"&gt;&lt;br /&gt;ActionMailer::Base.server_settings = {&lt;br /&gt;  :address =&gt; "smtp.gmail.com",&lt;br /&gt;  :port =&gt; 587,&lt;br /&gt;  :domain =&gt; "mycompany.com",&lt;br /&gt;  :authentication =&gt; :plain,&lt;br /&gt;  :user_name =&gt; "username",&lt;br /&gt;  :password =&gt; "password"&lt;br /&gt;}&lt;/pre&gt;You call your ActionMailer::Base's deliver method (perhaps from a custom subclass), it will send an email through GMail. Mission accomplished.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-115103361069491248?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/115103361069491248/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=115103361069491248' title='75 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115103361069491248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/115103361069491248'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/06/how-to-use-gmail-smtp-server-to-send.html' title='How to use GMail SMTP server to send emails in Rails ActionMailer'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>75</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-114857534712512812</id><published>2006-05-25T11:41:00.000-05:00</published><updated>2006-05-25T20:15:32.736-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><title type='text'>Some unspoken tips on managing an Agile project</title><content type='html'>Want to have an easier time to manage an Agile project and avoid common pitfalls while have some fun? Read on...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Food&lt;/b&gt;&lt;br /&gt;Believe it or not, food is not a luxury item on an Agile project. Food is *essential*. Agile practices encourages as much open-air communication as quickly as possible, be it computer-to-human (through tests) or human-to-human (short iterative cycle). Yet sometimes it is hard to force down onto people to communicate more effectively from a Project Manager. By having good food/snacks, people who are not co-located are going to come to the room much more often than usual, and a casual "how are you guys doing" while shuffling popcorn into his/her mouth is usually a conversation starter on many things the development team needs. And food, of course, improves morale inmeasurably =)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Team co-location&lt;/b&gt;&lt;br /&gt;This is key to being able to communicate more effectively, which is one of the tenets of a successful Agile project. By more effectively, I mean the project can execute in less time because people can make more informed decision based on an abundant amount of open-air information. For whatever reasons, people are generally good at filtering useless information but not good at carrying information to the appropriate people who need them. By bringing the team together in a relatively open area with maximum face-to-face communication, you are encouraging team members to make more informed decisions before they waste time to work on the wrong thing based on false assumptions on continually ongoing changes within the development team and the business. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;CI build sound&lt;/b&gt;&lt;br /&gt;If you use CruiseControl.NET, try putting a sound clip that reminds of some embarrassing moments of some development team members as the build success/failure cctray settings. This can get quite amusing some times. Another morale booster.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Quote sheet&lt;/b&gt;&lt;br /&gt;During the course of development, there is bound to be someone who inadvertently said something hilarious about someone or something. Keep a list of them on a &lt;a href="http://en.wikipedia.org/wiki/Wiki"&gt;wiki&lt;/a&gt; on the build server where the team members have access to. This is a must as part of the team-building process. At &lt;a href="http://www.thoughtworks.com"&gt;ThoughtWorks&lt;/a&gt;'s annual national gathering day, we collect great and funny quotes from ThoughtWorkers from projects everywhere, print them on T-shirts, then auction them out. The money collected goes to donation. Funny and meaningful.&lt;br /&gt;&lt;br /&gt;My funny quote of my current project? Nah. You have to ask me personally for that =P&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Second story wall for issues after 1.0 release&lt;/b&gt;&lt;br /&gt;In a XP story wall, which represents the progress of stories planned for the current iteration, usually contains columns ("DEV Ready", "DEV In Progress", "QA Ready", "QA Complete") that mark the stages of a story from ready for development to QA complete. Now how does the wall change when your application goes 1.0 release?&lt;br /&gt;&lt;br /&gt;Generally, after a 1.0 release, the team will create a new branch of the source code for urgent production defect fixing purpose. The problem is that when you have a large team of developers split across urgent production defect fixes and release 1.1 development, or there are many small and frequent 1.x releases coming after your initial release, or for some uncommon reason you have more than one branches of source code (after you have determined it is ultimately unavoidable). Then managing the merging of code from your release branch to your active development branch becomes tricky, because if developers forget to merge, your active development branch now has bugs.&lt;br /&gt;&lt;br /&gt;How about at 1.0 release, create a new story/issue wall next to the original story wall, but it only contains columns "Issue Verified", "Issue In Progress", "Issue Resolved", and have the developers merge their source code before they can move any issue cards from "Issue Resolved" column of that wall to the other wall's "QA Ready"? This physical movement of an issue card from one wall to another always remind developers to merge their changes. If that doesn't help the busy and forgetful developers, have them move issue cards to a new column called "Issue Merged".&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Stand-up token&lt;/b&gt;&lt;br /&gt;A good stand-up meeting is not an avenue to identify "solutions" of problems participants face. It should be a quick meeting to identify individuals who need to hold face-to-face conversations after the stand-up and then move on. But people being people, sometimes conversations go out of hand during a stand-up. Have a stand-up token, like a football, in stand-up so that only the person who has the token can talk. When you see people exchange the token too many times in a conversation, perhaps it's time to ask them to offline it till after the stand-up.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-114857534712512812?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/114857534712512812/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=114857534712512812' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/114857534712512812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/114857534712512812'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/05/some-unspoken-tips-on-managing-agile.html' title='Some unspoken tips on managing an Agile project'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-114619075351763430</id><published>2006-04-27T21:02:00.000-05:00</published><updated>2006-04-27T21:19:13.540-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><title type='text'>Stacking "using" blocks together</title><content type='html'>I had always been thinking of using the using-block to instantiate multiple disposable objects so I don't have to remember explicitly calling Close() or Dispose() on them:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Bitstream Vera Sans Mono Bold; font-size: 10pt; color: black; background: white; border-top: windowtext 1pt solid; padding-top: 0pt; border-left: windowtext 1pt solid; padding-left: 0pt; border-right: windowtext 1pt solid; padding-right: 0pt; border-bottom: windowtext 1pt solid; padding-bottom: 0pt;"&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;using&lt;/span&gt; (IDisposable one = &lt;span style="color: blue;"&gt;new&lt;/span&gt; DisposableOne(), &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; IDisposable two = &lt;span style="color: blue;"&gt;new&lt;/span&gt; DisposableTwo())&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;// Compile time error&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;But unfortunately C# does not support that. So as a work around, one would do these nested using statements to do the trick:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Bitstream Vera Sans Mono Bold; font-size: 10pt; color: black; background: white; border-top: windowtext 1pt solid; padding-top: 0pt; border-left: windowtext 1pt solid; padding-left: 0pt; border-right: windowtext 1pt solid; padding-right: 0pt; border-bottom: windowtext 1pt solid; padding-bottom: 0pt;"&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;using&lt;/span&gt; (IDisposable one = &lt;span style="color: blue;"&gt;new&lt;/span&gt; DisposableOne())&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;using&lt;/span&gt; (IDisposable two = &lt;span style="color: blue;"&gt;new&lt;/span&gt; DisposableTwo())&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;// Ops. Code is now indented&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;But now, you have messed up your pretty-looking indentation of your code. Remember back in the acadamia your programming class instructor told you that all your if-else/for/while statements will work without curly brackets? Well, surprise, same case for using blocks:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Bitstream Vera Sans Mono Bold; font-size: 10pt; color: black; background: white; border-top: windowtext 1pt solid; padding-top: 0pt; border-left: windowtext 1pt solid; padding-left: 0pt; border-right: windowtext 1pt solid; padding-right: 0pt; border-bottom: windowtext 1pt solid; padding-bottom: 0pt;"&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;using&lt;/span&gt; (IDisposable one = &lt;span style="color: blue;"&gt;new&lt;/span&gt; DisposableOne())&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;using&lt;/span&gt; (IDisposable two = &lt;span style="color: blue;"&gt;new&lt;/span&gt; DisposableTwo())&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;// This works!&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Time to show off to your pairing friend you can write prettier code than him/her =D&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-114619075351763430?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/114619075351763430/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=114619075351763430' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/114619075351763430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/114619075351763430'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/04/stacking-using-blocks-together.html' title='Stacking &quot;using&quot; blocks together'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-114563973147826314</id><published>2006-04-21T11:54:00.000-05:00</published><updated>2006-04-21T15:13:20.733-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><title type='text'>Multi-threading applications tips</title><content type='html'>Recently I have been involved with a multi-threading application, and throughout development I have been acquiring tips and gotchas here and there. One of the fun things about such applications is that you get to play with some sort of almighty powerful server box. In my case, a cool 8-processor dual core box with 8 GB RAM... How much is it? $70,000. =)&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Test your application in a comparable multi-processor testing server. Better yet, test it on your production box&lt;/strong&gt;&lt;br /&gt;Yes, you heard me. Usually multi-threading applications are performance critical. Otherwise how else can you justify a pricy machine and more complicated code? If you are not able to perform testing on the actual production box, chances are the users will ultimately find the problems. Which means developers, QA, and project managers all have to work OT hours to fix the problem and push those fixes to production, plus the number of phone calls and help desk tickets you have to make. When you multiply these hours and stress, the justification of having another pricy testing server before going into production all of a sudden is not too pricy anymore.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Server GC vs. Workstation GC&lt;/strong&gt;&lt;br /&gt;.NET garbage collection behaves differently when it is installed on a single processor machine versus on a multi-processor machine. If you install it on a single processor box, it is in Workstation GC mode, meaning there is at most one thread doing garbage collection. When you install .NET onto a multi-processor box, it is installed as Server GC mode, meaning there is one GC thread per CPU. I strongly advise that the environment you deploy to before your production environment to be a multi-processor machine and have Server GC mode turned on. That way you are testing more closely to the real environment. In performance-intensive applications, garbage collection, albeit automatic, is usually worth monitoring as well.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Raise an event thread-safe&lt;/strong&gt;&lt;br /&gt;In C#, many of us have raised an event this way. In fact, this is the way most books/lectures/tutorials demonstrate it.&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Bitstream Vera Sans Mono Bold; font-size: 10pt; color: black; background: white; border-top: windowtext 1pt solid; padding-top: 0pt; border-left: windowtext 1pt solid; padding-left: 0pt; border-right: windowtext 1pt solid; padding-right: 0pt; border-bottom: windowtext 1pt solid; padding-bottom: 0pt;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; Battery&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2&lt;/span&gt;&amp;nbsp;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;const&lt;/span&gt; &lt;span style="color: blue;"&gt;int&lt;/span&gt; LOW_BATTERY_LEVEL = 20;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4&lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;5&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;event&lt;/span&gt; EventHandler LowBattery;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;event&lt;/span&gt; EventHandler Depleted;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;7&lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;8&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;int&lt;/span&gt; remainingBattery = 100;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;9&lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;10&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; OnLowBattery()&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;11&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;12&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (LowBattery != &lt;span style="color: blue;"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;13&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;14&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LowBattery(&lt;span style="color: blue;"&gt;this&lt;/span&gt;, EventArgs.Empty);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;15&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;16&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;17&lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;18&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; OnDepleted()&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;19&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;20&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (Depleted != &lt;span style="color: blue;"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;21&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;22&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Depleted(&lt;span style="color: blue;"&gt;this&lt;/span&gt;, EventArgs.Empty);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;23&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;24&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;25&lt;/span&gt;&amp;nbsp;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Did you know that this is not thread-safe? Thread A could be executing this code, does a null check at line 12, when it is just about to raise the event, Thread B grabs the subscriber and unregisters/unwires the event. When Thread A resumes execution, it tries to raise the event when no one is subscribing to it. Null reference exception.&lt;br /&gt;&lt;br /&gt;In order to make the code thread-safe, we can take advantage of delegates are value-types, in other words, you can only create"copies" of them, but not by reference.&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Bitstream Vera Sans Mono Bold; font-size: 10pt; color: black; background: white; border-top: windowtext 1pt solid; padding-top: 0pt; border-left: windowtext 1pt solid; padding-left: 0pt; border-right: windowtext 1pt solid; padding-right: 0pt; border-bottom: windowtext 1pt solid; padding-bottom: 0pt;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;10&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; OnLowBattery()&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;11&lt;/span&gt;&amp;nbsp;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;12&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; EventHandler handler = LowBattery;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;13&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (handler != &lt;span style="color: blue;"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;14&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;15&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; handler(&lt;span style="color: blue;"&gt;this&lt;/span&gt;, EventArgs.Empty);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;16&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;17&lt;/span&gt;&amp;nbsp;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Use of lock(this) and lock(typeof(Foo))&lt;/strong&gt;&lt;br /&gt;Every C# programmer is aware of the lock keyword. It prevents multiple threads from referencing the resource while the resource is being modified. When you want to modify a member variable thread-safe, you can go and lock the object that contains it:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Bitstream Vera Sans Mono Bold; font-size: 10pt; color: black; background: white; border-top: windowtext 1pt solid; padding-top: 0pt; border-left: windowtext 1pt solid; padding-left: 0pt; border-right: windowtext 1pt solid; padding-right: 0pt; border-bottom: windowtext 1pt solid; padding-bottom: 0pt;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;public&lt;/span&gt; Foo()&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;lock&lt;/span&gt;(&lt;span style="color: blue;"&gt;this&lt;/span&gt;)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; _memberVariable = "SOMETHING";&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;But what about static variables? Well, interestingly the lock statement allows you to lock a type object as well:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Bitstream Vera Sans Mono Bold; font-size: 10pt; color: black; background: white; border-top: windowtext 1pt solid; padding-top: 0pt; border-left: windowtext 1pt solid; padding-left: 0pt; border-right: windowtext 1pt solid; padding-right: 0pt; border-bottom: windowtext 1pt solid; padding-bottom: 0pt;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; Foo&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: blue;"&gt;string&lt;/span&gt; StaticVariable = &lt;span style="color: blue;"&gt;null&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; SetUpStatic()&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;lock&lt;/span&gt; (&lt;span style="color: blue;"&gt;typeof&lt;/span&gt;(Foo))&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; StaticVariable = "SOMETHING";&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Give you threads names&lt;/strong&gt;&lt;br /&gt;Yes. Threads in the old days have only a thread id, which gets generated every time when a new thread is genereated, and that makes debugging very painful. Now in .NET you can programmatically give each thread your application creates a name with its t.Name property. Use them, then in your VS.NET Debug/Windows/Threads (Ctrl-Alt-H while debugging) you will see each thread with their name. Now they look much more friendly. Better yet, name your threads after each project manager you have worked with =)&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Use .NET synchronization classes&lt;/strong&gt;&lt;br /&gt;Don't tell people you know how to write multi-threading applications knowing only how to use lock(). If you don't know how Monitor.Wait() and Monitor.Pulse() and the shopping bag of the synchronization classes that .NET provides, you are missing out alot. As a starter, try this excellent article &lt;a href="http://research.microsoft.com/~birrell/papers/ThreadsCSharp.pdf"&gt;here&lt;/a&gt;. It's a must read for anyone programs C# and multi-threading.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-114563973147826314?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/114563973147826314/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=114563973147826314' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/114563973147826314'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/114563973147826314'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/04/multi-threading-applications-tips_21.html' title='Multi-threading applications tips'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-114549255727061284</id><published>2006-04-19T19:14:00.000-05:00</published><updated>2006-04-19T20:25:51.356-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='efficiency'/><title type='text'>Checking duplicate Sql statements in Excel</title><content type='html'>In my current project we are doing database Continuous Integration (CI). This means that every developer will have their own database instance for development, and at every check-in the build server kicks off the CI cycle, which starts getting the latest source code, compile, rebuild the database from scratch through running SQL scripts, re-insert data (reference data) these tables should have, compile, test, etc. When done right, this allows the much-required freedom for developers to develop against the database without interfering other developers' databases, and significantly reduces the time it takes to ask the DBA's to modify the database table for you.&lt;br /&gt;&lt;br /&gt;When you acquire from the customer/business analysts a new set of reference data that they would like you to insert into these database tables, I have seen in many cases where these new data conflicts with the data that already exists due to duplication (mostly human error). Since most of these reference data are scripted in text files and there are usually hundreds if not thousands of them, sometimes finding that one duplicating line is like trying to find a needle in a sea.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/2001/988/1600/reference_data_example.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/2001/988/320/reference_data_example.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Fortunately in Excel there is a function to rescue: =COUNTIF(set, to_find).&lt;br /&gt;&lt;br /&gt;Usage: Increment count if "to_find" exists in "set".&lt;br /&gt;&lt;br /&gt;So to quickly find out which is the offending PK constraint INSERT line, you do this:&lt;br /&gt;=COUNTIF(E3:E11, E3)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/2001/988/1600/excel_countif.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/2001/988/320/excel_countif.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And as you see you will quickly find out where that awful INSERT statement is... Time to bug the customer =)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-114549255727061284?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/114549255727061284/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=114549255727061284' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/114549255727061284'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/114549255727061284'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/04/checking-duplicate-sql-statements-in.html' title='Checking duplicate Sql statements in Excel'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-114175169379978731</id><published>2006-03-16T23:10:00.000-06:00</published><updated>2006-03-15T23:11:12.990-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='pattern'/><title type='text'>Pragmatic NAnt scripting</title><content type='html'>Why am I talking about NAnt when the rest of the world is in love with Ruby and Rake? Well, I still think NAnt has its value. Rake is still pretty raw (it doesn't even have an NUnit task yet), and until it has matured there is still a huge group of developers out there not wanting to spend a huge deal of time learning Ruby/Rake. Plus, most companies out there who have already adopted NAnt is stuck with their NAnt code. This article provides some insights on how to provide such companies business value by making NAnt scripts more maintainable.&lt;br /&gt;&lt;br /&gt;Based on my experiences with NAnt, I am concluding the followings are true:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;NAnt scripts are very hard to maintain, due to reasons such as:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Target dependencies are all intermingled into a big unmanageable web.&lt;/li&gt;&lt;li&gt;No consistencies across NAnt scripts. There is no coding convention in NAnt scripts.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;The scripts have to handle a lot if you think about it. It has to handle...&lt;ul&gt;&lt;li&gt;Pre-Build (like build artifact directory creations)&lt;/li&gt;&lt;li&gt;The CI (Continuous Integration) cycle like GetLatest, Compile, UnitTests, OtherTests.&lt;/li&gt;&lt;li&gt;Post-Build (like package, stage, distribute, deploy, blah...)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Build scripts are nasty and no one wants to inherit, and that's why names like "build monkey" (and even more provocative ones) are created and stick to people who initially start the script and tend to stick to those people until they leave the project. It is a thankless job.&lt;/li&gt;&lt;li&gt;Build scripts are, nevertheless, crucial to any projects. If you have a good set of build scripts, they can help developers writing higher quality code, BAs to verify story functionalities quicker, QA to mitigate environment nightmare, and streamlines deployment process by deployment team.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;And here are some tips that I have found to be useful to extend the maintainability and lifespan of your build scripts.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Have multiple build script files&lt;/strong&gt;&lt;br /&gt;I would break down what I see in many projects one monolithic projectname.build file into many smaller build files named after their build function. For example, the file below is called Sesame.build. It has one and only one purpose: to build a project, and test it. I am putting these two build functions together into one build file because the targets involving testing the build output is usually small, and usually go hand-in-hand together. Conveniently co-locating them into one file I think is better than creating a small and isolated test.build file. But other build disciplines, such as package &amp; deploy, by itself could be a big deal, so if complexity warrants I will have a separate file for them. The only encapsulation we can apply to build scripts is really separating them into files.&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&amp;lt;?xml version="1.0" ?&amp;gt;&lt;br /&gt;&amp;lt;project name="cruise" default="all"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="all" depends="get, build, tag" description="Target executed by CCNet." /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="get" depends="get.get_latest" description="Gets the latest source code from Subversion." /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="build" depends="build.build" description="Compile and build the source code." /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="tag" depends="tag.tag" description="Tag the successful build by the naming convention tags\\CRUISE-B999" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="get.get_latest"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="build.build"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;nant buildfile="Sesame.build" target="all" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="tag.tag"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&amp;lt;?xml version="1.0" ?&amp;gt;&lt;br /&gt;&amp;lt;project name="Sesame" default="all"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="all" depends="build, test" description="Compile, build, and test the Sesame project." /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="build" depends="build.compile, build.database" description="Compiles the .NET source code and setup local database instance." /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="test" depends="test.unit_test, test.other_test" description="Runs unit tests and functional tests." /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="build.compile"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="build.database"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="test.unit_test"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="test.other_test"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;NAnt target categorizations and naming convention&lt;/strong&gt;&lt;br /&gt;The ultimate sin of unmaintainable build scripts more or less attribute to the over-profileration of target dependencies. When targets start having a complex web of dependencies, build scripts will take an enormous amount of courage and time to repair.&lt;br /&gt;&lt;br /&gt;By breaking down all targets in a single NAnt build file into three categories, and through coding consistency and naming conventions, build scripts can last for a very long time. I find the following categorizations of all NAnt targets in your build scripts useful.&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&amp;lt;?xml version="1.0" ?&amp;gt;&lt;br /&gt;&amp;lt;project name="Sesame" default="all"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;!-- Level 1 --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="all" depends="build, test" description="Compile, build, and test the Sesame project." /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;!-- Level 2 --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="build" depends="build.compile, build.database" description="Compiles the .NET source code and setup local database instance." /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="test" depends="test.unit_test, test.other_test" description="Runs unit tests and functional tests." /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;!-- Level 3 --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="build.compile"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Compile using the build.solution_configuration property value...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="build.database"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Rebuild database using the database_server property value...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="test.unit_test"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="test.other_test"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Points of interests:&lt;ul&gt;&lt;br /&gt;    &lt;li&gt;Level 1: These are targets that orchestrates the order of executions of various Level 2 targets and not Level 3 targets. They will only contain depends, but never have a target body. They are the common entry points into the build function the script file represents (eg. target "all" in cruise.build will be called by CruiseControl.NET to kick off the CI build process). They must have descriptions. I prefer names to be underscore-delimited.&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;Level 2: These are targets that group Level 3 targets together to make a cohesive unit of work of function. For example, a "clean" target might do altogether a few things to clean a build: build artifacts directories, build results directories, VS.NET bin/obj/VSWebCache, etc. They again will never have a target body. These targets will also have descriptions. I again prefer their names to be underscored.&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;Level 3: These targets will *never* contain depends. They will *only* do one very refined detail piece of work. In addition, their names should be namespaced by a Level 2 target name using a period, and then their function, so that they are easily distinguishable from other targets. This helps newcomers to recognize them and start treating them differently. They never have descriptions (think of these as private methods in a class that does one very specific function for you).&lt;/li&gt;&lt;/ul&gt;The namespacing naming convention can also be expanded to properties as well.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Use properties to consolidate paths&lt;/strong&gt;&lt;br /&gt;Have a file (eg. common_paths.build) that extensively use NAnt properties to consolidate your source's folder structure. This will save a lot of code duplication in your build scripts in the longer run if you intend to keep your build scripts for a while.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/2001/988/1600/folder_structure.0.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/2001/988/400/folder_structure.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&amp;lt;?xml version="1.0" ?&amp;gt;&lt;br /&gt;&amp;lt;project name="common_paths.nant" default="all"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="trunk.dir" value="." /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="build_output.dir" value="${trunk.dir}\build_output" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="build_results.dir" value="${trunk.dir}\build_results" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="source.dir" value="${trunk.dir}\source" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="tools.dir" value="${trunk.dir}\tools" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="dotnet.dir" value="${source.dir}\dotnet" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="dts.dir" value="${source.dir}\dts" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="sql.dir" value="${source.dir}\sql" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="nunit.dir" value="${tools.dir}\nunit" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="nant.dir" value="${tools.dir}\nant" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="nantcontrib.dir" value="${tools.dir}\nantcontrib" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Use the target description attribute&lt;/strong&gt;&lt;br /&gt;NAnt provides a -projecthelp command line switch to list all of the targets in a given build file. When you give targets a description these targets gets first-class recognition in the listing as "Main Targets":&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/2001/988/1600/projecthelp.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/2001/988/400/projecthelp.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Combining this tip with the naming convention of the Level 3 targets can be a very powerful technique in improving the readability of your build scripts. As a bonus tip, consider also implementing a target named "help" to display all this -projecthelp target lists.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Use depends or call, but not both&lt;/strong&gt;&lt;br /&gt;If you start using both, you will be impairing the readability of your build scripts. They more or less do the same thing anyways. I would use depends over call because newcomers to NAnt would be much more likely to learn about depends ahead of call.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Know your property inheritance&lt;/strong&gt;&lt;br /&gt;NAnt's property inheritance convenience is commonly overlooked and under-valued. Many people chose using &amp;lt;call&amp;gt; task to preserve locally declared property values, rather than experimenting property inheritance of various kinds. I am publishing my findings here.&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&amp;lt;?xml version="1.0" ?&amp;gt;&lt;br /&gt;&amp;lt;project name="example" default="example"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;include buildfile="common_paths.nant" unless="${property::exists('trunk.dir')}" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="example" depends="calling_script.properties"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;nant buildfile="example_two.build" inheritall="true"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;properties&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="solution.configuration" value="DEBUG" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="build_output.dir" value="c:\modified_build_output" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/properties&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/nant&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="calling_script.properties"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="calling_script.depends_inherited" value="Inherited from depends clause." /&amp;gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&amp;lt;?xml version="1.0" ?&amp;gt;&lt;br /&gt;&amp;lt;project name="example_two"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;include buildfile="common_paths.nant" unless="${property::exists('trunk.dir')}" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;echo message="1) Explicitly inheriting property from nant task tag: solution.configuration='${solution.configuration}'" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;echo message="2) Explicitly overriding property in calling script's nant task tag: build_output.dir='${build_output.dir}'" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;echo message="3) Implicitly inheriting property from calling depends clause: calling_script.depends_inherited='${calling_script.depends_inherited}'" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;echo message="Is (1) readonly? ${property::is-readonly('solution.configuration')}" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;echo message="Is (2) readonly? ${property::is-readonly('build_output.dir')}" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;echo message="Is (3) readonly? ${property::is-readonly('calling_script.depends_inherited')}" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/2001/988/1600/properties_run_result.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/2001/988/400/properties_run_result.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;1 &amp; 2) Inherit properties through &amp;lt;nant&amp;gt; task&lt;br /&gt;You can call the &amp;lt;nant&amp;gt; task and include a &amp;lt;properties&amp;gt; set within the tags, while setting the inheritall attribute to true to pass those properties to the build script that is getting invoked. In addition, as shown in (2), you can also override the loaded property "build_output.dir" in the properties section prior to passing it into the callee build script.&lt;br /&gt;&lt;br /&gt;If you use this style of properties passing, it is a very good idea to pass them as readonly=true to avoid your customization of script behaviors to be reset again by the callee script.&lt;br /&gt;&lt;br /&gt;3) It's important to know that properties declared earlier in the depends clause can be used further downstream in the later depends targets, even into another callee build script. This technique is useful when you do not want to load all properties for all targets up front (with the side-effect that the last target in your execution will have access to all properties declared in every single previously executed target, but I am okay with it because I rarely run into properties nightmare compare to build scripts nightmare). Therefore, the following readable script can be achieved:&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&amp;lt;?xml version="1.0" ?&amp;gt;&lt;br /&gt;&amp;lt;project name="Sesame" default="all"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="all" depends="build, test" description="Compile, build, and test the Sesame project." /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="build" depends="build.properties, build.compile, build.database" description="Compiles the .NET source code and setup local database instance." /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="build.properties"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="build.solution_configuration" value="DEBUG" unless="${property::exists('build.solution_configuration'}" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="build.database_server" value="localhost" unless="${property::exists('build.database_server'}" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="build.compile"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Compile using the build.solution_configuration property value...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;target name="build.database"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Rebuild database using the database_server property value...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/target&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I am sure there are lots of other tips as well, but that's it for this post. Comments, feedback welcome.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-114175169379978731?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/114175169379978731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=114175169379978731' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/114175169379978731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/114175169379978731'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/03/pragmatic-nant-scripting.html' title='Pragmatic NAnt scripting'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-113894197471647681</id><published>2006-02-02T22:30:00.000-06:00</published><updated>2006-02-02T22:47:56.826-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='pattern'/><title type='text'>A good way to create a custom Value-Type object in C#?</title><content type='html'>Have you ever written code like this?&lt;br /&gt;&lt;br /&gt;&lt;div    style="border: 1pt solid windowtext; padding: 0pt; background: white none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;font-family:Bitstream Vera Sans Mono Bold;font-size:10pt;color:black;"&gt;&lt;pre style="margin: 0px;"&gt;State state = &lt;span style="color:blue;"&gt;new&lt;/span&gt; State("AZ");&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Money money = &lt;span style="color:blue;"&gt;new&lt;/span&gt; Money(299.95);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt; &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;State state = State.Parse("AZ");&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Money money = Money.Parse(299.95);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;How many domain-specific Value Type classes (you care about equality but not identities) have you been creating every project? The question of should I use the "new" keyword or a static .Parse() method to create them is always a coding consistency problem.&lt;br /&gt;&lt;br /&gt;What if I tell you that you can do this in C#:&lt;br /&gt;&lt;br /&gt;&lt;div face="Bitstream Vera Sans Mono Bold" size="10pt" color="black" style="border: 1pt solid windowtext; padding: 0pt; background: white none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;pre style="margin: 0px;"&gt;State state = "AZ";&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Money money = 299.95;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Simple and clean. Here's how:&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1pt solid windowtext; padding: 0pt; background: white none repeat scroll 0% 50%; font-family: Bitstream Vera Sans Mono Bold; font-size: 10pt; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color:blue;"&gt;public&lt;/span&gt; &lt;span style="color:blue;"&gt;class&lt;/span&gt; State&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;    &lt;span style="color:blue;"&gt;private&lt;/span&gt; &lt;span style="color:blue;"&gt;readonly&lt;/span&gt; &lt;span style="color:blue;"&gt;string&lt;/span&gt; _state;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt; &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;    &lt;span style="color:blue;"&gt;private&lt;/span&gt; State(&lt;span style="color:blue;"&gt;string&lt;/span&gt; state)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;    {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;        _state = state;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;    }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt; &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;    &lt;span style="color:blue;"&gt;public&lt;/span&gt; &lt;span style="color:blue;"&gt;static&lt;/span&gt; &lt;span style="color:blue;"&gt;implicit&lt;/span&gt; &lt;span style="color:blue;"&gt;operator&lt;/span&gt; State(&lt;span style="color:blue;"&gt;string&lt;/span&gt; state)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;    {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;        &lt;span style="color:blue;"&gt;return&lt;/span&gt; &lt;span style="color:blue;"&gt;new&lt;/span&gt; State(state);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;    }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-113894197471647681?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/113894197471647681/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=113894197471647681' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/113894197471647681'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/113894197471647681'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/02/good-way-to-create-custom-value-type.html' title='A good way to create a custom Value-Type object in C#?'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-113816534809105143</id><published>2006-01-24T22:14:00.000-06:00</published><updated>2006-01-30T16:36:44.060-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='pattern'/><title type='text'>Few findings about Presentation Model</title><content type='html'>Almost every project that I have been on strives for code testability. By increasing code testability one can better the design of the solution as well as continue to change code without worries. However when it comes to increase testability of the view in the classic MVC, it is always hard to do. Microsoft proclaims that they have achieved MVC (or Model-ASPX-CodeBehind), but it fails to address the testability issue of the code in V and C: CodeBehind logic are extremely hard to test against. Both the Presentation Model (PM) and the Model-View-Presenter (MVP) pattern address this issue.&lt;br /&gt;&lt;br /&gt;Having used both patterns for my past few projects, I think I am seeing some pros/cons. Today let's look at the Presentation Model first:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Presentation Model:&lt;/span&gt;&lt;br /&gt;I like using Presentation Model (PM) when I am developing a web application. In web apps, there are a couple major headaches when it comes to increase code testability, the first of which is carrying state across requests. Secondly, in ASP.NET, the Page class gets created and disposed of every request, making using an intermediary class (such as a Presenter) to "push" content from the model to the view very difficult, and hence a directional referencing problem.&lt;br /&gt;&lt;br /&gt;PM employs a "pull" model, meaning the consumer class (aka the code-behind) will ask for data that the PM has prepared for it. Because of this model, at each Page cycle step (Page_Load, Page_Init, etc.) the view will be in the driving seat as far as controlling when to reload the controls and what to load them with. The PM simply sits there and wait for someone to tap his shoulder to ask him to produce something. Notice the directional reference: the view has a reference to the PMs. The PMs do not know about the view, and do not push content out to force the view's state to be refreshed.&lt;br /&gt;&lt;br /&gt;Usually you will have one PM per ASPX page, which I call a Page PM. When the page gets busy, your Page PM is likely to be a gynormous 1,000 line class, containing many properties for the use of the many dynamic view controls' use. Therefore, use sub-PM classes to better organize your code and avoid code duplication. The levels of sub-PM classes from my experience probably won't go deeper than 3 inheritance hierarchy.&lt;br /&gt;&lt;br /&gt;Most of your Page PMs usually will have reference to the session object, in which most probably the aggregate root of your Domain Model will be stored. For a more complex Domain Model, the PM's task of flattening out this 3-dimensional beast starting with this aggregate root will make your view look extraordinarily simple.&lt;br /&gt;&lt;br /&gt;Because PMs usually flatten out your Domain Model into some easily readable grid or string format, your Domain Model objects may not be the ultimate data holder objects that your view 's controls bind to. For example, you will obviously need a new object for a grid showing an insurance policy's type, coverages, each and every insured entity, and each coverage's premiums. (Hopefully they live in separate objects in your Domain Model). Create some what I call "Data Item" objects to facilitate data binding to your grids or lists. Data Item objects are just for holding your diced and sliced Domain Model data and have no behaviors. Think of them as classes that represent a row of data in every single grid on your page. In the above example, your PolicyInfoDataItem class will have public properties of [PolicyType :string, CoverageName :string, InsuredEntityName :string, CoverageAmount :double]. This inspiration comes from DataGridItem, and various ListItem variants in the .NET Framework.&lt;br /&gt;&lt;br /&gt;Unit testing your Page PMs in NUnit is always not easy because it uses the Session object, which is not available in NUnit testing. I have seen people use reflection to create a fake HttpContext object that stores Session data on the current thread, which then during [SetUp] set the context object (yes it does have a setter). Then your PM unit test classes have access to HttpContext.Session.&lt;br /&gt;&lt;br /&gt;Speaking of unit testing PMs, I find it to be much more work to use mocking to unit tests your PMs. Especially if you love constructor injection, having your PM's ctor to take all those IEverything classes and during unit tests have them all mocked out is a lot of work because PMs is much more object state dependent than behavior loaded. As a result, I would prefer state-based testing (Stubs or the Object Mother pattern) to facilitate unit testing PMs. One must also manage the creation and customization of your domain model's state carefully, because these tend to duplicate code across your test classes easily if you go with state-based testing.&lt;br /&gt;&lt;br /&gt;Your view also should not directly instantiate or reference any Domain Model objects (as a guideline, not rule). Whenever possible, pass primitive type user input information gathered off the Page and pass them directly to the Page PM for actions. This can consolidate exception handling routines (instead of scattering them all over your views), and also decouple your view from your Model, which you might later put them into separate assemblies.&lt;br /&gt;&lt;br /&gt;Your Page PM can also handle behaviors that your view requests. For example, whenever a button is clicked, its code-behind event handler will make a call to the Page PM's "Save" method, passing the necessary information gathered off the controls, and then the Page PM will delegate the responsibility of the actual save to the Domain Model layer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-113816534809105143?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/113816534809105143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=113816534809105143' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/113816534809105143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/113816534809105143'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/01/few-findings-about-presentation-model.html' title='Few findings about Presentation Model'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-113729995452449648</id><published>2006-01-14T22:13:00.000-06:00</published><updated>2006-01-15T00:17:36.313-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><title type='text'>Agile story tips</title><content type='html'>Stories in an Agile project tend to be less talked about than Test Driven Development, Continuous Integration, and Food :-). But in fact, when it's done well it can profoundly affect the project team's productivity and end users' satisfaction towards the software. The following are a few findings from my experience related to stories management that will drive better software. Their importance are rated on a a scale of 1 to 5 asterisks:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Stories should be a thin-thread from the frontend all the way to the backend. (*****)&lt;br /&gt;&lt;br /&gt;This helps the features being developed each iteration to be completed more consistently, and consistency drives predictability, and thus increases visibility and helps business to prioritize and plan given the available time and resources.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Business analysts (or the sponsor/end user) should &lt;span style="font-style: italic;"&gt;own&lt;/span&gt; the stories. During IPM (Iteration Planning Meeting), they should be &lt;span style="font-style: italic;"&gt;signed-off&lt;/span&gt;, meaning no changes should be made when developers start developing them. (*****)&lt;br /&gt;&lt;br /&gt;By freezing the requirements, developers can be much more productive. Compare a 100m race between two runners, one can go all the way from start to finish without stopping, while the other has to stop-and-go every 10m because he has to worry about the next 10m track he runs will be changed. Also, by someone owning the stories, after the stories were developed the owner of the story will have the responsibility to verify the correctness of the solution. Thus this requires better-written story specifications (tying back to freezing requirements), and in the end the developed story becomes exactly what business wants.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Stories should be measured in terms of difficulties or story points, instead of ideal development days (IDD). (****)&lt;br /&gt;&lt;br /&gt;There are two camps of people when it comes to this bullet. One camp uses IDD to estimate the difficulty of a story, then uses Load Factor to measure how off they were in their original estimates. The other camp uses Level of Difficulty such as small/medium/large, or Story Points such as 1-5, so they can measure purely based on yesterday's weather, and not a number that someone uncomfortably being forced to make up.&lt;br /&gt;&lt;br /&gt;IMHO, the first camp's glossaries were created at "post-mortem," literally. Consider the following conversation after a project failure:&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Business: Why did you guys fail to deliver? You only delivered half of what I want.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Developers: Because we were not productive.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Business: Why?&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Developers: Too many damn meetings.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Business: Hm... so in "ideal time" if you had no meetings you would be able to deliver?&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Developers: Oh yea...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Business: So in hindsight, the original estimates you guys told me was off by a "load factor" of 1/2. Next time should I want a new software I better take that into my budget account...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The problem is that all these numbers are only meaningful within the context of that one project. But people being people, they like to carry these numbers with them to any projects they walk into wherever they go... because apparently they have been burned before. Next time when business asks for budget for a brand new project, guess what. They will bump up the budget by half, because of that "load factor."&lt;br /&gt;&lt;br /&gt;By using the terms of the second camp, one is implicitly forced to think of these measurements in the context of the current project. When I say this story's difficulty is a Large, one has to ask: Relative to what? Of course, the answer is relative to the other stories of &lt;span style="font-style: italic;"&gt;this&lt;/span&gt; project. When I say, Story A is a 1 and Story B is a 3, again you are forced to think in the context of&lt;span style="font-style: italic;"&gt; &lt;/span&gt;the current project.&lt;br /&gt;&lt;br /&gt;You might ask, now if you don't bump up the budget by half, then doesn't the business not get all of what they ask for? The answer is yes. But that's the beauty of short, iterative releases. If we can do that, then in the end the business without bumping up the original budget, yes they will exactly get what they have asked for according to their business priority, perhaps the 6 out of their 10 features, but since we have delivered at least some of the features in early iterations, not only the business has saved money due to those features being rolled out, but also the business is in better shape when it comes to repositioning itself to face more real world challenges, and thus will pump more budget to continue develop the software to give them what they want.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If stories are small enough, then there is no need to task them out during an IPM (Iteration Planning Meeting). If stories are to be tasked, they should be estimated. As each of them are completed, actuals should be measured.&lt;br /&gt;&lt;br /&gt;Admit to it. Small estimates are much more accurate than big estimates. Therefore, if each story is tasked out into small chunks of time estimates, and after story completion we have its corresponding actual time spent measurement, we then can find out how much work really is to complete say Story A, a medium-difficulty story or one that has Story Points of 3.&lt;br /&gt;&lt;br /&gt;So what are these estimates and actuals for? They are actual prove (or tracked history) of how we complete our &lt;span style="font-style: italic;"&gt;Stories&lt;/span&gt;. Let's say in Iteration One we have a story "Public user login" that has a Story Points of 3. In that iteration (two-weeks), at the IPM the development team estimates that there are a total of 10 tasks to be done to complete that story, and they busted their ass to complete that and only that story. Then, in Iteration Seven, a similar story "Restricted user login" shows up. Relatively speaking, it also has a Story Points of 3, since they are about equally difficult. However, since most of the one-time tasks to do that has been completed, the actual number of tasks to complete this story in Iteration Seven might be just 3 tasks. Now the team can use the rest of the time to build other stories, and thereby achieve more Story Points. If it turns out the team achieved total of 7 points in the end, then we say the team is kicking some ass and is more productive than they were in Iteration One. You would notice the total time spent on all tasks between the two iterations will be somewhat the same (assuming no resources change), but completed Story Points increased. From the point of the business, it rocks, because they are seeing more stuff being churned out by the team, in exactly the manner they want it to be.&lt;br /&gt;&lt;br /&gt;This brings up another very important point, if you notice...&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;For story difficulty measurements, whatever you use (IDD, Small/Medium/Large, Story Points), you should always estimate it using the same &lt;span style="font-style: italic;"&gt;scale &lt;/span&gt;as you estimate the entire story list. (*****)&lt;br /&gt;&lt;br /&gt;This is the only way to measure whether the team is improving over the course of the development.&lt;br /&gt;&lt;br /&gt;Using the example in the last bullet, if in Iteration One the team thinks that "User login story" is a 3 Story Points story, and in Iteration Seven the team completes a similar also 3 Story Points story "Restricted login story", then if in Iteration Ten business comes back and say they want a brand new but similarly difficult "CEO only login story", now despite the fact that in Iteration Ten, after doing those two login stories, this new story requires very very little work to complete, we must again make this story have 3 Story points.&lt;br /&gt;&lt;br /&gt;This way, the measurements will tell the business the following:&lt;br /&gt;In Iteration One, the development completed 3 Story Points.&lt;br /&gt;In Iteration Seven, the development completed 7 Story Points (because the tasks required to do the "Restricted login story" has reduced.&lt;br /&gt;In Iteration Ten, the development completed 14 Story Points (because even few tasks is required to complete the new 3-Point "CEO only login" story).&lt;br /&gt;In terms of the business people, Story Points = functionalities = business value. They know what they are getting in a consistent basis.&lt;br /&gt;&lt;br /&gt;Should there be a case somewhere in Iteration Eight the number of Story Points dropped, then one has to figure out why. Here's the task actuals can come into handy. In Iteration Seven, 7 Story Points and total of say 100 actual hours of time were needed to complete all tasks. In Iteration Eight, only 6.5 Story Points were completed. But if we look at the hours of the actual, only 90 hours were recorded from all tasks. Now we know the time the team spent on the actual tasks for all stories are about the same. Probably because people having vacations or public holidays that contributes to the drop in productivity.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-113729995452449648?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/113729995452449648/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=113729995452449648' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/113729995452449648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/113729995452449648'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2006/01/agile-story-tips.html' title='Agile story tips'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-113328678468027897</id><published>2005-11-29T11:47:00.000-06:00</published><updated>2005-11-29T15:00:45.700-06:00</updated><title type='text'>Do you check in your OSS source into trunk?</title><content type='html'>How to use Open-Source Software? What I mean is, should you see OSS source code as &lt;span style="font-style: italic;"&gt;part of&lt;/span&gt;  your project's source code, keep their source code around and maintain/update them, or should you just &lt;span style="font-style: italic;"&gt;use&lt;/span&gt; them in your project and wait for it to be maintained by their authors? This is a question for a lot of development teams, because by now almost anyone would have known the pros and cons of OSS. The bottomline is, you will use them at one point or another. The bigger question is, how to use them to your project's benefits, without carrying too much overhead.&lt;br /&gt;&lt;br /&gt;For me, I want to maintain as few lines of code as possible. So my answer is, don't ever give me the OSS's source code. Give me your &lt;span style="font-style: italic;"&gt;project's&lt;/span&gt; source code plus the OSS assemblies that are in use. I should be able to checkout your project's trunk and go. Don't assume I have stuff installed after I downloaded your project's source code. If at some point in the future the code does not run because of an OSS bug, &lt;span style="font-style: italic;"&gt;fix the bug then submit it back to the community&lt;/span&gt;. If this happens more than few times, &lt;span style="font-style: italic;"&gt;use something else.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This solves the problem of trying to maintain a chunk of code that a team has no idea about. At the minimum, it solves any new dev's problem of not having to download a 100MB trunk with 80MB of it is OSS source code (admittedly it's more a nuisance than a problem).&lt;br /&gt;&lt;br /&gt;One of the good things about OSS is that there usually is an abundant amount of alternatives out there. Take functional testing as an example, Selenium and WATIR. There are things Selenium is good at (cross-browser testing), and there are things WATIR is good at (more powerful script coding). Mock objects anyone? NMock, EasyMock.NET, Rhino.Mocks, etc. Code coverage? There are even TWO NCovers out there that has the same name...&lt;br /&gt;&lt;br /&gt;I think the problem of merging a tweaked, home-brew version of an OSS back into using a latest version down the road is much more painful than using multiple OSS in your code base. For the latter at least the breaking changes are documented.&lt;br /&gt;&lt;br /&gt;If it hurts to use something in solving your problem. Don't use it. Problem solved. The problem you are getting paid to solve is delivering business value.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-113328678468027897?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/113328678468027897/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=113328678468027897' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/113328678468027897'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/113328678468027897'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2005/11/do-you-check-in-your-oss-source-into.html' title='Do you check in your OSS source into trunk?'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-113242534674971114</id><published>2005-11-19T12:30:00.000-06:00</published><updated>2005-11-19T13:10:42.376-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><title type='text'>The best Visual Studio.NET blogging companion</title><content type='html'>Check this out, all I did was in VS 2003, right-click, select "Copy as HTML...", click OK, and CTRL-V in blogger. All of a sudden you get this stylish code colorization in a blog:&lt;br /&gt;&lt;br /&gt;&lt;div    style="border: 1pt solid windowtext; padding: 0pt; background: white none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial;font-family:Bitstream Vera Sans Mono Bold;font-size:10pt;color:black;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color:blue;"&gt;    public&lt;/span&gt; &lt;span style="color:blue;"&gt;class&lt;/span&gt; Bootstrap : IDisposable&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;    {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;        &lt;span style="color:blue;"&gt;private&lt;/span&gt; IMutablePicoContainer picoContainer;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt; &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;        [STAThread]&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;        &lt;span style="color:blue;"&gt;public&lt;/span&gt; &lt;span style="color:blue;"&gt;static&lt;/span&gt; &lt;span style="color:blue;"&gt;void&lt;/span&gt; Main()&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;        {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;            &lt;span style="color:blue;"&gt;try&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;            {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;                &lt;span style="color:blue;"&gt;using&lt;/span&gt; (Bootstrap bootstrap = &lt;span style="color:blue;"&gt;new&lt;/span&gt; Bootstrap())&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;                {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;                    IMainForm mainForm = bootstrap.BuildMainForm();&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;                    Application.Run((Form) mainForm);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;                }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;            }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;            &lt;span style="color:blue;"&gt;catch&lt;/span&gt; (Exception e)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;            {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;                MessageBox.Show(e.ToString());&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;            }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;        }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Awesome VS.NET Add-in! &lt;a href="http://www.jtleigh.com/CopySourceAsHtml/"&gt;CopyAsHTMLSource&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-113242534674971114?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/113242534674971114/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=113242534674971114' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/113242534674971114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/113242534674971114'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2005/11/best-visual-studionet-blogging.html' title='The best Visual Studio.NET blogging companion'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-113242436838665084</id><published>2005-11-19T12:07:00.000-06:00</published><updated>2005-11-19T12:20:25.516-06:00</updated><title type='text'>Web 2.0... the what?</title><content type='html'>Recently I am hearing more and more about Web 2.0. So what the hell is it? It seems to me no one really knows exactly what it is, but among all opinions they all point to the same direction: enhanced web application's user experience. I guess the industry is looking for the next buzz word after AJAX.&lt;br /&gt;&lt;br /&gt;In my company's forum, a couple web sites have been mentioned, and they really impressed me with what they mean by "user experience":&lt;br /&gt;&lt;a href="http://script.aculo.us/"&gt;script.aculo.us&lt;/a&gt;&lt;br /&gt;&lt;a href="http://openrico.org/rico/home.page"&gt;Rico&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Man I am falling in love with the head-shaking textbox in script.aculo.us!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-113242436838665084?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/113242436838665084/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=113242436838665084' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/113242436838665084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/113242436838665084'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2005/11/web-20-what.html' title='Web 2.0... the what?'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-112898170377730348</id><published>2005-10-10T16:50:00.000-05:00</published><updated>2005-10-10T23:23:55.230-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><title type='text'>How to mock out event calls in NMock?</title><content type='html'>When it comes to .NET eventing, a lot of developers barf at and not knowing how to test them. one is that the event handler methods are correctly wired to the corresponding events, and two the event handler code does what it's supposed to. The first is hard to test, because the wiring of a method to the event is internal to the containing class only. The second is easier, because you could stub out the event handler method and making sure that it is getting called, but pure mockists would dislike this approach.&lt;br /&gt;&lt;br /&gt;Consider the following example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public interface IBattery {&lt;br /&gt;    event EventHandler Low;&lt;br /&gt;    event EventHandler Depleted;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Battery : IBattery {&lt;br /&gt;    public event EventHandler Low;&lt;br /&gt;    public event EventHandler Depleted;&lt;br /&gt;    &lt;br /&gt;    public Battery() {&lt;br /&gt;        Low += new EventHandler(OnLowBattery);&lt;br /&gt;        Depleted += new EventHandler(OnDepleted);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public void SomeMethodThatConsumesBattery() {&lt;br /&gt;        .&lt;br /&gt;        .&lt;br /&gt;        .&lt;br /&gt;        if (IsLowBatter("10%")) {&lt;br /&gt;            OnLowBattery(this, EventArgs.Empty);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected virtual void OnLowBattery(object sender, EventArgs e) {&lt;br /&gt;        if (Low!= null) {&lt;br /&gt;            Low(sender, e);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public interface ILaptop { }&lt;br /&gt;&lt;br /&gt;public class Laptop : ILaptop {&lt;br /&gt;    private IBattery battery;&lt;br /&gt;&lt;br /&gt;    public Laptop(IBattery battery) {&lt;br /&gt;        this.battery = battery;&lt;br /&gt;        &lt;br /&gt;        batter.Low += new EventHandler(OnLowBattery);        &lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    protected virtual void OnLowBattery(object sender, EventArgs e) {&lt;br /&gt;        // Count down 15 mins before shutdown!&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We want to unit test the events are wired for our Laptop class, but we want to avoid changing anything any of the classes for the purpose of unit testing, since I am a big advocate of "never change your production code just for testing." You may argue that I have a "protected virtual void" event handler method there to leave room for myself for stubbing, but as far as event handling method goes, I think it is actually a good idea to allow my subclasses to override and extend my default implementation. This is the standard and encouraged way of writing and declaring event handling methods too in writing custom ASP.NET server controls. Check out Nikhil Kothari's book.&lt;br /&gt;&lt;br /&gt;So, solution one, create a stub class for our Battery class in testing Laptop, and add extra methods to manually raise the events:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class BatteryStub : Battery {&lt;br /&gt;    privately bool lowBatteryExecuted = false;&lt;br /&gt;&lt;br /&gt;    public void RaiseLowBattery() {&lt;br /&gt;        Low(this, EventArgs.Empty);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    protected override void OnLowBattery(object sender, EventArgs e) {&lt;br /&gt;        lowBatteryExecuted = true;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public void Verify()&lt;br /&gt;    {&lt;br /&gt;        if (!lowBatteryExecuted) throw new ApplicationException();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class LaptopTests {&lt;br /&gt;    &lt;br /&gt;    [Test]&lt;br /&gt;    public void StartCountdownWhenLowBattery() {&lt;br /&gt;        IBattery batteryStub = new BatteryStub();&lt;br /&gt;        ILaptop laptop = new Laptop(batteryStub);&lt;br /&gt;&lt;br /&gt;        batteryStub.RaiseLowBattery();&lt;br /&gt;&lt;br /&gt;        // Assert laptop countdown started.&lt;br /&gt;&lt;br /&gt;        battery.Verify();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's a lot of code to just unit test a single event is wired correctly. Also this stub is really doing a lot of a mock's work. Look at the Verify() and the simplistic stubbed event handler method. The fact that they are there and are simply is because in testing we are not interested in what they do, but that they are getting called. Now, duplicate this kind of test stub class for every object you have events, and you will quickly lose appetite on how many you have to write for all your domain objects.&lt;br /&gt;&lt;br /&gt;Fortuntely there is a second solution, if you use NMock 1.1:&lt;br /&gt;&lt;br /&gt;(credit to my co-worker and talented friend Levi Khatskevitch)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class DynamicEventMock : DynamicMock&lt;br /&gt;{&lt;br /&gt;    private const string ADD_PREFIX = "add_";&lt;br /&gt;    private const string REMOVE_PREFIX = "remove_";&lt;br /&gt;    &lt;br /&gt;    private EventHandlerList handlers = new EventHandlerList();&lt;br /&gt;    &lt;br /&gt;    public override object Invoke(string methodName, params object[] args)&lt;br /&gt;    {&lt;br /&gt;        if (methodName.StartsWith(ADD_PREFIX))&lt;br /&gt;        {&lt;br /&gt;            handlers.AddHandler(GetKey(methodName, ADD_PREFIX), (Delegate) args[0]);&lt;br /&gt;            return null;&lt;br /&gt;        }&lt;br /&gt;        if (methodName.StartsWith(REMOVE_PREFIX))&lt;br /&gt;        {&lt;br /&gt;            handlers.RemoveHandler(GetKey(methodName, REMOVE_PREFIX), (Delegate) args[0]);&lt;br /&gt;            return null;&lt;br /&gt;        }&lt;br /&gt;        return base.Invoke(methodName, args);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public void RaiseEvent(string eventName, params object[] args)&lt;br /&gt;    {&lt;br /&gt;        Delegate handler = handlers[eventName];&lt;br /&gt;&lt;br /&gt;        if (handler == null)&lt;br /&gt;        {&lt;br /&gt;            if (mockedType.GetEvent(eventName) == null)&lt;br /&gt;            {&lt;br /&gt;                throw new MissingMemberException("Event " + eventName + " is not defined");&lt;br /&gt;            }&lt;br /&gt;            else if (Strict)&lt;br /&gt;            {&lt;br /&gt;                throw new ApplicationException("Event " + eventName + " is not handled");&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        handler.DynamicInvoke(args);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    private static string GetKey(string methodName, string prefix)&lt;br /&gt;    {&lt;br /&gt;        return string.Intern(methodName.Substring(prefix.Length));&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, in your test class:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class LaptopTests {&lt;br /&gt;    &lt;br /&gt;    [Test]&lt;br /&gt;    public void BatteryLowIsRaised()&lt;br /&gt;    {&lt;br /&gt;        DynamicEventMock mockBattery = new DynamicEventMock(typeof(IBattery));&lt;br /&gt;        &lt;br /&gt;        ILaptop laptop = new Laptop((IBattery)mockBattery.MockInstance);&lt;br /&gt;        &lt;br /&gt;        mockBattery.RaiseEvent("PlayClick", EventArgs.Empty);&lt;br /&gt;        &lt;br /&gt;        // Assert the laptop instance's 15 mins count down started&lt;br /&gt;        &lt;br /&gt;        mockBattery.Verify();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Should the Low event wasn't wired, this test fails. You have explicitly told the mock object to raise its event, and assuming your events are wired correctly to the correct event handler, you have control over when to fire them.&lt;br /&gt;&lt;br /&gt;There are drawbacks, of course. Notice the event name is represented by a string, making event renaming a pain to do, as it is for NMock for method expectations. My only advise is try out EasyMock.NET if that is a real pain for you.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-112898170377730348?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/112898170377730348/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=112898170377730348' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/112898170377730348'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/112898170377730348'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2005/10/how-to-mock-out-event-calls-in-nmock.html' title='How to mock out event calls in NMock?'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-112896225182415083</id><published>2005-10-10T11:37:00.000-05:00</published><updated>2007-11-27T23:11:38.277-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby/rails'/><title type='text'>Ruby on Rails demo</title><content type='html'>A lot of buzz has been generated by Ruby, and given that Ruby on Rails pushes that hype to another level it sure is worth a look. This video from rubyonrails.com is a great tutorial on what is Ruby on Rails and how it works.&lt;br /&gt;&lt;br /&gt;http://www.rubyonrails.com.nyud.net:8090/media/video/rails_take2_with_sound.mov&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-112896225182415083?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/112896225182415083/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=112896225182415083' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/112896225182415083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/112896225182415083'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2005/10/ruby-on-rails-demo.html' title='Ruby on Rails demo'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-112749238623048749</id><published>2005-09-23T11:12:00.000-05:00</published><updated>2005-09-23T11:46:56.530-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='pattern'/><title type='text'>Container framework in .NET - yes - 1.1</title><content type='html'>Haven't blogged for a good while... =P&lt;br /&gt;&lt;br /&gt;I have been looking at the System.ComponentModel namespace. It is obviously an area where I have constantly overlooked. What is in there? There is a ton in there that the Visual Studio .NET IDE extensively uses, so it means it is useless to us as developers, right? Maybe not.&lt;br /&gt;&lt;br /&gt;Recently I have found myself interested in digging deeper into PicoContainer.NET. It allows you to design better OO business objects by decoupling your business objects from one another (through IoC). For those who aren't familiar with IoC (Inversion of Control), basically it means in this context instead of having object A to instantiate and use object B in it, object A will ask for an instance of object B in its ctor at its construction time. How it helps OO design is that now object A no longer has a irreplaceable link to object B, thus they are decoupled, and it allows us to test object A easily by either stubbing or mocking out object B.&lt;br /&gt;&lt;br /&gt;In the above example, object A and object B are "&lt;span style="font-style: italic;"&gt;Components&lt;/span&gt;". They will be put in a "&lt;span style="font-style: italic;"&gt;Container&lt;/span&gt;" and Pico will automagically instantiate object B when you request for an instance of object A.&lt;br /&gt;&lt;br /&gt;Back to the System.ComponentModel namespace, it also contains interfaces &lt;span style="font-style: italic;"&gt;IContainer&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;IComponent&lt;/span&gt;. Now things get interesting, each &lt;span style="font-style: italic;"&gt;IComponent&lt;/span&gt; is called "sited" after being added into a &lt;span style="font-style: italic;"&gt;IContainer&lt;/span&gt; (like the glue between the Container and the Component). A component can now call &lt;span style="font-style: italic;"&gt;base.Site.GetService(typeof(anotherComponent))&lt;/span&gt; to access another component's functionality. For our example, in object A's ctor, it can call &lt;span style="font-style: italic;"&gt;base.Site.GetService(typeof(ObjectB))&lt;/span&gt; to retrieve an instance of object B and create itself, without having to know how to "hard create" an instance of object B.&lt;br /&gt;&lt;br /&gt;How does it help testing? For testing object A, one can stub or mock out its &lt;span style="font-style: italic;"&gt;Site&lt;/span&gt; property and provide a testing implementation when its &lt;span style="font-style: italic;"&gt;GetService()&lt;/span&gt; is getting called. This pattern (or good practice) is very important to design decoupled OO systems.&lt;br /&gt;&lt;br /&gt;So when to use what? My take is that one should use Pico in your bootstrap class (&lt;span style="font-style: italic;"&gt;public static void Main(args[])&lt;/span&gt;), and since it is considered &lt;a href="http://www.picocontainer.org/Container+Dependency"&gt;harmful&lt;/a&gt; to use Pico in anywhere deeper than your bootstrap class, I will consider using the System.ComponentModel stuff in everywhere else I think using a Container-Component pattern will help decoupling my design.&lt;br /&gt;&lt;br /&gt;Some excellent reads on the topic:&lt;br /&gt;&lt;a href="http://www.picocontainer.org/"&gt;PicoContainer&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.urbanpotato.net/default.aspx/document/1757"&gt;[Urban Potato] Geek: System.ComponentModel&lt;/a&gt;&lt;br /&gt;&lt;a href="http://weblogs.asp.net/cazzu/archive/2004/05/10/129140.aspx"&gt;[Daniel Cazzulino] Lightweight Containers and Plugin Architectures: Dependency Injection and Dynamic Service Locators in .NET&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-112749238623048749?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/112749238623048749/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=112749238623048749' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/112749238623048749'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/112749238623048749'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2005/09/container-framework-in-net-yes-11.html' title='Container framework in .NET - yes - 1.1'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-111843099618983521</id><published>2005-06-10T14:10:00.000-05:00</published><updated>2005-06-12T13:38:48.503-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='efficiency'/><title type='text'>Want to whip up some quick UI Tree?</title><content type='html'>I had to produce for one of my clients some form of UI Tree to help them to focus on how various pieces of user interactions with the system is going to take place from a UI perspective. It went from paper bubbles to Visio-lizing it and client still don't like it. Finally, I stumbled across &lt;a href="http://www.thebrain.com/"&gt;this product&lt;/a&gt; that saves the day.&lt;br /&gt;&lt;br /&gt;It is basically a map of thoughts organized in a click-me-and-drill-down-as-you-go application. The point is, it proides a very easy drag-and-drop tree creating inetrface to do the job very quickly.&lt;br /&gt;&lt;br /&gt;At the end of the day this did wow the people who saw it, and it is extremely easy to use. It got the job done. I am however interested in the many ways of people who create the so-called UI Tree how they do it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-111843099618983521?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/111843099618983521/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=111843099618983521' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/111843099618983521'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/111843099618983521'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2005/06/want-to-whip-up-some-quick-ui-tree.html' title='Want to whip up some quick UI Tree?'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-111697168844087109</id><published>2005-05-24T16:45:00.000-05:00</published><updated>2005-10-07T18:02:46.656-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><title type='text'>Connecting a Pocket PC emulator to host internet in Whidbey</title><content type='html'>This tip is for developing environment which has Whidbey Beta 2. I do not know if it will work (probably won't) on a Virtual PC environment.&lt;br /&gt;&lt;br /&gt;If you are trying to connect your Pocket PC emulator to the internet because you are developing/deploying perhaps a web service on your host system, and you want your emulator to be able to consume it in your debugging, here is a few things in sequence I have tried to get it to work:&lt;br /&gt;&lt;br /&gt;- Restart your computer. Yes.&lt;br /&gt;- Open your .sln by double-clicking it in your Explorer, not by opening your Whidbey and then selecting it from your File/"Recent Projects". Apparently there is a funky difference.&lt;br /&gt;- Run F5 to bring up the emulator.&lt;br /&gt;- It will probably tell you "Cannot connect to device", but that's okay.&lt;br /&gt;- In Whidbey, go to Tools/"Device Emulator Manager", set your emulating device to Cradle.&lt;br /&gt;- Reset the state of your emulator.&lt;br /&gt;- Close down the emulator.&lt;br /&gt;- Run F5 again to bring it back up.&lt;br /&gt;- In your emulator Connections/Advanced/"Select Networks", select "My Work Network" in the first droplist for programs that need access to the internet.&lt;br /&gt;- Go to its Edit/"Proxy Settings", uncheck the box "This network connects to the internet". Notice this box will automatically be checked again by the emulator once it can connect to the internet.&lt;br /&gt;- And finally, verify that you have &lt;span style="font-style: italic;"&gt;turned your firewall software off&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;This would work in the emulator IE whether you type "http://localhost&lt;ip&gt;/MyService.asmx" or "http://&lt;/ip&gt;localhost&lt;ip&gt;&lt;machinename&gt;/MyService.asmx". If I were you, I would see if I could even connect to Google first in your emulator IE before testing the web service.&lt;/machinename&gt;&lt;/ip&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-111697168844087109?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/111697168844087109/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=111697168844087109' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/111697168844087109'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/111697168844087109'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2005/05/connecting-pocket-pc-emulator-to-host.html' title='Connecting a Pocket PC emulator to host internet in Whidbey'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-111635845321479972</id><published>2005-05-17T14:24:00.000-05:00</published><updated>2005-05-17T15:26:04.386-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><title type='text'>Dynamic deployment config settings</title><content type='html'>Using Visual Studio .NET 2003, there are different options to dynamically use app/web.config settings depending on what environment the build is deployed on. One of which is in your code, use preprocessor directives like #if, #elif, and #endif. I don't like this approach, period. This is really ugly. What if you have 5 deployment environments? You will end up with values with keys "dbConnStringDev", "dbConnStringTest", "dbConnStringQA", "dbConnStringProd", etc.&lt;br /&gt;&lt;br /&gt;Another option is have inside your app/web.config file, just create one key-value pair for each setting regardless of how many deploying environments you might have (eg. &lt;add key="dbConnString" value="Data Source=..."&gt;), and then after each deployment, manually (or automate this by some scripts) go and edit the settings for all these. Well... this is just as bad as a single mistype would deem a deployment failed.&lt;br /&gt;&lt;br /&gt;A third option would be, if you have separate a set of physical machines per deployment environment, one could use each machine's machine.config as the definitive setting storage, and then override them with local app/web.config in for example your developer's .NET projects. Question arises if for example you have five different deployment environments (dev, test, QA, stage, and prod) but you only have two boxes for your web services server across all of these environments: how do you direct traffic for dev, test, and QA to box A while stage and prod to box B. It's the same problem all over again.&lt;br /&gt;&lt;br /&gt;The solution (well, not really) I have found so far is this. In the .csproj that could house a app/web.config file, you can find the following lines for each solution configuration:&lt;br /&gt;&lt;/add&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;&amp;lt;Config&lt;br /&gt;      Name = "Release"&lt;br /&gt;      AllowUnsafeBlocks = "false"&lt;br /&gt;      BaseAddress = "285212672"&lt;br /&gt;      CheckForOverflowUnderflow = "false"&lt;br /&gt; &lt;/span&gt;       &lt;span style="font-weight: bold;font-size:85%;" &gt;ConfigurationOverrideFile = "app.release.config"&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;      DefineConstants = "TRACE"&lt;br /&gt;      DocumentationFile = ""&lt;br /&gt;      DebugSymbols = "false"&lt;br /&gt;      FileAlignment = "4096"&lt;br /&gt;      IncrementalBuild = "false"&lt;br /&gt;      NoStdLib = "false"&lt;br /&gt;      NoWarn = ""&lt;br /&gt;      Optimize = "true"&lt;br /&gt;      OutputPath = "bin\Release\"&lt;br /&gt;      RegisterForComInterop = "false"&lt;br /&gt;      RemoveIntegerChecks = "false"&lt;br /&gt;      TreatWarningsAsErrors = "false"&lt;br /&gt;      WarningLevel = "4"&lt;br /&gt;      ...&lt;/span&gt;&lt;br /&gt;     &lt;/blockquote&gt;if you create a new .config file for each deployment environment that you have, then create a new solution configuration for each deployment envionment (eg. Debug, Release, QA, Stage, Production), then change that bolded line for each deployment into the appropriate .config file, then create a Setup project in your solution and build it with the solution configuration you want for your deployment, the output .msi will include *a* app.config file, with it being replaced with the file you specified in the above bolded text. So in the example above, app.release.config will be used and renamed to app.config when the output .msi is deployed onto the environment.&lt;br /&gt;&lt;br /&gt;This is so far the cleanest solution for me. But it does tie you into using Setup project for your deployment configuration. And from what I have heard, .msi has not been a good deployment strategy for complex application deployment (yes, the fact that there is a relational database built into a .msi does not make it suit for enterprise level if it is complex to use).&lt;br /&gt;&lt;br /&gt;Still looking into alternatives... on a side note, the fact that Microsoft is switching from "no-touch deployment" into "one-click deployment" means to me that the marketeers at MS is going one step to far in their marketing effort =P&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11933937-111635845321479972?l=www.stephenchu.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.stephenchu.com/feeds/111635845321479972/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11933937&amp;postID=111635845321479972' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/111635845321479972'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11933937/posts/default/111635845321479972'/><link rel='alternate' type='text/html' href='http://www.stephenchu.com/2005/05/dynamic-deployment-config-settings.html' title='Dynamic deployment config settings'/><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='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11933937.post-111535345876965695</id><published>2005-05-05T22:59:00.000-05:00</published><updated>2005-05-05T23:24:41.973-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><title type='text'>Whidbey Beta 2 Visual J# Redistributable, cannot load package problem</title><content type='html'>I was having a tough time to install Whidbey Beta 2 onto not my Virtual PC instance, but onto my host OS Windows XP Prof. I installed it three times in total, fortunately I have my laptop backed up before I acted.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The problem I was having was that during installation, the wizard tells me that the component 'Microsoft Visual J# Redistributable Package 2.0 Beta 2' installation was unsuccessful. The Whidbey install went on, but as I
