<?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-3427575211647135852</id><updated>2012-01-08T19:10:07.857-05:00</updated><category term='Toronto'/><category term='ruby'/><category term='KDE'/><category term='Linux Mint'/><category term='CSS'/><category term='movies'/><category term='Debian'/><category term='books'/><category term='ActiveRecord'/><category term='Rails'/><category term='WordPress'/><category term='Etch'/><category term='military'/><category term='wtf'/><category term='reasoning'/><category term='Sinatra'/><category term='routes'/><category term='motivation'/><category term='PHP'/><category term='Australia'/><category term='rspec'/><category term='dreams'/><category term='Cucumber'/><category term='food'/><category term='resources'/><category term='Linux'/><category term='Mac OS'/><category term='parkour'/><category term='coding'/><category term='mod_rewrite'/><category term='marketing'/><category term='Plasma'/><category term='design'/><category term='mod_rails'/><category term='Apache'/><category term='testing'/><category term='health'/><category term='Passenger'/><category term='conventions'/><category term='startups'/><title type='text'>nickh</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>23</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-697899247115132048</id><published>2009-10-15T21:12:00.005-04:00</published><updated>2009-10-15T21:59:25.144-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='KDE'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux Mint'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Plasma'/><title type='text'>Linux Mint Is Weird</title><content type='html'>I have a MacBook Pro. I bought it back in 2007 when I was living in Australia. It's awesome. I love it, especially the keyboard. Unfortunately, Mac OS X has pissed me off way too many times. So I jumped ship to Linux Mint 7.&lt;br /&gt;&lt;br /&gt;Out of the box, Mint 7 supports my Mac's Atheros wifi card &lt;strong&gt;and&lt;/strong&gt; ATI video card. That's something that not even Ubuntu managed to do. Needless to say, I was impressed.&lt;br /&gt;&lt;br /&gt;After installing and configuring most of Mint 7, things were looking pretty good. As some of you know, I customize KDE heavily. Shortcuts, panels, virtual desktops, colour schemes, etc. Once all of that's configured as I like it, I don't touch'em: they're perfect (for me).&lt;br /&gt;&lt;br /&gt;Mint 7 ships with KDE 4 though, which uses Plasma. Trying to get Plasma configured just right is like trying to fine-tune a space ship: it sounds like fun, but it just Ain't Gonna Happen &lt;sup&gt;(tm)&lt;/sup&gt;.&lt;br /&gt;&lt;br /&gt;While trying to configure Plasma, I ran &lt;i&gt;sudo aptitude safe-upgrade&lt;/i&gt;, and went to bed. I awoke to hell. Well, desktop hell. None of my panels were showing, the &lt;a href="http://sidux.files.wordpress.com/2009/04/plasma_cashew2.png"&gt;Plasma "cashew"&lt;/a&gt; was missing, and right-clicking on the desktop did nothing. &lt;strong&gt;Fark!&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;To make a long story short, Steely in #LinuxMint.com on irc.spotchat.org saved me. He corrected my sources.list , and after running mintupdate, life was peachy!&lt;br /&gt;&lt;br /&gt;Apparently, one is &lt;strong&gt;not&lt;/strong&gt; supposed to use aptitude's &lt;i&gt;safe-upgrade&lt;/i&gt; tool, because it'll install packages that Linux Mint doesn't want you to.&lt;br /&gt;&lt;br /&gt;How/why/what/?!?&lt;br /&gt;&lt;br /&gt;According to Steely:&lt;pre&gt;mint will fix packages or omit items until they get what they consider a stable base&lt;br /&gt;mint uses updates levels 1 to 5 and mintupdate is set to use levels 1 to 3 by default, 4 and 5&lt;br /&gt;has kernel and driver and other package updates&lt;br /&gt;if you use mintupdate you will rarely have problems but if you use the terminal it ignores&lt;br /&gt;levels and can introduce packages that conflict with mint's patches / fixes&lt;/pre&gt;&lt;br /&gt;So there you have it. When running Linux Mint, &lt;strong&gt;use &lt;i&gt;mintUpdate&lt;/i&gt;, not &lt;i&gt;aptitude&lt;/i&gt;, to perform upgrades&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;In case anyone else is plagued by this problem, the solution is to comment out this line in /etc/apt/sources.list :&lt;pre&gt;deb http://ppa.launchpad.net/kubuntu-ppa/ppa/ubuntu jaunty main&lt;/pre&gt;and uncomment this line:&lt;pre&gt;deb http://archive.ubuntu.com/ubuntu/ jaunty-backports main restricted universe multiverse&lt;/pre&gt;Next, use &lt;i&gt;mintUpdate&lt;/i&gt; to upgrade the rest of your packages, reboot, and pray to whichever god(s) you believe in.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-697899247115132048?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/697899247115132048/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=697899247115132048' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/697899247115132048'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/697899247115132048'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2009/10/linux-mint-is-weird.html' title='Linux Mint Is Weird'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-2435750170968687130</id><published>2009-09-22T11:04:00.005-04:00</published><updated>2009-09-22T11:16:39.066-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mod_rewrite'/><category scheme='http://www.blogger.com/atom/ns#' term='Apache'/><title type='text'>Idiosynchracy With Apache Rewrites</title><content type='html'>Did you know that it's not possible to apply an Apache rewrite condition to multiple rewrite rules? I thought this would work, but requests from every IP address were being redirected:&lt;br /&gt;&lt;pre&gt;RewriteCond %{REMOTE_ADDR} ^10\.0\.10\.10$&lt;br /&gt;RewriteRule ^uploads/(.*)$ /wp-content/uploads/$1&lt;br /&gt;RewriteRule (.*) http://deadorange.com/blog$1 [L,R=301]&lt;/pre&gt;&lt;br /&gt;It turns out that RewriteCond directives only apply to the following RewriteRule. So my rewrite directives above were equivalent to:&lt;br /&gt;&lt;pre&gt;If the request came from 10.0.10.10, rewrite /uploads/.* to /wp-content/uploads/.* .&lt;br /&gt;Next, redirect everyone to http://deadorange.com/blog$1 .&lt;/pre&gt;&lt;br /&gt;Unfortunately, the only solution is to repeat the condition:&lt;br /&gt;&lt;pre&gt;RewriteCond %{REMOTE_ADDR} ^10\.0\.10\.10$&lt;br /&gt;RewriteRule ^uploads/(.*)$ /wp-content/uploads/$1&lt;br /&gt;RewriteCond %{REMOTE_ADDR} ^10\.0\.10\.10$&lt;br /&gt;RewriteRule (.*) http://deadorange.com/blog$1 [L,R=301]&lt;/pre&gt;&lt;br /&gt;That isn't very &lt;a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY&lt;/a&gt;, but if it's the only way, we have to live with it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-2435750170968687130?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/2435750170968687130/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=2435750170968687130' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/2435750170968687130'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/2435750170968687130'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2009/09/idiosynchracy-with-apache-rewrites.html' title='Idiosynchracy With Apache Rewrites'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-1679893496442610487</id><published>2009-09-17T10:26:00.003-04:00</published><updated>2009-09-17T10:50:03.139-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='Apache'/><category scheme='http://www.blogger.com/atom/ns#' term='WordPress'/><title type='text'>Importing large WordPress blogs</title><content type='html'>I just finished importing a WordPress blog with &gt;1500 posts into a different WordPress blog. Importing the posts succeeded, but the last few steps at the end failed, like recalculating how many posts are in each category. This happened because the page always timed out; the import took more than 10 minutes.&lt;br /&gt;&lt;br /&gt;If you find yourself with this problem, the fix is easy:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;First, comment out the following line in wp-admin/import/wordpress.php . In WordPress 2.8.4, it's line 367.&lt;pre&gt;set_time_limit( 60 );&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Configure your web server to allow PHP scripts to execute for a long time. In Apache, you do that with the "Timeout" directive:&lt;pre&gt;Timeout 6000&lt;/pre&gt;This can be put within a specific virtual host, or configured globally.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Increase PHP's max execution time. This is done with the "max_execution_time" setting in the appropriate php.ini :&lt;pre&gt;max_execution_time = 600    ; Maximum execution time of each script, in seconds&lt;/pre&gt;or within your web server's configuration, if that's allowed. For example, within Apache, you include this globally, or within a virtual host:&lt;pre&gt;&amp;lt;IfModule mod_php5.c&amp;gt;&lt;br /&gt;  php_value max_execution_time 6000&lt;br /&gt;&amp;lt;/IfModule&amp;gt;&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-1679893496442610487?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/1679893496442610487/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=1679893496442610487' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/1679893496442610487'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/1679893496442610487'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2009/09/importing-large-wordpress-blogs.html' title='Importing large WordPress blogs'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-373318770894776747</id><published>2009-08-21T16:07:00.013-04:00</published><updated>2010-09-22T15:07:38.261-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><title type='text'>Hashes and Frozen Keys</title><content type='html'>The hash class &lt;a href="http://www.ruby-doc.org/core/classes/Hash.html#M002851"&gt;freezes keys&lt;/a&gt; that are strings:&lt;br /&gt;&lt;script src="http://gist.github.com/172416.js"&gt;&lt;/script&gt;&lt;br /&gt;This can be a problem if you want to use those keys later on...more specifically, if you want to extract and edit those strings:&lt;br /&gt;&lt;script src="http://gist.github.com/172417.js"&gt;&lt;/script&gt;&lt;br /&gt;Unfortunately, those strings will have to be duped &lt;span style='text-decoration: line-through;'&gt;or cloned&lt;/span&gt;:&lt;br /&gt;&lt;script src="http://gist.github.com/173453.js"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-373318770894776747?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/373318770894776747/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=373318770894776747' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/373318770894776747'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/373318770894776747'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2009/08/hashes-and-frozen-keys.html' title='Hashes and Frozen Keys'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-3543494130451841939</id><published>2009-08-13T16:24:00.002-04:00</published><updated>2009-08-13T16:56:53.359-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='mod_rails'/><category scheme='http://www.blogger.com/atom/ns#' term='Passenger'/><category scheme='http://www.blogger.com/atom/ns#' term='Sinatra'/><title type='text'>Sinatra and Passenger/mod_rails</title><content type='html'>If you want Apache to serve up a Sinatra app, your best bet is to use &lt;a href="http://www.modrails.com/"&gt;Passenger&lt;/a&gt; (AKA mod_rails).&lt;br /&gt;&lt;br /&gt;All your app needs is the usual Rackup config file (config.ru) in the app's root directory, and the directory public/ .&lt;br /&gt;&lt;br /&gt;There's one caveat, though: if you have a file named environment.rb , do &lt;span style="font-weight: bold;"&gt;not&lt;/span&gt; put it in config/ .&lt;br /&gt;&lt;br /&gt;If mod_rails finds config/environment.rb , it'll think your app runs on Rails:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.flickr.com/photos/nphoffman/3818060253/sizes/o/"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 500px; height: 400px;" src="http://farm3.static.flickr.com/2604/3818060253_56f6e9c173.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-3543494130451841939?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/3543494130451841939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=3543494130451841939' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/3543494130451841939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/3543494130451841939'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2009/08/sinatra-and-passengermodrails.html' title='Sinatra and Passenger/mod_rails'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm3.static.flickr.com/2604/3818060253_56f6e9c173_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-1563161205601387568</id><published>2009-02-05T22:24:00.006-05:00</published><updated>2009-08-23T17:04:49.648-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='Etch'/><category scheme='http://www.blogger.com/atom/ns#' term='Debian'/><title type='text'>Ruby in Etch</title><content type='html'>As some of you may know, Ruby and Debian's ways of packaging software go together like Darwin and religious zealots...as in, &lt;a href="http://pkg-ruby-extras.alioth.debian.org/rubygems.html"&gt;they don't&lt;/a&gt;. So how should you install Ruby when using Debian Stable? How do you stay up-to-date with new versions of Ruby and gems?&lt;br /&gt;&lt;br /&gt;By using Backports, and not worrying so much.&lt;br /&gt;&lt;br /&gt;First, setup your box to be able to install &lt;a href="http://backports.org/dokuwiki/doku.php?id=instructions"&gt;backports of packages&lt;/a&gt;. Next, uninstall any Ruby-related packages. Yeah, that's right. Do you know why? Because, if you're running Etch, you're using Ruby 1.8.5! Egad! That's almost medieval. Make sure to uninstall irb and rdoc, too.&lt;br /&gt;&lt;br /&gt;Now it's time to install shinier versions...1.8.7, to be exact:&lt;pre&gt;$ sudo aptitude install -t etch-backports libruby1.8 ruby1.8 rdoc1.8 irb1.8&lt;/pre&gt;All of that should be on one line.&lt;br /&gt;&lt;br /&gt;There you have it. Now you've got Ruby 1.8.7 on Debian Stable (AKA "Etch"). Ruby's not complete without RubyGems, though. Download the &lt;a href="http://rubyforge.org/frs/?group_id=126"&gt;latest version&lt;/a&gt; to /usr/src/ , and extract it:&lt;pre&gt;$ cd /usr/src/&lt;br /&gt;$ wget http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz&lt;br /&gt;$ tar zxf rubygems-1.3.1.tgz&lt;/pre&gt;Then run the setup utility:&lt;pre&gt;$ cd rubygems-1.3.1/&lt;br /&gt;$ sudo ruby setup.rb&lt;/pre&gt;&lt;br /&gt;Let's do one last thing to make life easier. The RubyGems setup script installed the "gem" command as /usr/bin/gem1.8 . I don't want to have to type that "1.8" every time.&lt;pre&gt;$ cd /usr/local/bin/&lt;br /&gt;$ sudo ln -v -s /usr/bin/gem1.8 gem&lt;/pre&gt;&lt;br /&gt;There. Now we can run "gem" like the rest of the world.&lt;br /&gt;&lt;br /&gt;Easy? Yar.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-1563161205601387568?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/1563161205601387568/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=1563161205601387568' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/1563161205601387568'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/1563161205601387568'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2009/02/ruby-in-etch.html' title='Ruby in Etch'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-1739645637933233152</id><published>2009-01-28T19:10:00.008-05:00</published><updated>2009-09-17T10:56:18.800-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ActiveRecord'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Default stegosaurses</title><content type='html'>Ever heard of Jonathan Viney's &lt;a href="http://svn.viney.net.nz/things/rails/plugins/active_record_defaults/"&gt;active_record_defaults&lt;/a&gt; plugin? It's fantastic. In a nutshell, it enables you to specify default values for model attributes. If an attribute isn't set when initialising a new instance, the attribute's set to whatever the default value is.&lt;br /&gt;&lt;br /&gt;For example, let's rebuild that Stegosaurus class from a &lt;a href="http://nick-hoffman.blogspot.com/2009/01/friends-dont-let-friends-code-without.html"&gt;previous post&lt;/a&gt;:&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Stegosaurus&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;attr_accessible&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:back_plates&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_numericality_of&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="symbol"&gt;:only_integer&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;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="symbol"&gt;:greater_than_or_equal_to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_numericality_of&lt;/span&gt; &lt;span class="symbol"&gt;:back_plates&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="symbol"&gt;:only_integer&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;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="symbol"&gt;:greater_than_or_equal_to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;With that, if we created a new Stegosaurus and didn't specify any attributes, not only would it have no tail spikes or back plates, but the model instance wouldn't be valid.&lt;br /&gt;&lt;br /&gt;To fix that, all we need to do is add two lines:&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Stegosaurus&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;attr_accessible&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:back_plates&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;default&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;4&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;default&lt;/span&gt; &lt;span class="symbol"&gt;:back_plates&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;6&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_numericality_of&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="symbol"&gt;:only_integer&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;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="symbol"&gt;:greater_than_or_equal_to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_numericality_of&lt;/span&gt; &lt;span class="symbol"&gt;:back_plates&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="symbol"&gt;:only_integer&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;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="symbol"&gt;:greater_than_or_equal_to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And voila, new Stegosaurus objects will automatically have 4 tail spikes and 6 back plates:&lt;pre&gt;&amp;gt;&amp;gt; s = Stegosaurus.new&lt;br /&gt;=&amp;gt; #&amp;lt;Stegosaurus tail_spikes: 4, back_plates: 6&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; &lt;br /&gt;?&amp;gt; s.tail_spikes&lt;br /&gt;=&amp;gt; 4&lt;br /&gt;&amp;gt;&amp;gt; s.back_plates&lt;br /&gt;=&amp;gt; 6&lt;/pre&gt;&lt;br /&gt;However, if used improperly, this can lead to several hours of pounding your head on your desk. Note that that the default values we used above, 4 and 6, are immutable objects. You can't change them. You can perform operations on them, such as addition (4 + 1), but that never &lt;b&gt;changes&lt;/b&gt; the object.&lt;br /&gt;&lt;br /&gt;Say we want our Stegosaurus class to have an array specifying its diet that defaults to "fern" and "cycad":&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Stegosaurus&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;attr_accessible&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:back_plates&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:diet&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;default&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;4&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;default&lt;/span&gt; &lt;span class="symbol"&gt;:back_plates&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;6&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;default&lt;/span&gt; &lt;span class="symbol"&gt;:diet&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;fern cycad&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_numericality_of&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="symbol"&gt;:only_integer&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;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="symbol"&gt;:greater_than_or_equal_to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;validates_numericality_of&lt;/span&gt; &lt;span class="symbol"&gt;:back_plates&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="symbol"&gt;:only_integer&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;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="symbol"&gt;:greater_than_or_equal_to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That looks right. But watch this:&lt;pre&gt;=&amp;gt; charles = Stegosaurus.new&lt;br /&gt;=&amp;gt; #&amp;lt;Stegosaurus tail_spikes: 4, back_plates: 6, diet: ["fern", "cycad"]&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; &lt;br /&gt;?&amp;gt; arnold = Stegosaurus.new&lt;br /&gt;=&amp;gt; #&amp;lt;Stegosaurus tail_spikes: 4, back_plates: 6, diet: ["fern", "cycad"]&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; &lt;br /&gt;?&amp;gt; charles.diet &lt;&lt; 'moss'&lt;br /&gt;=&amp;gt; ["fern", "cycad", "moss"]&lt;br /&gt;&amp;gt;&amp;gt; &lt;br /&gt;?&amp;gt; arnold.diet&lt;br /&gt;=&amp;gt; ["fern", "cycad", "moss"]&lt;/pre&gt;We didn't mean to do that...Arnold doesn't want to eat moss!&lt;br /&gt;&lt;br /&gt;At the moment, when the Stegosaurus class is initialised, it creates one Array for the default "diet" attribute of all future Stegosaurus objects. In other words, Charles' and Arnolds' "diet" attributes were references to the same object:&lt;pre&gt;&amp;gt;&amp;gt; charles.diet.object_id&lt;br /&gt;=&amp;gt; 19103090&lt;br /&gt;&amp;gt;&amp;gt; &lt;br /&gt;?&amp;gt; arnold.diet.object_id&lt;br /&gt;=&amp;gt; 19103090&lt;/pre&gt;&lt;br /&gt;How do we fix that? We tell the Stegosaurus class to create a new Array for each model instance. Simply change this:&lt;pre&gt;&lt;span class="ident"&gt;default&lt;/span&gt; &lt;span class="symbol"&gt;:diet&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;fern cycad&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;/pre&gt;to this:&lt;pre&gt;&lt;span class="ident"&gt;default&lt;/span&gt; &lt;span class="symbol"&gt;:diet&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;Proc&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="punct"&gt;%w(&lt;/span&gt;&lt;span class="string"&gt;fern cycad&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;br /&gt;Now each time we create a new Stegosaurus, that Proc will fire, and create a new "diet" Array:&lt;pre&gt;=&amp;gt; charles = Stegosaurus.new&lt;br /&gt;=&amp;gt; #&amp;lt;Stegosaurus tail_spikes: 4, back_plates: 6, diet: ["fern", "cycad"]&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; &lt;br /&gt;?&amp;gt; arnold = Stegosaurus.new&lt;br /&gt;=&amp;gt; #&amp;lt;Stegosaurus tail_spikes: 4, back_plates: 6, diet: ["fern", "cycad"]&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; &lt;br /&gt;?&amp;gt; charles.diet &lt;&lt; 'moss'&lt;br /&gt;=&amp;gt; ["fern", "cycad", "moss"]&lt;br /&gt;&amp;gt;&amp;gt; &lt;br /&gt;?&amp;gt; arnold.diet&lt;br /&gt;=&amp;gt; ["fern", "cycad"]&lt;/pre&gt;&lt;br /&gt;I highly recommend giving &lt;a href="http://svn.viney.net.nz/things/rails/plugins/active_record_defaults/"&gt;active_record_defaults&lt;/a&gt; a try. It's very handy, and very easy to use.&lt;br /&gt;&lt;br /&gt;And by the way, yes, I was bitten by this problem. Why else would I be writing about it! Can you guess how I discovered it, though? ... My &lt;a href="http://nick-hoffman.blogspot.com/2009/01/testingits-what-lifes-all-about.html"&gt;tests&lt;/a&gt; picked it up!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-1739645637933233152?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/1739645637933233152/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=1739645637933233152' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/1739645637933233152'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/1739645637933233152'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2009/01/default-stegosaurses.html' title='Default stegosaurses'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-2590368286685445945</id><published>2009-01-23T15:41:00.003-05:00</published><updated>2009-01-23T15:44:22.534-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='food'/><title type='text'>Toblerone</title><content type='html'>Why is &lt;a href="http://www.toblerone.com"&gt;Toblerone&lt;/a&gt; so damn tasty? Y'know what? It doesn't matter. All that matters is access to it when desired. My parents each gave me a huge Toblerone bar for Christmas. The first one went quickly, and I've been savouring the second one...but it's nearly done, too!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-2590368286685445945?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/2590368286685445945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=2590368286685445945' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/2590368286685445945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/2590368286685445945'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2009/01/toblerone.html' title='Toblerone'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-9081297248976794948</id><published>2009-01-16T15:12:00.008-05:00</published><updated>2009-01-23T15:45:05.407-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac OS'/><category scheme='http://www.blogger.com/atom/ns#' term='Toronto'/><title type='text'>Windows Preview: BSOD</title><content type='html'>The &lt;a href="http://www.theglobeandmail.com/servlet/story/RTGAM.20090116.wtoroutage0115/BNStory/National/home"&gt;power went out last night,&lt;/a&gt; so I'm at my Dad's house right now, leeching his heat, electricity, and intertubes connection.&lt;br /&gt;&lt;br /&gt;I opened up Finder, and noticed that MacOS had found a computer on the LAN. I clicked "Get Info" on it, and well..I'll let the image speak for itself:&lt;br /&gt;&lt;br /&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; width: 237px; height: 320px;" src="http://2.bp.blogspot.com/_yjqr5Jjq8ak/SXDrqsnd86I/AAAAAAAAABQ/RtUcUnYRhoo/s320/Mac_OS_preview_of_a_Windows_computer.png" alt="What Apple thinks of Windows" id="BLOGGER_PHOTO_ID_5291988680924066722" border="0" /&gt;&lt;br /&gt;I love finding small touches like that. It reminds me that there are real people at work behind the things we use every day.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-9081297248976794948?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/9081297248976794948/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=9081297248976794948' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/9081297248976794948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/9081297248976794948'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2009/01/windows-preview-bsod.html' title='Windows Preview: BSOD'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_yjqr5Jjq8ak/SXDrqsnd86I/AAAAAAAAABQ/RtUcUnYRhoo/s72-c/Mac_OS_preview_of_a_Windows_computer.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-5519726363266558687</id><published>2009-01-13T14:41:00.005-05:00</published><updated>2009-09-17T10:58:38.694-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rspec'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>RSpec Project Template</title><content type='html'>Who doesn't love starting a new project? It's exciting. Loads of fun! But it's a bit of a pain in the ass to have to setup the project's directory structure, a rake-file, a spec-helper, etc each time.&lt;br /&gt;&lt;br /&gt;To make life a bit easier when starting a new [non-Rails] Ruby project, I've released &lt;a href="http://github.com/nickhoffman/ruby_project_template/"&gt;ruby_project_template&lt;/a&gt;. Anyone can use it as a base from which to start new Ruby projects that use RSpec for testing/speccing.&lt;br /&gt;&lt;br /&gt;It's quite basic, but that's because you don't need, or want, much to start with.&lt;br /&gt;&lt;br /&gt;By the way, &lt;a href="http://github.com/nickhoffman/ruby_project_template/"&gt;ruby_project_template&lt;/a&gt; is hosted at Github, which means that you &lt;a href="http://www.google.com/search?q=how+to+install+Git"&gt;need Git to be installed&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;To get going, just click on &lt;a href="http://github.com/nickhoffman/ruby_project_template/"&gt;ruby_project_template&lt;/a&gt;, read the short readme, and you'll be off to the races!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-5519726363266558687?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/5519726363266558687/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=5519726363266558687' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/5519726363266558687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/5519726363266558687'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2009/01/rspec-project-template.html' title='RSpec Project Template'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-8262969156165673504</id><published>2009-01-08T12:54:00.017-05:00</published><updated>2009-01-28T19:18:42.464-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rspec'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Friends don’t let friends code without tests</title><content type='html'>But what if your friends don't write tests? Or what if you're just lazy? Well, if the latter applies, that's your own damn fault. But if nobody around you tests their code, you're probably not going to be motivated to test your own code. But, fear not! RSpec is here!&lt;br /&gt;&lt;br /&gt;RSpec is a really awesome testing framework. By "awesome", I mean that it:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;makes it easy to write tests;&lt;/li&gt;&lt;li&gt;makes your tests easy to read;&lt;/li&gt;&lt;li&gt;gives you an easy development path to follow.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;It accomplishes this by following &lt;a href="http://209.85.229.132/search?q=cache:r-hQvNP_81YJ:dannorth.net/introducing-bdd+behaviour+driven+development&amp;amp;hl=en&amp;amp;ct=clnk&amp;amp;cd=6" title="Dan North's site seems to be having problems at the moment, so we have to resort to Google's cache."&gt;behaviour-driven development (BDD)&lt;/a&gt;. If you're not a keener, don't bother following that link. Just read on for the stegosaurus.&lt;br /&gt;&lt;br /&gt;With BDD, you write specs that describe your application's behaviour in terms of what it &lt;span style="font-weight: bold;"&gt;should&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;shouldn't&lt;/span&gt; do.&lt;br /&gt;&lt;br /&gt;Let's write an app to mimic one of my favourite dinosaurs. And while we're at it, we'll do some testing. But first, install RSpec:&lt;pre&gt;&lt;span class="ident"&gt;sudo&lt;/span&gt; &lt;span class="ident"&gt;gem&lt;/span&gt; &lt;span class="ident"&gt;install&lt;/span&gt; &lt;span class="ident"&gt;rspec&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Stegosauruses are known for 2 things. Let's start with those awesome tail spikes:&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Stegosaurus&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;  attr_accessor&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;That's pretty simple. So how do we spec this? We start off by describing what we want our application to do. In this case, we should be able to grab the "tail_spikes" attribute:&lt;pre&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;stegosaurus&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;should have tail spikes&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes=&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;Run the spec, and we're off to the races!&lt;pre&gt;$ spec stegosaurus_spec.rb&lt;br /&gt;.&lt;br /&gt;&lt;br /&gt;Finished in 0.015328 seconds&lt;br /&gt;&lt;br /&gt;1 example, 0 failures&lt;/pre&gt;&lt;br /&gt;We should also spec setting the number of tail spikes, so let's update the spec:&lt;pre&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;stegosaurus&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;should have tail spikes&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes=&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail_spikes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;4&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;equal&lt;/span&gt; &lt;span class="number"&gt;4&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;That's one of the most readable and easy-to-understand tests you've ever seen, eh?&lt;br /&gt;&lt;br /&gt;Stegosauruses are also known for the huge plates on their backs:&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Stegosaurus&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;attr_accessor&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;attr_accessor&lt;/span&gt; &lt;span class="symbol"&gt;:plates&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;The spec should be pretty obvious:&lt;pre&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;stegosaurus&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;should have tail spikes&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes=&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail_spikes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;4&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;equal&lt;/span&gt; &lt;span class="number"&gt;4&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;it&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;should have plates&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="symbol"&gt;:plates&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="symbol"&gt;:plates=&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plates&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;12&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plates&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;equal&lt;/span&gt; &lt;span class="number"&gt;12&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;br /&gt;Notice, though, that we're beginning to repeat ourselves. In both of those examples (#it blocks), we create a new Stegosaurus. Let's tidy that up a bit:&lt;pre&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;stegosaurus&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;before&lt;/span&gt; &lt;span class="symbol"&gt;:each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@steggie&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&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="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;should have tail spikes&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes=&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail_spikes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;4&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;equal&lt;/span&gt; &lt;span class="number"&gt;4&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;it&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;should have plates&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="symbol"&gt;:plates&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="symbol"&gt;:plates=&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plates&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;12&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plates&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;equal&lt;/span&gt; &lt;span class="number"&gt;12&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;What we did there is tell RSpec to create a new Stegosaurus before each example is run. To make the stegosaurus available to the examples, it simply needs to be an instance variable.&lt;br /&gt;&lt;br /&gt;When running your specs, there are a few different ways to format the output. I tend to use the default and "specdoc" formats. Currently, our spec's output looks like this:&lt;pre&gt;$ spec stegosaurus_spec.rb&lt;br /&gt;..&lt;br /&gt;&lt;br /&gt;Finished in 0.01591 seconds&lt;br /&gt;&lt;br /&gt;2 examples, 0 failures&lt;/pre&gt;But we can also tell RSpec to spit out a summary of our specs:&lt;pre&gt;$ spec stegosaurus_spec.rb --format specdoc&lt;br /&gt;&lt;br /&gt;Stegosaurus&lt;br /&gt;- should have tail spikes&lt;br /&gt;- should have plates&lt;br /&gt;&lt;br /&gt;Finished in 0.057443 seconds&lt;br /&gt;&lt;br /&gt;2 examples, 0 failures&lt;/pre&gt;&lt;br /&gt;Our Stegosaurus class is feeling a bit weird, though..technically, we can set the "tail_spikes" and "plates" attributes to any object:&lt;pre&gt;$ irb&lt;br /&gt;&amp;gt;&amp;gt; require 'stegosaurus'&lt;br /&gt;=&amp;gt; true&lt;br /&gt;&amp;gt;&amp;gt;&lt;br /&gt;?&amp;gt; steggie = Stegosaurus.new&lt;br /&gt;=&amp;gt; #&amp;lt;Stegosaurus:0x602afc&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt;&lt;br /&gt;?&amp;gt; steggie.tail_spikes = "we don't want to be able to do this!"&lt;br /&gt;=&amp;gt; "we don't want to be able to do this!"&lt;/pre&gt;So we need to restrict those two attributes to integers:&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Stegosaurus&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;attr_reader&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;attr_reader&lt;/span&gt; &lt;span class="symbol"&gt;:plates&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;tail_spikes=&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;number_of_tail_spikes&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;ArgumentError&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;The first argument (number_of_tail_spikes) must be a Fixnum.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;number_of_tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt; &lt;span class="constant"&gt;Fixnum&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@tail_spikes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;number_of_tail_spikes&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;plates=&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;number_of_plates&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;ArgumentError&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;The first argument (number_of_plates) must be a Fixnum.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;number_of_plates&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt; &lt;span class="constant"&gt;Fixnum&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@plates&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;number_of_plates&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;With that done, what do we need to change in our specs? We need to spec the behaviour of the 2 new setter methods:&lt;pre&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;stegosaurus&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;before&lt;/span&gt; &lt;span class="symbol"&gt;:each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@steggie&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&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="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;"tail_spikes" attribute&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;can only be set to an integer&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;    &lt;span class="constant"&gt;Proc&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;br /&gt;      &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail_spikes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;asdf&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;      &lt;span class="punct"&gt;}.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;raise_error&lt;/span&gt; &lt;span class="constant"&gt;ArgumentError&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;The first argument (number_of_tail_spikes) must be a Fixnum.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail_spikes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;4&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;equal&lt;/span&gt; &lt;span class="number"&gt;4&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;describe&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;"plates" attribute&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;can only be set to an integer&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;    &lt;span class="constant"&gt;Proc&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;br /&gt;      &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plates&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;asdf&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;      &lt;span class="punct"&gt;}.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;raise_error&lt;/span&gt; &lt;span class="constant"&gt;ArgumentError&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;The first argument (number_of_plates) must be a Fixnum.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plates&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;12&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plates&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;equal&lt;/span&gt; &lt;span class="number"&gt;12&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;Most of that is pretty self-explanatory. However, notice that we've just nested #describe inside of the original #describe...twice! We do this because it makes are specs read more fluidly, and it groups examples (#it blocks) together. And inside these inner-#describes, we can do anything we talked about earlier, like setup a before-block.&lt;br /&gt;&lt;br /&gt;Let's add one more attribute...a name:&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Stegosaurus&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;attr_reader&lt;/span&gt; &lt;span class="symbol"&gt;:tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:plates&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;tail_spikes=&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;number_of_tail_spikes&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;ArgumentError&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;The first argument (number_of_tail_spikes) must be a Fixnum.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;number_of_tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt; &lt;span class="constant"&gt;Fixnum&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@tail_spikes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;number_of_tail_spikes&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;plates=&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;number_of_plates&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;ArgumentError&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;The first argument (number_of_plates) must be a Fixnum.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;number_of_plates&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt; &lt;span class="constant"&gt;Fixnum&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@plates&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;number_of_plates&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;name=&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;name&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;ArgumentError&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;The first argument (name) must be a String.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt; &lt;span class="constant"&gt;String&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@name&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&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;/pre&gt;And now the specs look like:&lt;pre&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;stegosaurus&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;before&lt;/span&gt; &lt;span class="symbol"&gt;:each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="attribute"&gt;@steggie&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Stegosaurus&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="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;"tail_spikes" attribute&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;can only be set to an integer&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;    &lt;span class="constant"&gt;Proc&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;br /&gt;      &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail_spikes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;asdf&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;}.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;raise_error&lt;/span&gt; &lt;span class="constant"&gt;ArgumentError&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;The first argument (number_of_tail_spikes) must be a Fixnum.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail_spikes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;4&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail_spikes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;equal&lt;/span&gt; &lt;span class="number"&gt;4&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;describe&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;"plates" attribute&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;can only be set to an integer&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;    &lt;span class="constant"&gt;Proc&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;br /&gt;      &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plates&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;asdf&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;}.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;raise_error&lt;/span&gt; &lt;span class="constant"&gt;ArgumentError&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;The first argument (number_of_plates) must be a Fixnum.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plates&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;12&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plates&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;equal&lt;/span&gt; &lt;span class="number"&gt;12&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;describe&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;"name" attribute&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;can only be set to a string&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;    &lt;span class="constant"&gt;Proc&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;br /&gt;      &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;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;not valid&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt;&lt;br /&gt;    &lt;span class="punct"&gt;}.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;raise_error&lt;/span&gt; &lt;span class="constant"&gt;ArgumentError&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;The first argument (name) must be a String.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;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;Rupert&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;    &lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;equal&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Rupert&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;br /&gt;&lt;/pre&gt;Let's run that spec now:&lt;pre&gt;$ spec stegosaurus_spec.rb&lt;br /&gt;..F&lt;br /&gt;&lt;br /&gt;1)&lt;br /&gt;'Stegosaurus "name" attribute can only be set to a string' FAILED&lt;br /&gt;expected "Rupert", got "Rupert" (using .equal?)&lt;br /&gt;./stegosaurus_spec.rb:37:&lt;br /&gt;&lt;br /&gt;Finished in 0.02225 seconds&lt;br /&gt;&lt;br /&gt;3 examples, 1 failure&lt;/pre&gt;Hrmmm, that's no good. But that's a fairly useful error message, eh?...well, sort of. It's telling us what it expected, and what it received. The thing is, it expected "Rupert" and got "Rupert", so what's going on? The problem lies in the fact that you can't use #equal? to compare strings, and that's exactly what we're doing. If you don't believe me, try it for yourself:&lt;pre&gt;$ irb&lt;br /&gt;&amp;gt;&amp;gt; 'Rupert'.equal? 'Rupert'&lt;br /&gt;=&amp;gt; false&lt;/pre&gt;BTW, did you notice the "(using .equal?)" hint that RSpec gave us?&lt;br /&gt;&lt;br /&gt;Enough banter. All we need to do is change this:&lt;pre&gt;&lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;equal&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Rupert&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;to this:&lt;pre&gt;&lt;span class="attribute"&gt;@steggie&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Rupert&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Shall we view our specs in all their glory now?&lt;pre&gt;$  spec stegosaurus_spec.rb --format specdoc&lt;br /&gt;&lt;br /&gt;Stegosaurus "tail_spikes" attribute&lt;br /&gt;- can only be set to an integer&lt;br /&gt;&lt;br /&gt;Stegosaurus "plates" attribute&lt;br /&gt;- can only be set to an integer&lt;br /&gt;&lt;br /&gt;Stegosaurus "name" attribute&lt;br /&gt;- can only be set to a string&lt;br /&gt;&lt;br /&gt;Finished in 0.018755 seconds&lt;br /&gt;&lt;br /&gt;3 examples, 0 failures&lt;/pre&gt;&lt;br /&gt;Those're the absolute basics of RSpec. If you're thirsty for more, check out the &lt;a href="http://www.rspec.info/"&gt;RSpec website&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-8262969156165673504?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/8262969156165673504/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=8262969156165673504' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/8262969156165673504'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/8262969156165673504'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2009/01/friends-dont-let-friends-code-without.html' title='Friends don’t let friends code without tests'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-6390145431085526068</id><published>2009-01-07T22:05:00.008-05:00</published><updated>2009-09-17T10:57:38.576-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rspec'/><category scheme='http://www.blogger.com/atom/ns#' term='Cucumber'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Testing...it's what life's all about</title><content type='html'>I just read a great &lt;a href="http://railstips.org/2009/1/6/test-or-die"&gt;call to action&lt;/a&gt; by John Nunemaker. He wants to see everyone writing tests for their code, and has decided to "post like a superhuman fiend until everyone is testing". He's on to something!&lt;br /&gt;&lt;br /&gt;With that said, I'm joining in. I primarily use &lt;a href="http://www.rspec.info/"&gt;RSpec&lt;/a&gt;, so I'll be focussing on that, but we'll give &lt;a href="http://wiki.github.com/aslakhellesoy/cucumber/"&gt;Cucumber&lt;/a&gt; and other tools a go, and we'll be starting with the basics.&lt;br /&gt;&lt;br /&gt;So subscribe to our feeds, and stay tuned!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-6390145431085526068?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/6390145431085526068/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=6390145431085526068' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/6390145431085526068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/6390145431085526068'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2009/01/testingits-what-lifes-all-about.html' title='Testing...it&apos;s what life&apos;s all about'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-5469700311983790581</id><published>2008-12-16T12:51:00.011-05:00</published><updated>2009-09-17T10:57:07.784-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='startups'/><category scheme='http://www.blogger.com/atom/ns#' term='CSS'/><category scheme='http://www.blogger.com/atom/ns#' term='motivation'/><title type='text'>CSS, Ruby, and breaks, oh my!</title><content type='html'>&lt;span style="font-family:verdana;"&gt;Today's the 16th. I haven't worked on YoDino since the 12th. Egad! What happened?! The usual "startup stress" happened.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;I've been working pretty hard on getting the last important bits of YoDino implemented before Christmas. On the 11th, I started styling the app. On the 12th, my penchant for well-thought-out, structured, and predictable languages (&amp;lt;cough&amp;gt;Ruby&amp;lt;/cough&amp;gt;) caused me to throw my hands in the air in frustration with CSS.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;CSS...What an AWFUL language!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;There, I said it. I don't care if you can do awesome things with it, like &lt;/span&gt;&lt;a style="font-family: verdana;" href="http://www.csszengarden.com/"&gt;CSS Zen Garden&lt;/a&gt;&lt;span style="font-family:verdana;"&gt;. Technically, you can do awesome things with Delphi and BASIC and ASP.NET, too. But I &lt;/span&gt;&lt;span style="font-weight: bold; font-family:verdana;"&gt;don't&lt;/span&gt; do things with Delphi, BASIC or ASP.NET..and for very good reasons! [1] The same should apply to CSS, but there's no alternative. Ugh.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;Okay, tangent's over. The frustration I felt from CSS was really blown out of proportion, but I think that's because I'm doing this startup alone. It's pretty much a fact that startups with multiple co-founders fare much better than startups with a single founder. Co-founders are able to discuss problems and ideas with each other, share frustrations, motivate each other, etc. They're essentially a small, tight family.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;I reckon my frustrations with CSS ended up being the straw that broke the camel's back. I needed a break. So, you know what? I bloody well took one! [2]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;Now, a couple of days later, I feel refreshed and ready to tackle it all again. And this time, I'm going to succeed. And CSS is going to be &lt;/span&gt;&lt;span style="font-weight: bold; font-family:verdana;"&gt;my&lt;/span&gt;&lt;span style="font-family:verdana;"&gt; bitch, not the other way around.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;[1] If you have to ask why I don't do things with Delphi, BASIC, or even ASP.NET, you've never experienced the clarity and melancholy of working in Ruby. &lt;a href="http://www.ruby-lang.org/en/documentation/quickstart/"&gt;Go try it&lt;/a&gt;. If, after a couple of days, you can honestly come back to me and say that you didn't soil yourself from pure pleasure, I'll buy you a beer.&lt;br /&gt;&lt;br /&gt;[2] Yes, I know that once I launch YoDino, taking a break won't be possible. But that's okay! I'll have made it through &lt;span style="font-weight: bold;"&gt;at least&lt;/span&gt; the first &lt;a href="http://www.squidoo.com/thedipbook"&gt;dip&lt;/a&gt; by then, and things will be different.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-5469700311983790581?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/5469700311983790581/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=5469700311983790581' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/5469700311983790581'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/5469700311983790581'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2008/12/css-ruby-and-breaks-oh-my.html' title='CSS, Ruby, and breaks, oh my!'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-9088390041009644909</id><published>2008-12-13T20:50:00.007-05:00</published><updated>2009-01-07T20:26:16.266-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='marketing'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='wtf'/><title type='text'>+1 for the thought, -10 for the execution</title><content type='html'>I just received an email from the anti-virus company ESET, reminding me that a license that I purchased for one of their products is coming up for renewal soon. I appreciate the email, but honestly, do you think they could've put some more time into formatting it and making it readable and appealing?&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #f1f1f3; color: black"&gt;!! ! PLEASE DO NOT REPLY TO THIS AUTOMATED EMAIL ! !------------------------------------------------------------PLEASE IGNORE THIS ROBOT RENEWAL REMINDERIF YOU HAVE ALREADY RENEWED YOUR LICENSE------------------------------------------------------------Dear valued client,Our records indicate that your ESET NOD32 Antivirus Systemlicense is due to expire on 31 December 2008.We often release several updates in a single day, but updatingstops when your license expires and your computer can quickly become dangerously exposed to brand new malware threats.Our offices will be minimally staffed from 22 December 2008 to 05 January 2009. We strongly recommend that you renew your license no later than 22 December 2008 to avoid deliverydelays.You won't lose time or money by renewing early. You can useyour new license immediately, but the renewal period won'tcome into effect until your current license expires. (Licensesexpired for more than 30 days cannot be renewed!)You can renew with your local ESET reseller, or by phone on07 3325 2999 (9:00am-4:00pm Monday-Friday), or online on:http://www.eset.com.au/purchase/purchase.htmlYou can renew only the number of computers covered byyour current license. Please call for a quote to increase thenumber of computers.Regards,Joyita FewsterESET Australiaphone : 07 3325 2999 (9am-4pm EST Monday-Friday)website : http://www.eset.com.auemail : HYPERLINK "mailto:sales@eset.com.au"sales@eset.com.au_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/NOD32 is the outright winner of Australian PC User's"Best Antivirus Program" / "Best Buy" Awards2001/2002/2003/2004/2005/2006/2007and has won more Virus Bulletin VB100% Awardsthan any other antivirus program in the world!_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/------------------------------------------------------------PLEASE IGNORE THIS ROBOT RENEWAL REMINDERIF YOU HAVE ALREADY RENEWED YOUR LICENSE------------------------------------------------------------! ! PLEASE DO NOT REPLY TO THIS AUTOMATED EMAIL ! !&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Scanned by NOD32 ( http://www.eset.com.au )&lt;br /&gt;and NetBox Blue ( http://netboxblue.com.au )&lt;/div&gt;&lt;br /&gt;There isn't a single newline in the message...WTF?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-9088390041009644909?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/9088390041009644909/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=9088390041009644909' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/9088390041009644909'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/9088390041009644909'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2008/12/1-for-thought-10-for-execution.html' title='+1 for the thought, -10 for the execution'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-5812756172592774969</id><published>2008-11-15T13:10:00.014-05:00</published><updated>2009-09-17T10:57:57.489-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='resources'/><category scheme='http://www.blogger.com/atom/ns#' term='routes'/><category scheme='http://www.blogger.com/atom/ns#' term='Rails'/><title type='text'>Rails Resources:   :as   vs   :controller</title><content type='html'>As you may or may not know, when configuring routes and resources in config/routes.rb , you can pass the :as or :controller options to map.resources . When I first read about these two options in the &lt;a href="http://guides.rubyonrails.org/routing_outside_in.html"&gt;Rails Routing From The Outside In&lt;/a&gt; Rails Guide, they seemed to do the exact same thing.&lt;br /&gt;&lt;br /&gt;However, that's not the case at all.&lt;br /&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;resources&lt;/span&gt; &lt;span class="symbol"&gt;:photos&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:as&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;images&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;This allows you to request the "photos" resource using the word "images". IE: http://localhost:3000/images/ would route to the "photos" controller.&lt;br /&gt;&lt;br /&gt;On the other hand,&lt;br /&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;resources&lt;/span&gt; &lt;span class="symbol"&gt;:photos&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;images&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;This allows you to request the "images" resource using the word "photos". IE: http://localhost:3000/photos/ would route to the "images" controller.&lt;br /&gt;&lt;br /&gt;Simple? Yes. Subtle? Yes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-5812756172592774969?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/5812756172592774969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=5812756172592774969' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/5812756172592774969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/5812756172592774969'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2008/11/rails-resources-as-vs-controller.html' title='Rails Resources: &amp;nbsp;&amp;nbsp;:as &amp;nbsp;&amp;nbsp;vs &amp;nbsp;&amp;nbsp;:controller'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-4796732415132640732</id><published>2008-11-11T13:28:00.010-05:00</published><updated>2009-08-23T17:03:51.511-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='reasoning'/><category scheme='http://www.blogger.com/atom/ns#' term='conventions'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>Convention and reasoning</title><content type='html'>When coding in the past, I rarely used the switch/select/case-conditional. However, since I started using Ruby, I've found it to be quite useful in some situations.&lt;br /&gt;&lt;br /&gt;Vim formats them like this:&lt;br /&gt;&lt;pre&gt;&lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="ident"&gt;foobar&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;something&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt; &lt;span class="comment"&gt;# Do something.&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;another thing&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt; &lt;span class="comment"&gt;# Do another thing.&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt; &lt;span class="comment"&gt;# Do this if the above "when"s don't match.&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;That indentation scheme doesn't feel right to me. The "when" and "else" clauses are pretty much children of the "case" clause. In my opinion, "when" and "else" should be indented +1, like this:&lt;br /&gt;&lt;pre&gt;&lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="ident"&gt;foobar&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;something&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;   &lt;span class="comment"&gt;# Do something.&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;another thing&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;   &lt;span class="comment"&gt;# Do another thing.&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;   &lt;span class="comment"&gt;# Do this if the above "when"s don't match.&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;But now the code within each "when" and "else" is indented +2! That's a lot of indentation.&lt;br /&gt;&lt;br /&gt;I brought this up in #ruby-lang on Freenode. A couple of people agreed with me, a couple didn't care, and the rest disagreed. There was one poignant comment though:&lt;br /&gt;&lt;pre class="code-block"&gt;&lt;code&gt;&amp;lt;quix&amp;gt; litage: why doesn't your reasoning for if/elsif apply to case/when?&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;I haven't been able to answer that yet. quix's reasoning is sound, but I can't shake the dirtiness that I feel when indenting "when" and "else" to the same level as "case". It just feels wrong...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-4796732415132640732?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/4796732415132640732/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=4796732415132640732' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/4796732415132640732'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/4796732415132640732'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2008/11/convention-and-reasoning.html' title='Convention and reasoning'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-8648254901273805275</id><published>2008-10-25T10:58:00.004-04:00</published><updated>2009-01-07T18:06:41.797-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dreams'/><category scheme='http://www.blogger.com/atom/ns#' term='military'/><title type='text'>Joining the military</title><content type='html'>Last night, I had a dream that I was joining the military. It was one of those dreams that's so real, you can't tell that you're in a dream...I was sh*tting myself.&lt;br /&gt;&lt;br /&gt;What's ironic is that, back in university, I applied to join The Canadian Forces as a Combat Engineer. I ended up not pursuing that, because a) I didn't want to learn how to kill people, and b) adventuring around Australia sounded like a whole lot more fun. Oh, and Black Hawk Down was released after I'd started my military application, which gave me a dose of reality and what war can be like.&lt;br /&gt;&lt;br /&gt;The scariest part of the dream wasn't that I was joining a military, but that I was joining the &lt;span style="font-weight: bold;"&gt;American&lt;/span&gt; military...and being shipped off to Afghanistan or Iraq. *shudder*&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-8648254901273805275?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/8648254901273805275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=8648254901273805275' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/8648254901273805275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/8648254901273805275'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2008/10/joining-military.html' title='Joining the military'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-4501324621246538505</id><published>2008-10-18T22:06:00.006-04:00</published><updated>2009-01-07T18:15:02.730-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='movies'/><title type='text'>Idiots and Angels</title><content type='html'>I went to see the movie &lt;a href="http://torontoafterdark.com/2008/program/feature-films/idiots-and-angels/"&gt;Idiots and Angels&lt;/a&gt; at the &lt;a href="http://torontoafterdark.com/2008/"&gt;Toronto After Dark Film Festival&lt;/a&gt; today. The animation style was amazing, and the movie was pretty good, too. Check out the &lt;a href="http://www.youtube.com/watch?v=2YpbKnr4iJE"&gt;trailer&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;object height="344" width="425"&gt;&lt;param name="movie" value="http://www.youtube.com/v/2YpbKnr4iJE&amp;amp;hl=en&amp;amp;fs=1&amp;amp;rel=0"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;embed src="http://www.youtube.com/v/2YpbKnr4iJE&amp;amp;hl=en&amp;amp;fs=1&amp;amp;rel=0" type="application/x-shockwave-flash" allowfullscreen="true" height="344" width="425"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-4501324621246538505?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/4501324621246538505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=4501324621246538505' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/4501324621246538505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/4501324621246538505'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2008/10/idiots-and-angels.html' title='Idiots and Angels'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-9135394682560194930</id><published>2008-10-14T11:36:00.005-04:00</published><updated>2009-01-07T18:06:28.788-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Toronto'/><category scheme='http://www.blogger.com/atom/ns#' term='Australia'/><title type='text'>Longing.au</title><content type='html'>I'm reading &lt;a href="http://www.amazon.com/Jupiters-Travels-Years-Around-Triumph/dp/0965478521"&gt;Jupiter's Travels&lt;/a&gt; right now. It's about a guy who motorbiked 63,000 miles (that's over 100,000km!) around the world...starting in 1974! His recounting of biking in Australia really made me miss Australia.&lt;br /&gt;&lt;br /&gt;I miss the sun and warmth, the laid-back approach to life, the friendliness of most people, and of course the lizards, echidnas, and wombats. I even miss the spiders.&lt;br /&gt;&lt;br /&gt;But I have to accept that I've decided to live and work in Toronto for now. As much as I love Australia, it's not where I should be at the moment.&lt;br /&gt;&lt;br /&gt;So as Chopper would say, "Harden the fuck up, Nick!"&lt;br /&gt;&lt;br /&gt;PS: Matt, thanks for that HTFU reminder.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-9135394682560194930?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/9135394682560194930/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=9135394682560194930' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/9135394682560194930'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/9135394682560194930'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2008/10/longingau.html' title='Longing.au'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-3491862176612566965</id><published>2008-10-11T14:42:00.004-04:00</published><updated>2009-01-07T18:06:08.357-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Toronto'/><category scheme='http://www.blogger.com/atom/ns#' term='health'/><category scheme='http://www.blogger.com/atom/ns#' term='Australia'/><title type='text'>Hypothesis: Toronto is bad for my health</title><content type='html'>I spent 3.5 years in Australia. During that time, I was only sick twice. I've been back in Toronto for six months, and been sick (IE: sore throat, runny nose, etc) three times.&lt;br /&gt;&lt;br /&gt;I can think of two possible explanations:&lt;br /&gt;1) Toronto's environment is dirtier and makes me more prone to getting sick;&lt;br /&gt;2) I'm under more stress in Toronto.&lt;br /&gt;&lt;br /&gt;#1 is definitely true in comparison to pretty much all of Australia, except for areas near mines and other heavy industry.&lt;br /&gt;&lt;br /&gt;#2 is possible, but I don't buy it. There were many times that I was under a ton of stress while working at Voxpak in The Gold Coast, Australia.&lt;br /&gt;&lt;br /&gt;Thus, my hypothesis is that Toronto is bad for my health.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-3491862176612566965?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/3491862176612566965/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=3491862176612566965' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/3491862176612566965'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/3491862176612566965'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2008/10/hypothesis-toronto-is-bad-for-my-health.html' title='Hypothesis: Toronto is bad for my health'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-3661347158234425331</id><published>2008-10-10T08:12:00.005-04:00</published><updated>2009-01-23T13:38:13.572-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rspec'/><title type='text'>ModelSpeccer Released!</title><content type='html'>Yesterday I released my first open-source project. It's called ModelSpeccer, and it generates RSpec specs on-the-fly for Rails model attributes. It started out as part of my housing rentals website, and eventually grew into a separate module. Once that happened, I figured I might as well learn how to write a Rails plugin, and published it on GitHub. Have a look:&lt;br /&gt;http://github.com/nickhoffman/modelspeccer/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-3661347158234425331?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/3661347158234425331/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=3661347158234425331' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/3661347158234425331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/3661347158234425331'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2008/10/released.html' title='ModelSpeccer Released!'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-6437435753717909901</id><published>2008-08-05T13:46:00.006-04:00</published><updated>2009-01-07T18:14:49.015-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='parkour'/><title type='text'>Encouragement</title><content type='html'>Most of you know what parkour is. If you don't, in one sentence, it's about using your body and environment around you to move from one location to another as quickly as possible. Here's some more info:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://pkto.ca/parkour.php" target="PKTO"&gt;Parkour Toronto FAQ&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Parkour" target="Wikipedia"&gt;Wikipedia article on parkour&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.youtube.com/watch?v=x98jCBnWO8w" target="YouTube"&gt;Video of David Belle, the founder of parkour&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;I started training on and off in Australia around the middle of 2007. When I moved back to Toronto in March 2008, I began training with &lt;a href="http://www.pkto.ca/"&gt;PKTO&lt;/a&gt; (Parkour Toronto).&lt;br /&gt;&lt;br /&gt;Unfortunately, it seems that I'm a bit prone to injuries. From parkour alone, I've punctured the same shin twice, sprained an ankle a couple of times, bashed my knees many times, and received the usual cuts and scrapes that come with the territory. Two weeks ago, I also crashed while riding my bicycle. That was NOT pretty, and it did a number on knee. And the second shin puncture occured two weeks before the crash. Ack.&lt;br /&gt;&lt;br /&gt;So for the past month, I've been healing rather than training. Healing sucks. I mean, it's great beacuse it's getting you closer to being able to train again, but the fact that you can't train...at least, not train "fully"...sucks.&lt;br /&gt;&lt;br /&gt;Yesterday evening I got back into training. However, while I was healing, there were times when I was pretty unmotivated and down. I felt like I was making no progress: I'm always injured, what's the point?, etc. But I slowly realised that there are things you can do to keep yourself motivated while healing.&lt;br /&gt;&lt;br /&gt;First of all, train other parts of your body. My knee was giving me grief, so I started working towards &lt;a href="http://www.hundredpushups.com/" target="Hundred Push-Ups"&gt;100 push-ups&lt;/a&gt;. If you hurt, say, your shoulder, work your abs and legs. Also, stay involved in the community. Go out to meet-ups even if you can't train. It'll keep you motivated and looking forward to when you can train again.&lt;br /&gt;&lt;br /&gt;Another thing you can do is stretch and do research. Videos abound explaining technique, conditioning routines, etc. Do some homework and develop a routine for the time that you're recovering, and tweak your usual training regimen.&lt;br /&gt;&lt;br /&gt;It's all about making the best of the situation that you're in. Yeah, it sucks to be injured. But accept it when it happens, and modify your training to fit in with your predicament. If you do that, you'll be pleasantly surprised when you get back to your regular training.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-6437435753717909901?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/6437435753717909901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=6437435753717909901' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/6437435753717909901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/6437435753717909901'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2008/08/encouragement.html' title='Encouragement'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3427575211647135852.post-606092188172455996</id><published>2008-08-01T11:16:00.009-04:00</published><updated>2009-01-07T18:15:39.680-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Books</title><content type='html'>As a kid, I devoured books like most kids devoured free candy. They took me to different worlds, fuelled my imagination, expanded my vocabulary, and did so much more. I even got into trouble for reading &lt;span style="font-weight: bold;"&gt;too&lt;/span&gt; much at night. I read throughout highschool, but when university came, the urge and time to do so sunk like a submarine with a screen door. In the past..8 years!..I've probably read only a couple of dozen books &lt;span style="font-weight: bold;"&gt;max&lt;/span&gt;. It's probably closer to 15-20, excluding required readings for uni. That's embarassing. Well, it is for me, at least.&lt;br /&gt;&lt;br /&gt;But, the light has shone through the clouds! I sauntered into a comic/graphic novel store on my way home last week, and stumbled upon &lt;a href="http://www.amazon.com/Good-Omens-Neil-Gaiman/dp/0441003257"&gt;Good Omens&lt;/a&gt; by Neil Gaiman and Terry Pratchet. 10-15 years ago, my brother was expounding upon Terry Pratchett's fantastic British wit and style. Plus, I was in the mood for something along the lines of &lt;a href="http://www.amazon.com/Ultimate-Hitchhikers-Guide-Galaxy/dp/0345453743/"&gt;The Hitchhiker's Guide To The Galaxy&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I'm about half of the way through &lt;a href="http://www.amazon.com/Good-Omens-Neil-Gaiman/dp/0441003257"&gt;Good Omens&lt;/a&gt;. It's no &lt;a href="http://www.amazon.com/Ultimate-Hitchhikers-Guide-Galaxy/dp/0345453743/"&gt;Hitchhiker's Guide&lt;/a&gt;, but it definitely makes me laugh and is helping renew my lust for reading again.&lt;br /&gt;&lt;br /&gt;If you've been meaning to kill the current lull in your literary absorptions, why not pick out something that you &lt;span style="font-weight: bold;"&gt;feel&lt;/span&gt; like reading - something that's got its tractor beam set on you - rather than what you &lt;span style="font-weight: bold;"&gt;think&lt;/span&gt; you should read. You might just enjoy it...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3427575211647135852-606092188172455996?l=nick-hoffman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nick-hoffman.blogspot.com/feeds/606092188172455996/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3427575211647135852&amp;postID=606092188172455996' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/606092188172455996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3427575211647135852/posts/default/606092188172455996'/><link rel='alternate' type='text/html' href='http://nick-hoffman.blogspot.com/2008/08/books.html' title='Books'/><author><name>Nick</name><uri>http://www.blogger.com/profile/14068375291725989827</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_yjqr5Jjq8ak/SQKZQUu7quI/AAAAAAAAAAU/dDSI1QCt6So/S220/Nick+icon.jpg'/></author><thr:total>0</thr:total></entry></feed>
