<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title>A Single Programmer's Blog</title>
  <subtitle>A really swell blog built with Webby</subtitle>
  <link href="http://blog.teksol.info/" rel="alternate" type="text/html" />
  
  <link href="http://feeds.feedburner.com/teksol" rel="alternate" type="application/atom+xml" />
  <author>
    <name>François Beausoleil</name>
    <email>francois@teksol.info</email>
  </author>
  <id>http://blog.teksol.info/</id>
  <updated>2009-09-29T14:31:31Z</updated>
  <link rel="self" href="http://feeds.feedburner.com/teksol" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry>
    <title>Entering the Enterprise Space: Moving to Bloom Digital Platforms</title>
    <link href="http://blog.teksol.info/2009/09/29/entering-the-enterprise-space-moving-to-bloom-digital-platforms.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-09-29:20090929103131</id>
    <updated>2009-09-29T10:31:31-04:00</updated>
    <content type="html">&lt;p&gt;After a few years as a consultant, I received an offer from &lt;a href="http://twitter.com/bosko"&gt;Bosko Milekic&lt;/a&gt; from &lt;a href="http://bloomdigital.com/"&gt;Bloom Digital Platforms Inc&lt;/a&gt;.  Starting on October 5th, I will be a permanent Bloom employee.  &lt;a href="http://macournoyer.com/blog/"&gt;Marc-André&lt;/a&gt; and &lt;a href="http://garyharan.com/"&gt;Gary&lt;/a&gt; are moving outside the enterprise, while I&amp;#8217;m moving inside.&lt;/p&gt;
&lt;p&gt;At Bloom, I will be responsible for the &lt;a href="http://www.adgear.com/"&gt;AdGear&lt;/a&gt; API, enabling applications to easily connect to their platform for ad serving and management.  We even have an open source Ruby client available on GitHub as &lt;a href="http://github.com/bloom/ad_gear_client"&gt;ad_gear_client&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This will be a welcome change of pace for me.&lt;/p&gt;
&lt;p&gt;See you at the next &lt;a href="http://www.montrealonrails.com/"&gt;Montreal.rb&lt;/a&gt;, at my new office!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/ROraSPiGyi0" height="1" width="1"/&gt;</content>
    
    <category term="employment" label="Employment" />
      </entry>
  <entry>
    <title>RestClient + Multipart Form Data: See My Branch</title>
    <link href="http://blog.teksol.info/2009/08/12/restclient-+-multipart-form-data-see-my-branch.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-08-12:20090812110538</id>
    <updated>2009-08-12T11:05:38-04:00</updated>
    <content type="html">&lt;p&gt;I just merged Rick Olson&amp;#8217;s (&lt;a href="http://techno-weenie.net/"&gt;technoweenie&lt;/a&gt;) rest-client &lt;a href="http://github.com/technoweenie/rest-client/tree/multipart_streaming"&gt;multipart_streaming&lt;/a&gt; branch with RestClient 1.0.4.  You can see the results at &lt;a href="http://github.com/francois/rest-client"&gt;francois/rest-client&lt;/a&gt;.  I sent a pull request to &lt;a href="http://adam.blog.heroku.com/"&gt;Adam&lt;/a&gt;, so if he pulls it in, everyone will benefit from this code.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/UpYlPmDb8fE" height="1" width="1"/&gt;</content>
    
    <category term="restclient" label="Restclient" />
      </entry>
  <entry>
    <title>Using ActiveResource With Digest Authentication: Branch in Progress</title>
    <link href="http://blog.teksol.info/2009/08/04/using-activeresource-with-digest-authentication-branch-in-progress.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-08-04:20090804160411</id>
    <updated>2009-08-04T16:04:11-04:00</updated>
    <content type="html">&lt;p&gt;I have a work-in-progress branch for using Digest authentication with ActiveResource.&lt;/p&gt;
&lt;p&gt;So, how did I do it?  It wasn&amp;#8217;t too hard actually.  When I spiked, I changed &lt;a href="http://github.com/francois/rails/blob/ar_digest/activeresource/lib/active_resource/connection.rb#L109"&gt;&lt;code&gt;ActiveResource::Connection#request&lt;/code&gt;&lt;/a&gt; to handle authentication itself.  I ended up with a big mess: a new rescue clause, 10 lines of code to calculate the digest and so on.  But I knew it would work.  So, I &lt;code&gt;git checkout .&lt;/code&gt; and started with tests, as it should.&lt;/p&gt;
&lt;p&gt;The way ActiveResource is built, if a username / password is sent in, ActiveResource will send those automatically in an Authorization header, using the &lt;a href="http://en.wikipedia.org/wiki/Basic_access_authentication"&gt;Basic&lt;/a&gt; authentication method.  I need a way to turn this off.  Thus grew &lt;a href="http://github.com/francois/rails/commit/c33a0a015af50aa3cbf7cc4071e24ec91cfbedb9"&gt;#use_basic_authentication=&lt;/a&gt; and &lt;code&gt;#use_digest_authentication=&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next up, actually being able to calculate the Digest.  A quick search turned up code by &lt;a href="http://blog.segment7.net/"&gt;Eric Hodel&lt;/a&gt; in the form of a Ruby module: &lt;a href="http://segment7.net/projects/ruby/snippets/digest_auth.rb"&gt;An implementation of HTTP Digest Authentication in Ruby&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After a bit of cleanup and rewriting, I have a branch of ActiveResource that&amp;#8217;s ready to be commented on. Please see &lt;a href="http://github.com/francois/rails/tree/ar_digest"&gt;francois/ar_digest&lt;/a&gt; and leave comments there.&lt;/p&gt;
&lt;p&gt;An example of using Digest would be:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;logger&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt; require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;activeresource&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt; require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;pp&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt; 
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; &lt;span class="co"&gt;ActiveResource&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.logger = &lt;span class="co"&gt;Logger&lt;/span&gt;.new(&lt;span class="co"&gt;STDERR&lt;/span&gt;)
&lt;span class="no"&gt; 6&lt;/span&gt; 
&lt;span class="no"&gt; 7&lt;/span&gt; &lt;span class="co"&gt;ActiveResource&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.site     = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://adgear.local/api&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 8&lt;/span&gt; &lt;span class="co"&gt;ActiveResource&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.user     = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;francois&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt; &lt;span class="co"&gt;ActiveResource&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.password = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;my-funny-new-password-which-you've-never-seen-before&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; &lt;span class="co"&gt;ActiveResource&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.timeout  = &lt;span class="i"&gt;30&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt; 
&lt;span class="no"&gt;12&lt;/span&gt; &lt;span class="c"&gt;# Don't attempt Basic authentication, but be sure to use Digest&lt;/span&gt;
&lt;span class="no"&gt;13&lt;/span&gt; &lt;span class="co"&gt;ActiveResource&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.use_basic_authentication  = &lt;span class="pc"&gt;false&lt;/span&gt;
&lt;span class="no"&gt;14&lt;/span&gt; &lt;span class="co"&gt;ActiveResource&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.use_digest_authentication = &lt;span class="pc"&gt;true&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt;16&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Site&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveResource&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;
&lt;span class="no"&gt;17&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;18&lt;/span&gt; 
&lt;span class="no"&gt;19&lt;/span&gt; pp sites = &lt;span class="co"&gt;Site&lt;/span&gt;.find(&lt;span class="sy"&gt;:all&lt;/span&gt;)

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This work was sponsored by &lt;a href="http://www.bloomdigital.com/"&gt;Bloom Digital Platforms&lt;/a&gt;, as part of my work on their AdGear API.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/hXU1zoZPdss" height="1" width="1"/&gt;</content>
    
    <category term="active_resource" label="Active resource" />
    
    <category term="activeresource" label="Activeresource" />
    
    <category term="active-resource" label="Active-resource" />
      </entry>
  <entry>
    <title>Piston 2.0.6: minor bug fixes</title>
    <link href="http://blog.teksol.info/2009/08/01/piston-2.0.6-minor-bug-fixes.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-08-01:20090801132114</id>
    <updated>2009-08-01T13:21:14-04:00</updated>
    <content type="html">&lt;p&gt;I just pulled in a couple of patches from outside contributors.  These are all minor bug fixes, but are important:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://github.com/chrisgibson"&gt;Chris Gibson&lt;/a&gt;: When forcing the repository type, Piston would break because it called #downcase on a Symbol. &lt;a href="http://github.com/francois/piston/commit/1bcc16bf874afca88e061ea35cd049b7a1d38ab4"&gt;1bcc16bf8&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Terry Heath:  &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt;&amp;#8216;s &lt;tt&gt;&amp;#8212;non-interactive&lt;/tt&gt; would prevent OS X&amp;#8217;s keychain from kicking in. &lt;a href="http://github.com/francois/piston/commit/93d9a9578f0947c62a70b52f86d8d4033cfb56ad"&gt;93d9a957&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Florian Aßmann: In certain cases, the revision would be a String, and other times it would be an Integer.  Normalize before comparing. &lt;a href="http://github.com/francois/piston/commit/40c0bc4e3c63aa398d319ec08846538705950e3d"&gt;40c0bc4e&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All users are advised to upgrade:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="er"&gt;$&lt;/span&gt; sudo gem install piston

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/Valxk7_jDvs" height="1" width="1"/&gt;</content>
    
    <category term="piston" label="Piston" />
      </entry>
  <entry>
    <title>Clear and Concise Explanation of RESTful Resources</title>
    <link href="http://blog.teksol.info/2009/07/22/clear-and-concise-explanation-of-restful-resources.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-07-22:20090722143023</id>
    <updated>2009-07-22T14:30:23-04:00</updated>
    <content type="html">&lt;p&gt;I just found &lt;a href="http://www.theamazingrando.com/blog/?p=107"&gt;Your Web Service Might Not Be RESTful If&amp;hellip;&lt;/a&gt; by &lt;a href="http://www.theamazingrando.com/blog/"&gt;Paul Sadauskas&lt;/a&gt;.  This article is a very clear and concise explanation of how to do real RESTful services.&lt;/p&gt;
&lt;p&gt;His arguments are:&lt;/p&gt;
&lt;p&gt;1. Present a single top-level resource;&lt;br /&gt;
2. All resources should be accessible by followling links from the top-level resource;&lt;br /&gt;
3. Don&amp;#8217;t put API tokens in the URL or headers (use representations instead).&lt;/p&gt;
&lt;p&gt;A very enjoyable read, and something we should all be following.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/S4LTjcgcUeQ" height="1" width="1"/&gt;</content>
    
    <category term="rest" label="Rest" />
      </entry>
  <entry>
    <title>Piston 2.0.4: minor fixes</title>
    <link href="http://blog.teksol.info/2009/07/20/piston-2-dot-0.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-07-20:20090720111811</id>
    <updated>2009-07-20T11:18:11-04:00</updated>
    <content type="html">&lt;p&gt;I just release Piston 2.0.4.  This is a minor fix to enable Piston to work with more Git versions.&lt;/p&gt;
&lt;p&gt;Git 1.6.0.1 accepts checking out a new branch without specifying the remote&amp;#8217;s name:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="er"&gt;$&lt;/span&gt; git checkout -b my-&lt;span class="i"&gt;2&lt;/span&gt;-&lt;span class="i"&gt;3&lt;/span&gt;-stable &lt;span class="i"&gt;2&lt;/span&gt;-&lt;span class="i"&gt;3&lt;/span&gt;-stable
&lt;span class="no"&gt;2&lt;/span&gt; &lt;span class="c"&gt;# implies origin/2-3-stable&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Versions prior to that expect the full name of the remote branch:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="er"&gt;$&lt;/span&gt; git checkout -b my-&lt;span class="i"&gt;2&lt;/span&gt;-&lt;span class="i"&gt;3&lt;/span&gt;-stable origin/&lt;span class="i"&gt;2&lt;/span&gt;-&lt;span class="i"&gt;3&lt;/span&gt;-stable

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Installation is the usual incantation:  sudo gem install piston&lt;/p&gt;
&lt;h2&gt;What is Piston?&lt;/h2&gt;
&lt;p&gt;Piston is a utility that eases vendor branch management. This is similar to &lt;tt&gt;svn:externals&lt;/tt&gt;, except you have a local copy of the files, which you can modify at will.  As long as the changes are mergeable, you should have no problems.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/UknALddVB9U" height="1" width="1"/&gt;</content>
    
    <category term="piston" label="Piston" />
      </entry>
  <entry>
    <title>MongoDB or Redis?  The Choice Is Yours</title>
    <link href="http://blog.teksol.info/2009/07/15/mongodb-or-redis-the-choice-is-yours.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-07-15:20090715093554</id>
    <updated>2009-07-15T09:35:54-04:00</updated>
    <content type="html">&lt;p&gt;In the past couple of weeks, I had the opportunity to look at &lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; and &lt;a href="http://code.google.com/p/redis/"&gt;Redis&lt;/a&gt; in more details.  Both are fast, but serve different needs.  Here&amp;#8217;s my take on them both.&lt;/p&gt;
&lt;h2&gt;MongoDB&lt;/h2&gt;
&lt;p&gt;MongoDB is a &lt;a href="http://en.wikipedia.org/wiki/Document-oriented_database"&gt;document oriented database&lt;/a&gt;.  MongoDB stores structured data, as documents:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; {
&lt;span class="no"&gt;2&lt;/span&gt;   &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;François Beausoleil&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;,
&lt;span class="no"&gt;3&lt;/span&gt;   &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;francois@teksol.info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;,
&lt;span class="no"&gt;4&lt;/span&gt;   &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;year-of-birth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class="i"&gt;1973&lt;/span&gt;,
&lt;span class="no"&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;   &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;tags&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;: [&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;friendly&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;ruby&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;coder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;father&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;]
&lt;span class="no"&gt;6&lt;/span&gt; }

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Since MongoDB stores structured data, it has a richer query language:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt; &lt;span class="co"&gt;Find&lt;/span&gt; people that are fathers &lt;span class="co"&gt;OR&lt;/span&gt; coders
&lt;span class="no"&gt; 2&lt;/span&gt; db.people.find( { &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;tags&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : { &lt;span class="gv"&gt;$in&lt;/span&gt; : [&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;father&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;coder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;] } } );
&lt;span class="no"&gt; 3&lt;/span&gt; 
&lt;span class="no"&gt; 4&lt;/span&gt; &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt; &lt;span class="co"&gt;Find&lt;/span&gt; people that are fathers &lt;span class="co"&gt;AND&lt;/span&gt; coders
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; db.people.find( { &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;tags&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : { &lt;span class="gv"&gt;$all&lt;/span&gt; : [&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;father&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;coder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;] } } );
&lt;span class="no"&gt; 6&lt;/span&gt; 
&lt;span class="no"&gt; 7&lt;/span&gt; &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt; &lt;span class="co"&gt;Index&lt;/span&gt; the email member of documents
&lt;span class="no"&gt; 8&lt;/span&gt; db.people.ensureIndex({email: &lt;span class="i"&gt;1&lt;/span&gt;});
&lt;span class="no"&gt; 9&lt;/span&gt; 
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt; &lt;span class="co"&gt;Query&lt;/span&gt; using the index
&lt;span class="no"&gt;11&lt;/span&gt; db.people.findOne({email: &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;francois@teksol.info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;})

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Redis&lt;/h2&gt;
&lt;p&gt;Redis is a key-value store that is strongly typed.  Redis has three types of values: strings, sets and lists.  Out of the box, you cannot store structured data in Redis.  You have to build the index manually, storing yet other keys.&lt;/p&gt;
&lt;p&gt;Redis is conceptually simple.  All operations are either stores or fetches:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="co"&gt;PUT&lt;/span&gt; key value
&lt;span class="no"&gt;2&lt;/span&gt; &lt;span class="co"&gt;GET&lt;/span&gt; key

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can think of Redis as a persistent Hash or Dictionary.  The fact that Redis is strongly typed makes some operations very interesting.&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="c"&gt;# Store other data as serialized JSON object, but we won't be able to query the value itself&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt; &lt;span class="co"&gt;SET&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;people:francois&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;{'name': 'François Beausoleil', 'email': 'francois@teksol.info', 'year-of-birth': 1973, 'tags': ['friendly', 'ruby', 'coder', 'father']}&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt; 
&lt;span class="no"&gt; 4&lt;/span&gt; &lt;span class="c"&gt;# Build ourselves an index on the email value&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; &lt;span class="co"&gt;SET&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;email:francois@teksol.info&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;francois&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt; 
&lt;span class="no"&gt; 7&lt;/span&gt; &lt;span class="c"&gt;# Build another index for tags&lt;/span&gt;
&lt;span class="no"&gt; 8&lt;/span&gt; &lt;span class="co"&gt;SADD&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;tags:friendly&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;francois&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt; &lt;span class="co"&gt;SADD&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;tags:ruby&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;     &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;francois&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; &lt;span class="co"&gt;SADD&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;tags:coder&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;    &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;francois&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt; &lt;span class="co"&gt;SADD&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;tags:father&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;   &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;francois&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt; 
&lt;span class="no"&gt;13&lt;/span&gt; &lt;span class="c"&gt;# During authentication, we would find the person using the email address&lt;/span&gt;
&lt;span class="no"&gt;14&lt;/span&gt; &lt;span class="co"&gt;GET&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;email:francois@teksol.info&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt;16&lt;/span&gt; &lt;span class="c"&gt;# Get the rest of the data&lt;/span&gt;
&lt;span class="no"&gt;17&lt;/span&gt; &lt;span class="co"&gt;GET&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;people:francois&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;18&lt;/span&gt; 
&lt;span class="no"&gt;19&lt;/span&gt; &lt;span class="c"&gt;# Find the keys that have both father AND coder as tags&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt; &lt;span class="co"&gt;SINTER&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;tags:father&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;tags:coder&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So, which one should you choose?  The short answer is &amp;#8220;it depends&amp;#8221;.&lt;/p&gt;
&lt;p&gt;The longer answer is to use the best tool for the job.  Storing documents is different than storing simple key values.  Of course, both can be used for the same job, but Redis will be easier if you need a simple Hash/Dictionary.  Use MongoDB for storing structured data.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/_9kiGQHUx5Y" height="1" width="1"/&gt;</content>
    
    <category term="redis" label="Redis" />
    
    <category term="mongodb" label="Mongodb" />
    
    <category term="key-value-store" label="Key-value-store" />
      </entry>
  <entry>
    <title>Cliaws 1.5.0: cliec2 commands implemented</title>
    <link href="http://blog.teksol.info/2009/07/07/cliaws-1-dot-5-dot-0: cliec2 commands implemented.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-07-07:20090707204844</id>
    <updated>2009-07-07T20:48:44-04:00</updated>
    <content type="html">&lt;p&gt;Please grab cliaws from Rubyforge:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="er"&gt;$&lt;/span&gt; gem install cliaws

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;What is Cliaws?&lt;/h2&gt;
&lt;p&gt;Cliaws is a replacement for the Amazon EC2 API tools, but uses Ruby, and thus does not suffer from a long boot time.  Cliaws is also easier to setup:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="er"&gt;$&lt;/span&gt; &lt;span class="co"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;=&lt;span class="c"&gt;# Your Access ID&lt;/span&gt;
&lt;span class="no"&gt;2&lt;/span&gt; &lt;span class="er"&gt;$&lt;/span&gt; &lt;span class="co"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;=&lt;span class="c"&gt;# Your secret ID&lt;/span&gt;
&lt;span class="no"&gt;3&lt;/span&gt; &lt;span class="er"&gt;$&lt;/span&gt; &lt;span class="c"&gt;# Setup is done, enjoy!&lt;/span&gt;
&lt;span class="no"&gt;4&lt;/span&gt; &lt;span class="er"&gt;$&lt;/span&gt; clis3 list &lt;span class="co"&gt;YOUR_BUCKET&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; &lt;span class="er"&gt;$&lt;/span&gt; cliec2 launch &lt;span class="co"&gt;AMI&lt;/span&gt; --keypair &lt;span class="co"&gt;KEYNAME&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Please view the &lt;a href="http://github.com/francois/cliaws"&gt;README&lt;/a&gt; for some details.  The implementation is still the best place to get information about the options you can pass.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/T3l1rbtOuO0" height="1" width="1"/&gt;</content>
    
    <category term="aws" label="Aws" />
      </entry>
  <entry>
    <title>We've Been Doing TDD for Centuries, You Know..</title>
    <link href="http://blog.teksol.info/2009/06/20/weve-been-doing-tdd-for-centuries-you-know-dot.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-06-20:20090620215529</id>
    <updated>2009-06-20T21:55:29-04:00</updated>
    <content type="html">&lt;p&gt;Today, I was working on my lawn.  Spreading new soil to sow new grass.  I was looking at the ground, and I was pretty sure drainage wasn&amp;#8217;t going to be good.  I decided to make a test: I got out the garden hose and let it flow.  Sure enough, water was pooling instead of draining.  Back to the drawing board (or at least, a couple more rakes to spread things around).&lt;/p&gt;
&lt;p&gt;As I was working the soil, a thought hit me:  I&amp;#8217;d say nearly every profession has been doing test driven design (not development) since pretty much the dawn of time:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;the toolsmith in the ancient tribes would hit the stone, see if it did work or not, and repeat as appropriate;&lt;/li&gt;
	&lt;li&gt;the chef tastes his food before letting other people eat it;&lt;/li&gt;
	&lt;li&gt;the plumber tests the pipes before letting the water flow in;&lt;/li&gt;
	&lt;li&gt;the soldier tests his equipment before setting off on the battlefield;&lt;/li&gt;
	&lt;li&gt;naval engineers are &lt;a href="http://www.foxnews.com/story/0,2933,526833,00.html"&gt;simulation testing their latest aircraft carrier&lt;/a&gt; in an effort to ensure nothing is out of place;&lt;/li&gt;
	&lt;li&gt;certain programmers write automated tests for their code, to ensure it works as designed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If nearly every profession has been doing it for thousands of years, why aren&amp;#8217;t you doing it today?&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/TmM6ihURm0Q" height="1" width="1"/&gt;</content>
    
    <category term="tdd" label="Tdd" />
    
    <category term="test-driven-design" label="Test-driven-design" />
      </entry>
  <entry>
    <title>Piston on Windows: Some Pointers</title>
    <link href="http://blog.teksol.info/2009/06/07/piston-on-windows-some-pointers.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-06-07:20090607073420</id>
    <updated>2009-06-07T07:34:20-04:00</updated>
    <content type="html">&lt;p&gt;If you need to run Piston on Windows, &lt;a href="http://boxcycle.com"&gt;BoxCycle&lt;/a&gt; wrote some information about it at: &lt;a href="http://blog.boxcycle.com/2009/05/plugin-updates-svn-piston-windows-git/"&gt;Rails Plugin Updates, SVN, and Piston 2.0.2 on Windows&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/PRUhmeKQxYQ" height="1" width="1"/&gt;</content>
    
    <category term="piston" label="Piston" />
    
    <category term="windows" label="Windows" />
      </entry>
  <entry>
    <title>How to Parse HTML Documents Under JRuby Using XOM, TagSoup and Nux</title>
    <link href="http://blog.teksol.info/2009/05/01/how-to-parse-html-documents-under-jruby-using-xom-tagsoup-and-nux.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-05-01:20090501160316</id>
    <updated>2009-05-01T16:03:16-04:00</updated>
    <content type="html">&lt;p&gt;It&amp;#8217;s not that hard, but it still took me 2 hours to do it.  I had a couple of false starts, and I pored over documentation for a while until I hit upon the excellent &lt;a href="http://acs.lbl.gov/nux/"&gt;Nux&lt;/a&gt; library.&lt;/p&gt;
&lt;p&gt;I won&amp;#8217;t let you go through the same failures I had.  Here&amp;#8217;s the code:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="c"&gt;# Demonstrates how to parse a local HTML document using XOM,&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt; &lt;span class="c"&gt;# TagSoup and Nux, under JRuby.&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt; &lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt; &lt;span class="c"&gt;# http://www.xom.nu/&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; &lt;span class="c"&gt;# http://home.ccil.org/~cowan/XML/tagsoup/&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt; &lt;span class="c"&gt;# http://acs.lbl.gov/nux/&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt; 
&lt;span class="no"&gt; 8&lt;/span&gt; include &lt;span class="co"&gt;Java&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt; mydir = &lt;span class="co"&gt;File&lt;/span&gt;.expand_path(&lt;span class="co"&gt;File&lt;/span&gt;.dirname(&lt;span class="pc"&gt;__FILE__&lt;/span&gt;))
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt;11&lt;/span&gt; &lt;span class="c"&gt;# This is how you require libraries without touching your&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt; &lt;span class="c"&gt;# CLASSPATH from JRuby. I put the required files in vendor/.&lt;/span&gt;
&lt;span class="no"&gt;13&lt;/span&gt; &lt;span class="c"&gt;# Nux includes it's dependencies (XOM and saxon), so I didn't&lt;/span&gt;
&lt;span class="no"&gt;14&lt;/span&gt; &lt;span class="c"&gt;# have any other libraries to add.&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt; require &lt;span class="co"&gt;File&lt;/span&gt;.join(mydir, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;vendor&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;tagsoup.jar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;span class="no"&gt;16&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;%w(&lt;/span&gt;&lt;span class="k"&gt;nux.jar saxon8.jar xom.jar&lt;/span&gt;&lt;span class="dl"&gt;)&lt;/span&gt;&lt;/span&gt;.each &lt;span class="r"&gt;do&lt;/span&gt; |filename|
&lt;span class="no"&gt;17&lt;/span&gt;   require &lt;span class="co"&gt;File&lt;/span&gt;.join(mydir, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;vendor&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;nux&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, filename)
&lt;span class="no"&gt;18&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;19&lt;/span&gt; 
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt; import &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;org.ccil.cowan.tagsoup.Parser&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;21&lt;/span&gt; import &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;nu.xom.Builder&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;22&lt;/span&gt; 
&lt;span class="no"&gt;23&lt;/span&gt; builder = &lt;span class="co"&gt;Builder&lt;/span&gt;.new(&lt;span class="co"&gt;Parser&lt;/span&gt;.new)
&lt;span class="no"&gt;24&lt;/span&gt; 
&lt;span class="no"&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;/span&gt; &lt;span class="c"&gt;# XOM's Builder expects a full URL, so tell it where to find the&lt;/span&gt;
&lt;span class="no"&gt;26&lt;/span&gt; &lt;span class="c"&gt;# document.&lt;/span&gt;
&lt;span class="no"&gt;27&lt;/span&gt; doc = builder.build(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;file://&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;&lt;span class="co"&gt;File&lt;/span&gt;.expand_path(&lt;span class="co"&gt;File&lt;/span&gt;.join(mydir, &lt;span class="pc"&gt;ARGV&lt;/span&gt;[&lt;span class="i"&gt;0&lt;/span&gt;]))&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;span class="no"&gt;28&lt;/span&gt; puts doc.toXML

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;Extra!  Add XPath querying&lt;/h3&gt;
&lt;p&gt;Continuing from above, you can add XPath querying:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; import &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;nux.xom.xquery.XQueryUtil&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;2&lt;/span&gt; 
&lt;span class="no"&gt;3&lt;/span&gt; &lt;span class="c"&gt;# Must use '*:p'.  '*' stands for any/default namespace.&lt;/span&gt;
&lt;span class="no"&gt;4&lt;/span&gt; results = &lt;span class="co"&gt;XQueryUtil&lt;/span&gt;.xquery(doc, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;//*:p&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;span class="no"&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; p results.size
&lt;span class="no"&gt;6&lt;/span&gt; results.size.times &lt;span class="r"&gt;do&lt;/span&gt; |index|
&lt;span class="no"&gt;7&lt;/span&gt;   puts results.get(index).toXML
&lt;span class="no"&gt;8&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt; 

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Why am I going through these motions?  Because I wanted to use my 20% for fun.  Besides, I need to process large quantities of HTML as quickly as possible for a cool project I&amp;#8217;m working on, and JRuby seems to be the &lt;a href="http://twitter.com/fbeausoleil/status/1670082272"&gt;fastest implementation&lt;/a&gt;, according to my unscientific benchmark.&lt;/p&gt;
&lt;p&gt;But the real reason was that both &lt;a href="http://nokogiri.rubyforge.org/nokogiri/"&gt;Nokogiri&lt;/a&gt; and &lt;a href="http://wiki.github.com/why/hpricot"&gt;Hpricot&lt;/a&gt; wouldn&amp;#8217;t load/run under JRuby 1.2.0.&lt;/p&gt;
&lt;p&gt;Actually, let me rephrase that:  Nokogiri did load, but crashed while requiring the library:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="er"&gt;$&lt;/span&gt; jruby -w test.rb data.html 
&lt;span class="no"&gt; 2&lt;/span&gt; &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;francois/&lt;span class="co"&gt;Library&lt;/span&gt;/&lt;span class="co"&gt;Java&lt;/span&gt;/&lt;span class="co"&gt;JRuby&lt;/span&gt;/jruby-&lt;span class="fl"&gt;1.2&lt;/span&gt;.&lt;span class="i"&gt;0&lt;/span&gt;/lib/ruby/gems/&lt;span class="fl"&gt;1.8&lt;/span&gt;/gems/nokogiri-&lt;span class="fl"&gt;1.2&lt;/span&gt;.&lt;span class="i"&gt;3&lt;/span&gt;-java/lib/nokogiri/xml/node.rb:&lt;span class="i"&gt;180&lt;/span&gt;: undefined method &lt;span class="sh"&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;span class="k"&gt;next_sibling' for class &lt;/span&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class="co"&gt;Nokogiri&lt;/span&gt;::&lt;span class="co"&gt;XML&lt;/span&gt;::&lt;span class="co"&gt;Node&lt;/span&gt;&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt; (NameError)
&lt;span class="no"&gt; 3&lt;/span&gt;         from /Users/francois/Library/Java/JRuby/jruby-1.2.0/lib/ruby/gems/1.8/gems/nokogiri-1.2.3-java/lib/nokogiri/xml/node.rb:31:in `require&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;         from &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;francois/&lt;span class="co"&gt;Library&lt;/span&gt;/&lt;span class="co"&gt;Java&lt;/span&gt;/&lt;span class="co"&gt;JRuby&lt;/span&gt;/current/lib/ruby/site_ruby/&lt;span class="fl"&gt;1.8&lt;/span&gt;/rubygems/custom_require.rb:&lt;span class="i"&gt;31&lt;/span&gt;&lt;span class="sy"&gt;:in&lt;/span&gt; &lt;span class="sh"&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;span class="k"&gt;require'
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;         from /Users/francois/Library/Java/JRuby/jruby-1.2.0/lib/ruby/gems/1.8/gems/nokogiri-1.2.3-java/lib/nokogiri/xml.rb:3
&lt;span class="no"&gt; 6&lt;/span&gt;         from /Users/francois/Library/Java/JRuby/jruby-1.2.0/lib/ruby/gems/1.8/gems/nokogiri-1.2.3-java/lib/nokogiri/xml.rb:31:in &lt;/span&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;/span&gt;require&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;
&lt;span class="no"&gt; 7&lt;/span&gt;         from /Users/francois/Library/Java/JRuby/current/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 8&lt;/span&gt;         from &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;francois/&lt;span class="co"&gt;Library&lt;/span&gt;/&lt;span class="co"&gt;Java&lt;/span&gt;/&lt;span class="co"&gt;JRuby&lt;/span&gt;/jruby-&lt;span class="fl"&gt;1.2&lt;/span&gt;.&lt;span class="i"&gt;0&lt;/span&gt;/lib/ruby/gems/&lt;span class="fl"&gt;1.8&lt;/span&gt;/gems/nokogiri-&lt;span class="fl"&gt;1.2&lt;/span&gt;.&lt;span class="i"&gt;3&lt;/span&gt;-java/lib/nokogiri.rb:&lt;span class="i"&gt;10&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;         from &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;francois/&lt;span class="co"&gt;Library&lt;/span&gt;/&lt;span class="co"&gt;Java&lt;/span&gt;/&lt;span class="co"&gt;JRuby&lt;/span&gt;/jruby-&lt;span class="fl"&gt;1.2&lt;/span&gt;.&lt;span class="i"&gt;0&lt;/span&gt;/lib/ruby/gems/&lt;span class="fl"&gt;1.8&lt;/span&gt;/gems/nokogiri-&lt;span class="fl"&gt;1.2&lt;/span&gt;.&lt;span class="i"&gt;3&lt;/span&gt;-java/lib/nokogiri.rb:&lt;span class="i"&gt;36&lt;/span&gt;&lt;span class="sy"&gt;:in&lt;/span&gt; &lt;span class="sh"&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;span class="k"&gt;require'
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;         from /Users/francois/Library/Java/JRuby/current/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in &lt;/span&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;/span&gt;require&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;
&lt;span class="no"&gt;11&lt;/span&gt;         from test.rb:2
&lt;span class="no"&gt;12&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I have reported this &lt;a href="http://github.com/tenderlove/nokogiri/issues#issue/31"&gt;bug to the proper authorities&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hpricot is another matter entirely.  When I tried to use it earlier, I hit a roadblock because JRuby couldn&amp;#8217;t install the native extensions.  I tried again just now, and if you specify the version to be &lt;code&gt;~&amp;gt; 0.6.1&lt;/code&gt;, it works.  Specify any other version, and you&amp;#8217;re a sitting duck:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="er"&gt;$&lt;/span&gt; jruby -&lt;span class="co"&gt;S&lt;/span&gt; gem install -v &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;~&amp;gt; 0.6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; hpricot
&lt;span class="no"&gt; 2&lt;/span&gt; &lt;span class="co"&gt;Building&lt;/span&gt; native extensions.  &lt;span class="co"&gt;This&lt;/span&gt; could take a &lt;span class="r"&gt;while&lt;/span&gt;...
&lt;span class="no"&gt; 3&lt;/span&gt; &lt;span class="co"&gt;ERROR&lt;/span&gt;:  &lt;span class="co"&gt;Error&lt;/span&gt; installing hpricot:
&lt;span class="no"&gt; 4&lt;/span&gt;         &lt;span class="co"&gt;ERROR&lt;/span&gt;: &lt;span class="co"&gt;Failed&lt;/span&gt; to build gem native extension.
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt; 6&lt;/span&gt; /&lt;span class="co"&gt;Users&lt;/span&gt;/francois/&lt;span class="co"&gt;Library&lt;/span&gt;/&lt;span class="co"&gt;Java&lt;/span&gt;/&lt;span class="co"&gt;JRuby&lt;/span&gt;/current/bin/jruby extconf.rb install -v ~&amp;gt; &lt;span class="fl"&gt;0.6&lt;/span&gt; hpricot
&lt;span class="no"&gt; 7&lt;/span&gt; 
&lt;span class="no"&gt; 8&lt;/span&gt; 
&lt;span class="no"&gt; 9&lt;/span&gt; &lt;span class="co"&gt;Gem&lt;/span&gt; files will remain installed &lt;span class="r"&gt;in&lt;/span&gt; &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;francois/&lt;span class="co"&gt;Library&lt;/span&gt;/&lt;span class="co"&gt;Java&lt;/span&gt;/&lt;span class="co"&gt;JRuby&lt;/span&gt;/jruby-&lt;span class="fl"&gt;1.2&lt;/span&gt;.&lt;span class="i"&gt;0&lt;/span&gt;/lib/ruby/gems/&lt;span class="fl"&gt;1.8&lt;/span&gt;/gems/hpricot-&lt;span class="fl"&gt;0.8&lt;/span&gt;.&lt;span class="i"&gt;1&lt;/span&gt; &lt;span class="r"&gt;for&lt;/span&gt; inspection.
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; &lt;span class="co"&gt;Results&lt;/span&gt; logged to &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;francois/&lt;span class="co"&gt;Library&lt;/span&gt;/&lt;span class="co"&gt;Java&lt;/span&gt;/&lt;span class="co"&gt;JRuby&lt;/span&gt;/jruby-&lt;span class="fl"&gt;1.2&lt;/span&gt;.&lt;span class="i"&gt;0&lt;/span&gt;/lib/ruby/gems/&lt;span class="fl"&gt;1.8&lt;/span&gt;/gems/hpricot-&lt;span class="fl"&gt;0.8&lt;/span&gt;.&lt;span class="i"&gt;1&lt;/span&gt;/ext/hpricot_scan/gem_make.out
&lt;span class="no"&gt;11&lt;/span&gt; 
&lt;span class="no"&gt;12&lt;/span&gt; &lt;span class="er"&gt;$&lt;/span&gt; jruby -&lt;span class="co"&gt;S&lt;/span&gt; gem install -v &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;~&amp;gt; 0.6.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; hpricot
&lt;span class="no"&gt;13&lt;/span&gt; &lt;span class="co"&gt;Successfully&lt;/span&gt; installed hpricot-&lt;span class="fl"&gt;0.6&lt;/span&gt;.&lt;span class="i"&gt;164&lt;/span&gt;-java
&lt;span class="no"&gt;14&lt;/span&gt; &lt;span class="i"&gt;1&lt;/span&gt; gem installed
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt; &lt;span class="co"&gt;Installing&lt;/span&gt; ri documentation &lt;span class="r"&gt;for&lt;/span&gt; hpricot-&lt;span class="fl"&gt;0.6&lt;/span&gt;.&lt;span class="i"&gt;164&lt;/span&gt;-java...

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/myB-MHbRExk" height="1" width="1"/&gt;</content>
    
    <category term="jruby" label="Jruby" />
      </entry>
  <entry>
    <title>Keeping Yourself DRY in Shoulda Tests</title>
    <link href="http://blog.teksol.info/2009/04/29/keeping-yourself-dry-in-shoulda-tests.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-04-29:20090429220242</id>
    <updated>2009-04-29T22:02:42-04:00</updated>
    <content type="html">&lt;p&gt;When I&amp;#8217;m testing admin controllers, I often have tests that follow this form:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;test/functional/admin/orders_controller_test.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;OrdersControllerTest&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;TestCase&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   logged_in_as &lt;span class="sy"&gt;:active_user&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     context &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;on GET to :index&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;       setup &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;         get &lt;span class="sy"&gt;:index&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt; 
&lt;span class="no"&gt; 8&lt;/span&gt;       should_deny_access
&lt;span class="no"&gt; 9&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt; 
&lt;span class="no"&gt;12&lt;/span&gt;   not_logged_in &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt;13&lt;/span&gt;     context &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;on GET to :index&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt;14&lt;/span&gt;       setup &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt;         get &lt;span class="sy"&gt;:index&lt;/span&gt;
&lt;span class="no"&gt;16&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;17&lt;/span&gt; 
&lt;span class="no"&gt;18&lt;/span&gt;       should_deny_access
&lt;span class="no"&gt;19&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;21&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Well, this is all Ruby, right?  And Ruby has wonderful blocks, and blocks can be passed around&amp;#8230;&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;test/functional/admin/orders_controller_test.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;OrdersControllerTest&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;TestCase&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   deny_access_tests = lambda &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     context &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;on GET to :index&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;       setup &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;         get &lt;span class="sy"&gt;:index&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt; 
&lt;span class="no"&gt; 8&lt;/span&gt;       should_deny_access
&lt;span class="no"&gt; 9&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt; 
&lt;span class="no"&gt;12&lt;/span&gt;   logged_in_as &lt;span class="sy"&gt;:active_user&lt;/span&gt;, &amp;amp;deny_access_tests
&lt;span class="no"&gt;13&lt;/span&gt;   not_logged_in, &amp;amp;deny_access_tests
&lt;span class="no"&gt;14&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This is valid for any block of code that you want to test again and again:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;test/functional/admin/orders_controller_test.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;OrdersControllerTest&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;TestCase&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   successful_index_render = lambda &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     should_respond_with &lt;span class="sy"&gt;:success&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;     should_render_template &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;     should_assign_to &lt;span class="sy"&gt;:orders&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt; 
&lt;span class="no"&gt; 8&lt;/span&gt;   logged_in_as &lt;span class="sy"&gt;:admin&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;     context &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &amp;amp;successful_index_render
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt; 
&lt;span class="no"&gt;12&lt;/span&gt;   logged_in_as &lt;span class="sy"&gt;:sub_admin&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt;13&lt;/span&gt;     context &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &amp;amp;successful_index_render
&lt;span class="no"&gt;14&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Alternatively, and it might be easier in the end, you could use methods:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;test/functional/admin/orders_controller_test.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;OrdersControllerTest&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;TestCase&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.should_render_successful_index_response
&lt;span class="no"&gt; 3&lt;/span&gt;     should_respond_with &lt;span class="sy"&gt;:success&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;     should_render_template &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;     should_assign_to &lt;span class="sy"&gt;:orders&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt;     
&lt;span class="no"&gt; 8&lt;/span&gt;   logged_in_as &lt;span class="sy"&gt;:admin&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;     should_render_successful_index_response
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt; 
&lt;span class="no"&gt;12&lt;/span&gt;   logged_in_as &lt;span class="sy"&gt;:sub_admin&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt;13&lt;/span&gt;     should_render_successful_index_response
&lt;span class="no"&gt;14&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Note thought that you must define your methods at the top of your test case.  Remember that Ruby executes a class definition, so when you suddenly call &lt;code&gt;should_render_successful_index_response&lt;/code&gt;, the method definition has to be available, or else Ruby will complain with a &lt;code&gt;NoMethodError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ain&amp;#8217;t Ruby sweet?&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/FTAjRCM-KFs" height="1" width="1"/&gt;</content>
    
    <category term="unit-testing" label="Unit-testing" />
      </entry>
  <entry>
    <title>What if You Didn't Have Access to If/Then/Else?</title>
    <link href="http://blog.teksol.info/2009/04/17/what-if-you-didnt-have-access-to-if-then-else.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-04-17:20090417113316</id>
    <updated>2009-04-17T11:33:16-04:00</updated>
    <content type="html">&lt;p&gt;Image a Ruby where &lt;code&gt;if/then/else&lt;/code&gt; isn&amp;#8217;t available:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Account&lt;/span&gt;
&lt;span class="no"&gt;2&lt;/span&gt;   &lt;span class="c"&gt;# This method returns the balance in words, negative or positive, ready for display in the UI&lt;/span&gt;
&lt;span class="no"&gt;3&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;balance_in_words&lt;/span&gt;
&lt;span class="no"&gt;4&lt;/span&gt;     &lt;span class="c"&gt;# Err...  How do I do that?&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;6&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you were a strict Object-Oriented person, you&amp;#8217;d do it this way:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;PositiveBalanceAccount&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;balance_in_words&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Positive&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt; 
&lt;span class="no"&gt; 7&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;NegativeBalanceAccount&lt;/span&gt;
&lt;span class="no"&gt; 8&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;balance_in_words&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;     &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Negative&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;But then, your objects would have to change class whily-nilly.  Pretty darn hard.  But multi-method dispatching gives us a nice solution:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Account&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;initialize&lt;/span&gt;(balance)
&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="iv"&gt;@balance&lt;/span&gt; = balance
&lt;span class="no"&gt; 4&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt; 6&lt;/span&gt;   defmulti &lt;span class="sy"&gt;:balance_in_words&lt;/span&gt;,
&lt;span class="no"&gt; 7&lt;/span&gt;     lambda { &lt;span class="iv"&gt;@balance&lt;/span&gt; &amp;lt; &lt;span class="i"&gt;0&lt;/span&gt; } =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Negative&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
&lt;span class="no"&gt; 8&lt;/span&gt;     lambda { &lt;span class="iv"&gt;@balance&lt;/span&gt; &amp;gt; &lt;span class="i"&gt;0&lt;/span&gt; } =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Positive&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt;11&lt;/span&gt; &lt;span class="co"&gt;Account&lt;/span&gt;.new(&lt;span class="i"&gt;15&lt;/span&gt;).balance_in_words &lt;span class="c"&gt;# =&amp;gt; &amp;quot;Positive&amp;quot;&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt; &lt;span class="co"&gt;Account&lt;/span&gt;.new(&lt;span class="i"&gt;-5&lt;/span&gt;).balance_in_words &lt;span class="c"&gt;# =&amp;gt; &amp;quot;Negative&amp;quot;&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the observant amongst you, you might have noticed I forgot the nil balance case.  This is really a programming error, so it should be treated as such:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="co"&gt;Account&lt;/span&gt;.new(&lt;span class="i"&gt;0&lt;/span&gt;).balance_in_words
&lt;span class="no"&gt;2&lt;/span&gt; lib/defmulti.rb:&lt;span class="i"&gt;46&lt;/span&gt;&lt;span class="sy"&gt;:in&lt;/span&gt; &lt;span class="sh"&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;span class="k"&gt;balance_in_words': #&amp;lt;Account:0x197222c @balance=0&amp;gt; received balance_in_words but did not have a guard clause that matched and no else clause. (Defmulti::MissingGuardClause)
&lt;span class="no"&gt;3&lt;/span&gt;         from test.rb:16
&lt;span class="no"&gt;4&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the even more observant, yes, there exists an implementation that does exactly what I have described above.  It&amp;#8217;s called defmulti, it&amp;#8217;s a gem, and it&amp;#8217;s on GitHub:  http://github.com/francois/defmulti.&lt;/p&gt;
&lt;h2&gt;What is this useful for?&lt;/h2&gt;
&lt;p&gt;This library is a thought experiment.  When you lose the familiar tools, what can you do?  Of course, this library works atop the existing Ruby implementation and to be truly useful, syntax would have to be provided to make this much less verbose.  It&amp;#8217;s interesting nonetheless to see what can be done without the syntax extensions.&lt;/p&gt;
&lt;p&gt;Anyway, what would you use this for?  Multi-method dispatching is a tool that helps writing code without conditionals.  The conditionals are specified outside the block of code that executes.  The examples above are pretty thin, but looking at &lt;a href="http://blog.thinkrelevance.com/2008/8/26/java-next-3-dispatch-2"&gt;Java.next #3: Dispatch&lt;/a&gt;, I can provide another solution to his Ruby example:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; defmulti &lt;span class="sy"&gt;:letter_grade&lt;/span&gt;,
&lt;span class="no"&gt; 2&lt;/span&gt;   lambda {|grade| (&lt;span class="i"&gt;90&lt;/span&gt;..&lt;span class="i"&gt;100&lt;/span&gt;).include?(grade) || grade == &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;} =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
&lt;span class="no"&gt; 3&lt;/span&gt;   lambda {|grade| (&lt;span class="i"&gt;80&lt;/span&gt;...&lt;span class="i"&gt;90&lt;/span&gt;).include?(grade) || grade == &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;B&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;} =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;B&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
&lt;span class="no"&gt; 4&lt;/span&gt;   lambda {|grade| (&lt;span class="i"&gt;70&lt;/span&gt;...&lt;span class="i"&gt;80&lt;/span&gt;).include?(grade) || grade == &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;C&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;} =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;C&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;   lambda {|grade| (&lt;span class="i"&gt;60&lt;/span&gt;...&lt;span class="i"&gt;70&lt;/span&gt;).include?(grade) || grade == &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;D&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;} =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;D&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
&lt;span class="no"&gt; 6&lt;/span&gt;   lambda {|grade| ( &lt;span class="i"&gt;0&lt;/span&gt;...&lt;span class="i"&gt;60&lt;/span&gt;).include?(grade) || grade == &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;F&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;} =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;F&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt; 
&lt;span class="no"&gt; 8&lt;/span&gt; letter_grade &lt;span class="i"&gt;60&lt;/span&gt;  &lt;span class="c"&gt;# =&amp;gt; &amp;quot;D&amp;quot;&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt; letter_grade &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="c"&gt;# =&amp;gt; &amp;quot;A&amp;quot;&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; letter_grade &lt;span class="pc"&gt;nil&lt;/span&gt; &lt;span class="c"&gt;# =&amp;gt; Defmulti::MissingGuardClause&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Again, I&amp;#8217;m struck by the clunky syntax, but if we ignore that for a second, could this be even better than Stuart Halloway&amp;#8217;s example?  Ruby&amp;#8217;s case statement is very, very powerful and very easy to use.  Is this useful?  Not at the moment.  But it&amp;#8217;s a thought experiment I thought I&amp;#8217;d throw out there.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/1ak1zdaCU4k" height="1" width="1"/&gt;</content>
    
    <category term="language-design" label="Language-design" />
      </entry>
  <entry>
    <title>Beware of Git's Content Tracking</title>
    <link href="http://blog.teksol.info/2009/04/15/beware-of-gits-content-tracking.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-04-15:20090415003152</id>
    <updated>2009-04-15T00:31:52-04:00</updated>
    <content type="html">&lt;p&gt;A while ago, I was wondering how rename tracking in Git worked.  I was told that renames didn&amp;#8217;t really exist in Git, as Git tracked content, not files themselves.  Fair enough.&lt;/p&gt;
&lt;p&gt;But, I just stumbled upon something:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; $ gdc vendor/plugins/acts_as_money/LICENSE 
&lt;span class="no"&gt; 2&lt;/span&gt; diff --git a/vendor/plugins/acts_as_money/LICENSE b/vendor/plugins/acts_as_money/LICENSE
&lt;span class="no"&gt; 3&lt;/span&gt; index e69de29..a273c73 100644
&lt;span class="no"&gt; 4&lt;/span&gt; --- a/vendor/plugins/acts_as_money/LICENSE
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; +++ b/vendor/plugins/acts_as_money/LICENSE
&lt;span class="no"&gt; 6&lt;/span&gt; @@ -0,0 +1,4 @@
&lt;span class="no"&gt; 7&lt;/span&gt; +one:
&lt;span class="no"&gt; 8&lt;/span&gt; +  user: active
&lt;span class="no"&gt; 9&lt;/span&gt; +  name: name
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; +  description: description

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Tell me, does that look right?  I&amp;#8217;ll manually fix that file, but that just decreased my confidence level in Git.&lt;/p&gt;
&lt;p&gt;The original LICENSE file was empty, and there apparently was another file (a fixture file) that was empty too, and the latter saw some content added.&lt;/p&gt;
&lt;p&gt;Why did this happen?  LICENSE was updated as part of a merge&amp;mdash;from a branch in which the LICENSE file doesn&amp;#8217;t exist.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/9AdHpwq6dA4" height="1" width="1"/&gt;</content>
    
    <category term="git" label="Git" />
      </entry>
  <entry>
    <title>Returning CSV data to the browser - revisited</title>
    <link href="http://blog.teksol.info/2009/04/06/returning-csv-data-to-the-browser-revisited.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-04-06:20090406102330</id>
    <updated>2009-04-06T10:23:30-04:00</updated>
    <content type="html">&lt;p&gt;Back in 2006, I wrote &lt;a href="http://blog.teksol.info/2006/03/23/returning-csv-data-to-the-browser"&gt;Returning CSV data to the browser&lt;/a&gt;.  The method I was using back then is way obsolete.  Let&amp;#8217;s use the correct way to do it.  If you want to follow along, just browse over to &lt;a href="http://github.com/francois/blog.teksol.info/tree/master/content/2009/04/06/returning-csv-data-to-the-browser-revisited"&gt;returning-csv-data-to-the-browser-revisited&lt;/a&gt; and read on.&lt;/p&gt;
&lt;p&gt;First of all, the correct way to return non HTML data to the browser is to use &lt;a href="http://api.rubyonrails.org/classes/ActionController/MimeResponds/InstanceMethods.html#M000368"&gt;respond_to&lt;/a&gt;.  Let&amp;#8217;s do so here:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;app/controllers/reports/timelines_controller.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Reports::TimelinesController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ApplicationController&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;show&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="iv"&gt;@timelines&lt;/span&gt; = &lt;span class="co"&gt;Timeline&lt;/span&gt;.all
&lt;span class="no"&gt; 4&lt;/span&gt;     respond_to &lt;span class="r"&gt;do&lt;/span&gt; |format|
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;       format.csv &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;         response.headers[&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;]        = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;text/csv; charset=UTF-8; header=present&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt;         response.headers[&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Content-Disposition&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;] = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;attachment; filename=timeline-report.csv&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 8&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The rest is pretty easy.  Generating the data is easy enough:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;app/models/timeline.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;fastercsv&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt; 
&lt;span class="no"&gt; 3&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Timeline&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;   default_scope &lt;span class="sy"&gt;:order&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;started_at ASC&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt; 6&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;to_csv&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt;     &lt;span class="co"&gt;FasterCSV&lt;/span&gt;.generate_line([
&lt;span class="no"&gt; 8&lt;/span&gt;           started_at.to_s(&lt;span class="sy"&gt;:db&lt;/span&gt;),
&lt;span class="no"&gt; 9&lt;/span&gt;           ended_at.to_s(&lt;span class="sy"&gt;:db&lt;/span&gt;),
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;           project_id]).chomp
&lt;span class="no"&gt;11&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;And rendering the view?  Look how easy this gets:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;app/views/reports/timelines/show.csv.erb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="co"&gt;Started&lt;/span&gt; &lt;span class="co"&gt;At&lt;/span&gt;,&lt;span class="co"&gt;Ended&lt;/span&gt; &lt;span class="co"&gt;At&lt;/span&gt;,&lt;span class="co"&gt;Project&lt;/span&gt; &lt;span class="co"&gt;ID&lt;/span&gt;
&lt;span class="no"&gt;2&lt;/span&gt; &amp;lt;&lt;span class="s"&gt;&lt;span class="dl"&gt;%=&lt;/span&gt;&lt;span class="k"&gt; render :partial &lt;/span&gt;&lt;span class="dl"&gt;=&lt;/span&gt;&lt;/span&gt;&amp;gt; &lt;span class="iv"&gt;@timelines&lt;/span&gt; %&amp;gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Notice the file&amp;#8217;s name?  &lt;code&gt;show.csv.erb&lt;/code&gt;?  The &lt;code&gt;csv&lt;/code&gt; in the filename is what connects the respond_to call with the view.&lt;/p&gt;
&lt;p&gt;And the final piece, the timeline partial:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;app/views/reports/timelines/_timeline.csv.erb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &amp;lt;&lt;span class="s"&gt;&lt;span class="dl"&gt;%=&lt;/span&gt;&lt;span class="k"&gt; timeline.to_csv %&amp;gt;
&lt;span class="no"&gt;2&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, on to the next obsolete article in the bunch&amp;#8230;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/-1FwRW8nnZY" height="1" width="1"/&gt;</content>
      </entry>
  <entry>
    <title>Building the SQL WHERE Clause Dynamically - Updated</title>
    <link href="http://blog.teksol.info/2009/04/02/building-the-sql-where-clause-dynamically-updated.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-04-02:20090402092911</id>
    <updated>2009-04-02T09:29:11-04:00</updated>
    <content type="html">&lt;p&gt;Back in October 2005, I wrote an article entitled &lt;a href="http://blog.teksol.info/2005/10/31/building-the-sql-where-clause-dynamically-in-rails"&gt;Building the SQL WHERE Clause Dynamically&lt;/a&gt;.  This article is obsolete and I&amp;#8217;m replacing it with this one instead.&lt;/p&gt;
&lt;p&gt;What do you do with a FORM that has multiple conditions?  Something like this:&lt;/p&gt;
&lt;form action="#" method="get"&gt;
&lt;p&gt;
    &lt;label for="region"&gt;Region:&lt;/label&gt;
&lt;select id="region" name="region"&gt;
      &lt;option&gt;All&lt;/option&gt;
      &lt;option&gt;North&lt;/option&gt;
      &lt;option&gt;East&lt;/option&gt;
      &lt;option&gt;West&lt;/option&gt;
      &lt;option&gt;South&lt;/option&gt;
&lt;/select&gt;
&lt;/p&gt;
&lt;p&gt;
    &lt;label for="salary"&gt;Salary:&lt;/label&gt;
&lt;select id="salary" name="salary"&gt;
      &lt;option&gt;All&lt;/option&gt;
      &lt;option&gt;0-10K&lt;/option&gt;
      &lt;option&gt;10K-20K&lt;/option&gt;
      &lt;option&gt;20K-50K&lt;/option&gt;
      &lt;option&gt;50K+&lt;/option&gt;
&lt;/select&gt;
&lt;/p&gt;
&lt;p&gt;
    &lt;label for="tags"&gt;Tags:&lt;/label&gt;
&lt;input id="tags" name="tags"/&gt;
&lt;/p&gt;
&lt;/form&gt;
&lt;p&gt;The way I would do it today is using named scopes:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;app/models/person.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Person&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   is_taggable
&lt;span class="no"&gt; 3&lt;/span&gt; 
&lt;span class="no"&gt; 4&lt;/span&gt;   named_scope &lt;span class="sy"&gt;:in_region&lt;/span&gt;,
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;     lambda {|region| {&lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; {&lt;span class="sy"&gt;:region&lt;/span&gt; =&amp;gt; region}}}
&lt;span class="no"&gt; 6&lt;/span&gt;   named_scope &lt;span class="sy"&gt;:with_salary&lt;/span&gt;,
&lt;span class="no"&gt; 7&lt;/span&gt;     lambda {|lower, upper| {&lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt;
&lt;span class="no"&gt; 8&lt;/span&gt;       {&lt;span class="sy"&gt;:salary&lt;/span&gt; =&amp;gt; (lower .. upper)}}}
&lt;span class="no"&gt; 9&lt;/span&gt; 
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   &lt;span class="c"&gt;# Elided, but something similar to the above&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt;   named_scope &lt;span class="sy"&gt;:tagged_with&lt;/span&gt;,
&lt;span class="no"&gt;12&lt;/span&gt;     lambda {|tags| {&lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt;
&lt;span class="no"&gt;13&lt;/span&gt;       ...}}
&lt;span class="no"&gt;14&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then you need your controller to translate between what the view provides and what the model expects:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;app/controllers/searches_controller.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;SearchesController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ApplicationController&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;show&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     root = &lt;span class="co"&gt;Person&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;     root = root.in_region(params[&lt;span class="sy"&gt;:region&lt;/span&gt;]) &lt;span class="r"&gt;unless&lt;/span&gt; params[&lt;span class="sy"&gt;:region&lt;/span&gt;] =~ &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="mod"&gt;i&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;     &lt;span class="r"&gt;unless&lt;/span&gt; params[&lt;span class="sy"&gt;:salary&lt;/span&gt;] =~ &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="mod"&gt;i&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;       params[&lt;span class="sy"&gt;:salary&lt;/span&gt;].gsub!(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;K&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;000&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;span class="no"&gt; 7&lt;/span&gt;       lower, upper = params[&lt;span class="sy"&gt;:salary&lt;/span&gt;].split(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;span class="no"&gt; 8&lt;/span&gt;       root = root.with_salary(lower, upper)
&lt;span class="no"&gt; 9&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;     root = root.tagged_with(&lt;span class="co"&gt;Tag&lt;/span&gt;.parse(params[&lt;span class="sy"&gt;:tags&lt;/span&gt;])) &lt;span class="r"&gt;unless&lt;/span&gt; params[&lt;span class="sy"&gt;:tags&lt;/span&gt;].blank?
&lt;span class="no"&gt;11&lt;/span&gt;     &lt;span class="iv"&gt;@people&lt;/span&gt; = root.paginate(&lt;span class="sy"&gt;:page&lt;/span&gt; =&amp;gt; params[&lt;span class="sy"&gt;:page&lt;/span&gt;])
&lt;span class="no"&gt;12&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;13&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For simpler forms, the approach above would be sufficient.  For more complex forms, I suggest you look at plugins that aim to provide more structure around your SQL clauses, such as:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://ambition.rubyforge.org/"&gt;Ambition&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://agilewebdevelopment.com/plugins/criteria_query"&gt;criteria_query&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://agilewebdevelopment.com/plugins/ez_where"&gt;ez_where&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also suggest looking at &lt;a href="http://agilewebdevelopment.com/plugins/category/4"&gt;Searching and Queries Plugins &amp;#8211; Agile Web Development&lt;/a&gt; to find other plugins.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/Xs8sAvTz9aA" height="1" width="1"/&gt;</content>
    
    <category term="tips" label="Tips" />
      </entry>
  <entry>
    <title>Actions With Dashes - Updated</title>
    <link href="http://blog.teksol.info/2009/04/02/actions-with-dashes-updated.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-04-02:20090402092259</id>
    <updated>2009-04-02T09:00:00-04:00</updated>
    <content type="html">&lt;p&gt;Back in October 2005, I wrote an article entitled &lt;a href="http://blog.teksol.info/2005/10/25/actions-with-dashes"&gt;Actions with Dashes&lt;/a&gt;.  This article is perfectly obsolete and uses a trick instead of the router as it was intended.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s say you want an action where the URL is &lt;code&gt;http://myproject.com/contact-us&lt;/code&gt;.  The best way to do that currently is to use Rails&amp;#8217; router:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;config/routes.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; map.contact_us &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/contact-us&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:controller&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;pages&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:action&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;show&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:page&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;contact_us&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="code"&gt;
&lt;h5&gt;app/controllers/pages_controller.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;PagesController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActionController&lt;/span&gt;
&lt;span class="no"&gt;2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;show&lt;/span&gt;
&lt;span class="no"&gt;3&lt;/span&gt;     render &lt;span class="sy"&gt;:action&lt;/span&gt; =&amp;gt; params[&lt;span class="sy"&gt;:page&lt;/span&gt;]
&lt;span class="no"&gt;4&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code class="class"&gt;PagesController&lt;/code&gt; above is also good for any other static pages you want to serve on your site.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/8vzxfhkX3cI" height="1" width="1"/&gt;</content>
    
    <category term="tips" label="Tips" />
      </entry>
  <entry>
    <title>I'm Updating and Obsolescing Articles on My Blog</title>
    <link href="http://blog.teksol.info/2009/04/02/im-updating-and-obsolescing-articles-on-my-blog.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-04-02:20090402100836</id>
    <updated>2009-04-02T08:00:00-04:00</updated>
    <content type="html">&lt;p&gt;A couple of days ago, &lt;a href="http://twitter.com/jfcouture"&gt;@jfcouture&lt;/a&gt; tweeted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://twitter.com/cmercier"&gt;@cmercier&lt;/a&gt; People with stuff older than 6 months in the Rails world on their blog should do the same!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style="text-align:right"&gt;&lt;a href="http://twitter.com/jfcouture/status/1415694931"&gt;@jfcouture&lt;/a&gt; via &lt;a href="http://twitter.com/"&gt;Twitter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This was in response to Carl&amp;#8217;s rant that obsolete Merb tutorials should be removed / deprecated.&lt;/p&gt;
&lt;p&gt;I decided to follow their advice.  I&amp;#8217;m revisiting all my old posts and saying they are either obsolete, or providing links to replacements.  Expect to see at least a post a day for the next couple of weeks as I revisit my old posts.  This is interesting!  I had forgotten about a lot of things I had written about.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/AM5dY150fvk" height="1" width="1"/&gt;</content>
    
    <category term="general" label="General" />
      </entry>
  <entry>
    <title>Piston 2.0 Finally Released: With Git, Subversion and Ruby 1.9</title>
    <link href="http://blog.teksol.info/2009/04/01/piston-2-dot-0-finally-released-with-git-subversion-and-ruby-1-dot-9.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-04-01:20090401182758</id>
    <updated>2009-04-01T18:27:58-04:00</updated>
    <content type="html">&lt;p&gt;It&amp;#8217;s been a long time coming, I know.  Believe me when I say so.  But the good news first:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Piston 2.0 supports Git repositories and working copies&lt;/li&gt;
	&lt;li&gt;Piston 2.0 supports Subversion repositories and working copies&lt;/li&gt;
	&lt;li&gt;Piston 2.0 is Ruby 1.9.1 compliant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Get it&lt;/h3&gt;
&lt;p&gt;(Allow a couple of hours for the gem to propagate)&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; $ sudo gem install piston

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;What is Piston?&lt;/h3&gt;
&lt;p&gt;Piston eases your vendor branch management worries.  It copies vendor/upstream code into your own repository, preserving local patches.&lt;/p&gt;
&lt;h3&gt;The new website&lt;/h3&gt;
&lt;p&gt;Yes, I have a new look to go along with the website.  &lt;a href="http://piston.rubyforge.org/"&gt;http://piston.rubyforge.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is also an avenue for asking questions and getting help about Piston.  Register to the &lt;a href="http://groups.google.com/group/piston-discussion"&gt;piston-discussion Google Group&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Please have fun and enjoy yourselves.&lt;/p&gt;
&lt;p&gt;NOTE: This is not an April&amp;#8217;s Fool joke (although it would have been really cool to do it if I had thought so!)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/oW-xlXqJUno" height="1" width="1"/&gt;</content>
      </entry>
  <entry>
    <title>ArgumentError on #Sum When Using Classifier::Bayes</title>
    <link href="http://blog.teksol.info/2009/03/27/argumenterror-on-number-sum-when-using-classifier-bayes.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-03-27:20090327220424</id>
    <updated>2009-03-27T22:04:24-04:00</updated>
    <content type="html">&lt;p&gt;If you want to use the &lt;a href="http://classifier.rubyforge.org/"&gt;Classifier&lt;/a&gt; gem within a &lt;a href="http://rubyonrails.org/"&gt;Rails&lt;/a&gt; project, you&amp;#8217;re in for a surprise:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;Library&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class="co"&gt;Ruby&lt;/span&gt;/&lt;span class="co"&gt;Gems&lt;/span&gt;/&lt;span class="fl"&gt;1.8&lt;/span&gt;/gems/activerecord-&lt;span class="fl"&gt;2.3&lt;/span&gt;.&lt;span class="i"&gt;2&lt;/span&gt;/lib/active_record/attribute_methods.rb:&lt;span class="i"&gt;102&lt;/span&gt;&lt;span class="sy"&gt;:in&lt;/span&gt; &lt;span class="sh"&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;span class="k"&gt;sum': wrong number of arguments (1 for 0) (ArgumentError)
&lt;span class="no"&gt; 2&lt;/span&gt;         from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/attribute_methods.rb:102:in &lt;/span&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;/span&gt;instance_method_already_implemented?&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;         from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/attribute_methods.rb:72:in `define_attribute_methods&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;         from &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;Library&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class="co"&gt;Ruby&lt;/span&gt;/&lt;span class="co"&gt;Gems&lt;/span&gt;/&lt;span class="fl"&gt;1.8&lt;/span&gt;/gems/activerecord-&lt;span class="fl"&gt;2.3&lt;/span&gt;.&lt;span class="i"&gt;2&lt;/span&gt;/lib/active_record/attribute_methods.rb:&lt;span class="i"&gt;71&lt;/span&gt;&lt;span class="sy"&gt;:in&lt;/span&gt; &lt;span class="sh"&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;span class="k"&gt;each'
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;         from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/attribute_methods.rb:71:in &lt;/span&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;/span&gt;define_attribute_methods&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;         from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/attribute_methods.rb:351:in `respond_to?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt;         from &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;Library&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class="co"&gt;Ruby&lt;/span&gt;/&lt;span class="co"&gt;Gems&lt;/span&gt;/&lt;span class="fl"&gt;1.8&lt;/span&gt;/gems/activerecord-&lt;span class="fl"&gt;2.3&lt;/span&gt;.&lt;span class="i"&gt;2&lt;/span&gt;/lib/active_record/associations/association_proxy.rb:&lt;span class="i"&gt;219&lt;/span&gt;&lt;span class="sy"&gt;:in&lt;/span&gt; &lt;span class="sh"&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;span class="k"&gt;method_missing'
&lt;span class="no"&gt; 8&lt;/span&gt;         from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/association_proxy.rb:219:in &lt;/span&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;/span&gt;each&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;         from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/association_proxy.rb:219:in `send&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;         from &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;Library&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class="co"&gt;Ruby&lt;/span&gt;/&lt;span class="co"&gt;Gems&lt;/span&gt;/&lt;span class="fl"&gt;1.8&lt;/span&gt;/gems/activerecord-&lt;span class="fl"&gt;2.3&lt;/span&gt;.&lt;span class="i"&gt;2&lt;/span&gt;/lib/active_record/associations/association_proxy.rb:&lt;span class="i"&gt;219&lt;/span&gt;&lt;span class="sy"&gt;:in&lt;/span&gt; &lt;span class="sh"&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;span class="k"&gt;method_missing'
&lt;span class="no"&gt;11&lt;/span&gt;         from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/association_collection.rb:364:in &lt;/span&gt;&lt;span class="dl"&gt;`&lt;/span&gt;&lt;/span&gt;method_missing_without_paginate&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;
&lt;span class="no"&gt;12&lt;/span&gt;         from /Users/francois/Projects/family_budget/vendor/gems/mislav-will_paginate-2.3.5/lib/will_paginate/finder.rb:167:in `method_missing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;13&lt;/span&gt;         from script/classifier:&lt;span class="i"&gt;8&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This is because the Classifier gem implements &lt;code&gt;Array#sum&lt;/code&gt;.  Unfortunately, Classifier is loaded after Rails, which means ActiveSupport&amp;#8217;s own &lt;code&gt;#sum&lt;/code&gt; is replaced.  The solution?  Use either &lt;a href="http://github.com/yury/classifier"&gt;Yury&amp;#8217;s fork&lt;/a&gt;, or &lt;a href="http://github.com/francois/classifier"&gt;my own&lt;/a&gt;.  Both use ActiveSupport&amp;#8217;s #sum instead of a custom implementation.&lt;/p&gt;
&lt;p&gt;The only difference between my version and Yury&amp;#8217;s is I use &lt;a href="http://github.com/technicalpickles/jeweler"&gt;Jeweler&lt;/a&gt; to maintain and package the gem.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/ndTHvGOxoX4" height="1" width="1"/&gt;</content>
    
    <category term="tips" label="Tips" />
      </entry>
  <entry>
    <title>Know How to Use Your Tools: ResourceController</title>
    <link href="http://blog.teksol.info/2009/03/10/know-how-to-use-your-tools-resourcecontroller.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-03-10:20090310151208</id>
    <updated>2009-03-10T15:12:08-04:00</updated>
    <content type="html">&lt;p&gt;Today, I was building a controller action that was polymorphically finding and associating an object.  I could have used a &lt;del&gt;simple&lt;/del&gt; dumb solution, such as this:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;app/controllers/watches_controller.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;WatchesController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ApplicationController&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;create&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="iv"&gt;@watch&lt;/span&gt; = &lt;span class="co"&gt;Watch&lt;/span&gt;.build(&lt;span class="sy"&gt;:watcher&lt;/span&gt; =&amp;gt; current_person)
&lt;span class="no"&gt; 4&lt;/span&gt;     &lt;span class="iv"&gt;@watch&lt;/span&gt;.subject = &lt;span class="r"&gt;case&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;                      &lt;span class="r"&gt;when&lt;/span&gt; params[&lt;span class="sy"&gt;:person_id&lt;/span&gt;]
&lt;span class="no"&gt; 6&lt;/span&gt;                        &lt;span class="co"&gt;Person&lt;/span&gt;.find(params[&lt;span class="sy"&gt;:person_id&lt;/span&gt;])
&lt;span class="no"&gt; 7&lt;/span&gt;                      &lt;span class="r"&gt;when&lt;/span&gt; params[&lt;span class="sy"&gt;:event_id&lt;/span&gt;]
&lt;span class="no"&gt; 8&lt;/span&gt;                        &lt;span class="co"&gt;Event&lt;/span&gt;.find(params[&lt;span class="sy"&gt;:event_id&lt;/span&gt;])
&lt;span class="no"&gt; 9&lt;/span&gt;                      &lt;span class="r"&gt;else&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;                        raise &lt;span class="co"&gt;ArgumentError&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Don't know how to handle other keys... &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;params.keys.inspect&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt;                      &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt;     &lt;span class="iv"&gt;@watch&lt;/span&gt;.save!
&lt;span class="no"&gt;13&lt;/span&gt;     flash[&lt;span class="sy"&gt;:notice&lt;/span&gt;] = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;You're watching &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;&lt;span class="iv"&gt;@watch&lt;/span&gt;.subject.name&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;14&lt;/span&gt;     redirect_to root_url
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;16&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;That sucked.  Really bad.  Why did I have to have a &lt;code class="keyword"&gt;case&lt;/code&gt;/&lt;code class="keyword"&gt;switch&lt;/code&gt; statement in my controller?  Why not use a simpler alternative?  I could have gone the full polymorphic route too:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;app/controllers/watches_controller.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;WatchesController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ApplicationController&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;create&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="iv"&gt;@watch&lt;/span&gt; = &lt;span class="co"&gt;Watch&lt;/span&gt;.build(&lt;span class="sy"&gt;:watcher&lt;/span&gt; =&amp;gt; current_person)
&lt;span class="no"&gt; 4&lt;/span&gt; 
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;     &lt;span class="c"&gt;# For illustration purposes, this is fine, but INSECURE!!!&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;     &lt;span class="iv"&gt;@watch&lt;/span&gt;.subject_type = params[&lt;span class="sy"&gt;:subject_type&lt;/span&gt;]
&lt;span class="no"&gt; 7&lt;/span&gt;     &lt;span class="iv"&gt;@watch&lt;/span&gt;.subject_id   = params[&lt;span class="sy"&gt;:subject_id&lt;/span&gt;]
&lt;span class="no"&gt; 8&lt;/span&gt;     &lt;span class="iv"&gt;@watch&lt;/span&gt;.save!
&lt;span class="no"&gt; 9&lt;/span&gt; 
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;     flash[&lt;span class="sy"&gt;:notice&lt;/span&gt;] = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;You're watching &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;&lt;span class="iv"&gt;@watch&lt;/span&gt;.subject.name&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt;     redirect_to root_url
&lt;span class="no"&gt;12&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;13&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then, I remembered that my controller was already a &lt;a href="http://jamesgolick.com/resource_controller"&gt;ResourceController&lt;/a&gt; implementation.  I opened up the code and used this instead:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;app/controllers/watches_controller.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;WatchesController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ResourceController&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;
&lt;span class="no"&gt;2&lt;/span&gt;   belongs_to &lt;span class="sy"&gt;:person&lt;/span&gt;, &lt;span class="sy"&gt;:event&lt;/span&gt;
&lt;span class="no"&gt;3&lt;/span&gt; 
&lt;span class="no"&gt;4&lt;/span&gt;   create.before &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;     &lt;span class="iv"&gt;@watch&lt;/span&gt;.watcher = current_person
&lt;span class="no"&gt;6&lt;/span&gt;     &lt;span class="iv"&gt;@watch&lt;/span&gt;.subject = parent_object
&lt;span class="no"&gt;7&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;8&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I needed to add a bit of infrastructure (some routes and has_many declarations in my models), but those were benefits (I know I&amp;#8217;ll need them later anyway).  My controller code is simpler, and the intention is clearer, I think.&lt;/p&gt;
&lt;p&gt;In the same vein, are you following &lt;a href="http://search.twitter.com/search?q=%23standup"&gt;#standup&lt;/a&gt; on Twitter?&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/ZjXEqfH-L8A" height="1" width="1"/&gt;</content>
    
    <category term="plugins" label="Plugins" />
      </entry>
  <entry>
    <title>How to Get Your Autotest Output Directly in Your Editor</title>
    <link href="http://blog.teksol.info/2009/03/06/how-to-get-your-autotest-output-directly-in-your-editor.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-03-06:20090306162627</id>
    <updated>2009-03-06T16:26:27-05:00</updated>
    <content type="html">&lt;p&gt;While discussing in &lt;a href="http://campfirenow.com/"&gt;Campfire&lt;/a&gt; with &lt;a href="http://jamesgolick.com/"&gt;James Golick&lt;/a&gt; today, James said he would prefer to have his output directly in &lt;a href="http://www.vim.org/"&gt;Vim&lt;/a&gt;, our editor of choice.  I initially said that since Vim isn&amp;#8217;t &lt;a href="http://www.gnu.org/software/emacs/"&gt;Emacs&lt;/a&gt; and can&amp;#8217;t support a live console, it wouldn&amp;#8217;t work.  James told me to think outside the box.&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; $ autotest &amp;amp;&amp;gt;tmp/autotest.out &amp;amp;
&lt;span class="no"&gt;2&lt;/span&gt; $ vim
&lt;span class="no"&gt;3&lt;/span&gt; # :e tmp/autotest.out

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Yes, thinking outside the box, and &lt;strong&gt;knowing your tools&lt;/strong&gt; makes it much easier.  So far, I&amp;#8217;m liking autotest output in Vim very much.  It&amp;#8217;s easy to navigate the output using Vim movement commands:  &lt;tt&gt;/Person&lt;/tt&gt; finds the errors in person, &lt;tt&gt;gf&lt;/tt&gt; opens the named file, etc.  All in all, this is a pretty good experience.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/kgH6N8VFflY" height="1" width="1"/&gt;</content>
    
    <category term="autotest" label="Autotest" />
    
    <category term="tips" label="Tips" />
      </entry>
  <entry>
    <title>Getting Started with Autotest</title>
    <link href="http://blog.teksol.info/2009/03/06/getting-started-with-autotest.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-03-06:20090306105913</id>
    <updated>2009-03-06T10:59:13-05:00</updated>
    <content type="html">&lt;p&gt;Since yesterday, I read a bit more about &lt;a href="http://www.zenspider.com/ZSS/Products/ZenTest/"&gt;autotest&lt;/a&gt;.  I stumbled on &lt;a href="http://ph7spot.com/articles/getting_started_with_autotest"&gt;Getting Started with Autotest&lt;/a&gt; which you should read immediately.  The article talks about setting up your plugins and doing all kinds of interesting things.&lt;/p&gt;
&lt;p&gt;Getting more proficient with my tools now!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/NY0XTIvYF3Q" height="1" width="1"/&gt;</content>
    
    <category term="autotest" label="Autotest" />
    
    <category term="testing" label="Testing" />
      </entry>
  <entry>
    <title>How I Wasted an Hour on Something Git did in 10 seconds?</title>
    <link href="http://blog.teksol.info/2009/03/05/how-i-wasted-an-hour-on-something-git-did-in-10-seconds.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-03-05:20090305130017</id>
    <updated>2009-03-05T13:00:17-05:00</updated>
    <content type="html">&lt;p&gt;&lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt; is a wonderful system.  It has all kinds of nooks and crannies that one forgets about.  Anyway, I was doing client work this morning, and my build server (&lt;a href="http://github.com/foca/integrity"&gt;Integrity&lt;/a&gt;, but this isn&amp;#8217;t about Integrity) didn&amp;#8217;t fail, although my local tests were failing.  I began investigating, and &lt;code class="path"&gt;vendor/plugins&lt;/code&gt; wasn&amp;#8217;t even on the build server.  Uh?  I began investigating, and found &lt;code class="path"&gt;vendor/plugins&lt;/code&gt; had turned into a &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html"&gt;git submodule&lt;/a&gt; a while back.  I even had the commit&amp;#8217;s SHA1 of when that happened.  I found myself in the unenvious position of having to find and install every plugin we had before that commit back into the application.&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; # 04f7e1 is the commit when the submodule was born
&lt;span class="no"&gt;2&lt;/span&gt; # Checkout the commit before that error occured
&lt;span class="no"&gt;3&lt;/span&gt; $ git checkout 04f7e1^
&lt;span class="no"&gt;4&lt;/span&gt; $ ls vendor/plugins &amp;gt; plugins
&lt;span class="no"&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; $ git checkout master
&lt;span class="no"&gt;6&lt;/span&gt; $ cat plugins
&lt;span class="no"&gt;7&lt;/span&gt; $ script/plugin install git://github.com/giraffesoft/timeline_fu.git
&lt;span class="no"&gt;8&lt;/span&gt; # And so on, for the 9 other plugins

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;And then, there was this one plugin which we haven&amp;#8217;t released yet which I was missing.  It&amp;#8217;s called configurable_mailer, and allows us to configure &lt;a href="http://api.rubyonrails.org/classes/ActionMailer/Base.html"&gt;ActionMailer::Base&lt;/a&gt; from tests and from some environment variables.  Anyway, I again checked out the code from &lt;code class="sha1"&gt;04f7e1^&lt;/code&gt;, copied &lt;code class="path"&gt;vendor/plugins/configurable_mailer&lt;/code&gt; outside the repository, checked out master again, and moved the plugin back into place.  As you can imagine, this was painful.  And error-prone.  Then I ran my tests, and immediately had errors.  This application is based off of &lt;a href="http://github.com/giraffesoft/blank"&gt;Blank&lt;/a&gt;, and was using slightly modified versions of certain plugins.  So I added Blank as a remote, fetched and merged.  Ran my tests again.  Still had errors.  And lots of them:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; $ rake test:units
&lt;span class="no"&gt; 2&lt;/span&gt; (in /Users/francois/Projects/project)
&lt;span class="no"&gt; 3&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test &amp;quot;/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb&amp;quot; &amp;quot;test/unit/account_activation_mailer_test.rb&amp;quot; &amp;quot;test/unit/company_test.rb&amp;quot; &amp;quot;test/unit/event_test.rb&amp;quot; &amp;quot;test/unit/fanship_test.rb&amp;quot; &amp;quot;test/unit/internal_message_test.rb&amp;quot; &amp;quot;test/unit/job_application_test.rb&amp;quot; &amp;quot;test/unit/job_creation_presenter_test.rb&amp;quot; &amp;quot;test/unit/job_suggestion_test.rb&amp;quot; &amp;quot;test/unit/job_test.rb&amp;quot; &amp;quot;test/unit/media_test.rb&amp;quot; &amp;quot;test/unit/message_mailer_test.rb&amp;quot; &amp;quot;test/unit/message_test.rb&amp;quot; &amp;quot;test/unit/password_reset_mailer_test.rb&amp;quot; &amp;quot;test/unit/person_test.rb&amp;quot; &amp;quot;test/unit/profile_url_test.rb&amp;quot; &amp;quot;test/unit/recommendation_mailer_test.rb&amp;quot; &amp;quot;test/unit/recommendation_presenter_test.rb&amp;quot; &amp;quot;test/unit/recommendation_test.rb&amp;quot; &amp;quot;test/unit/relationship_test.rb&amp;quot; &amp;quot;test/unit/timeline_event_test.rb&amp;quot; 
&lt;span class="no"&gt; 4&lt;/span&gt; Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; Started
&lt;span class="no"&gt; 6&lt;/span&gt; ............FFF.........EEEEEEEEEEEEE........................FFFFFFFFFFFFFFFFFFFF.F....F.....EF..EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.................................................................................................................................................................................................................................................................................................................................................................................................
&lt;span class="no"&gt; 7&lt;/span&gt; Finished in 45.235105 seconds.
&lt;span class="no"&gt; 8&lt;/span&gt; 
&lt;span class="no"&gt; 9&lt;/span&gt; 542 tests, 762 assertions, 26 failures, 73 errors

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Bad, I&amp;#8217;m telling you.  Then I remembered something about &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-checkout.html"&gt;git checkout&lt;/a&gt; being able to checkout certain paths.  I read about this somewhere but I can&amp;#8217;t find the source, anymore.  Sorry!  Without even reading the manual, I set out to work.&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; $ get checkout -b fix
&lt;span class="no"&gt;2&lt;/span&gt; $ git rm -r vendor/plugins
&lt;span class="no"&gt;3&lt;/span&gt; $ git checkout -m &amp;quot;Removing submodule'd vendor/plugins (how did that happen?)&amp;quot;
&lt;span class="no"&gt;4&lt;/span&gt; $ git checkout 04f7e1^ -- vendor/plugins

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Wall clock time?  10 seconds.  I was ready to add and commit.&lt;/p&gt;
&lt;p&gt;This is a post to remind you to know your tools, and know them well.  Sorry I can&amp;#8217;t stay anymore.  I&amp;#8217;m off to read &lt;a href="http://www.zenspider.com/ZSS/Products/ZenTest/"&gt;autotest&lt;/a&gt;&amp;#8217;s source and documentation, again.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/brZqRupveJk" height="1" width="1"/&gt;</content>
    
    <category term="git" label="Git" />
      </entry>
  <entry>
    <title>Announcing XLsuite Open Source</title>
    <link href="http://blog.teksol.info/2009/02/18/announcing-xlsuite-open-source.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-02-18:20090218022934</id>
    <updated>2009-02-18T02:29:34-05:00</updated>
    <content type="html">&lt;p&gt;Would you believe we just GPL&amp;#8217;d nearly 40,000 lines of code?  &lt;a href="http://xlsuite.com/blogs/francois/2009/2/18/announcing-xlsuite-open-source/3311"&gt;Yes we did&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/-ntHDBhARTE" height="1" width="1"/&gt;</content>
    
    <category term="opensource" label="Opensource" />
      </entry>
  <entry>
    <title>Nonexistent When Using Phusion Passenger</title>
    <link href="http://blog.teksol.info/2009/02/18/nonexistent-when-using-phusion-passenger.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-02-18:20090218021159</id>
    <updated>2009-02-18T02:11:59-05:00</updated>
    <content type="html">&lt;p&gt;This evening, I was fooling around with mod_passenger for XLsuite.  I ran into a little problem where Passenger was telling me &amp;#8220;No such file or directory &amp;#8211; /nonexistent&amp;#8221;.  A quick search on Google revealed &lt;a href="http://blog.scopeport.org/ruby-on-rails/phusion-passenger-error-file-directory-nonexistent/"&gt;Phusion Passenger error “No such file or directory &amp;#8211; /nonexistent”&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That post really made my day.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/K1HGgdKzKhM" height="1" width="1"/&gt;</content>
    
    <category term="tips" label="Tips" />
      </entry>
  <entry>
    <title>GiraffeSoft FLOSS Week: Begins Today!</title>
    <link href="http://blog.teksol.info/2009/02/16/giraffesoft-floss-week-begins-today.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-02-16:20090216163303</id>
    <updated>2009-02-16T16:33:03-05:00</updated>
    <content type="html">&lt;p&gt;&lt;a href="http://jamesgolick.com/"&gt;James Golick&lt;/a&gt; just opened our company blog by adding &lt;a href="http://giraffesoft.com/blog/2009/02/16/floss-week.html"&gt;FLOSS week&lt;/a&gt;.  Over the next few days we&amp;#8217;ll release a couple of plugins that we&amp;#8217;ve been working on/with for the past few projects.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Only problem is, we never get around to releasing this stuff. Starting a blog seemed like a good excuse to spend some time polishing up some code and, you know, writing READMEs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Go on over and enjoy the view!  Don&amp;#8217;t forget the RSS feed:  &lt;a href="http://feeds.giraffesoft.ca/giraffesoft-the-blog"&gt;GiraffeSoft: The Blog&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/y2iROeOnCoc" height="1" width="1"/&gt;</content>
    
    <category term="links" label="Links" />
      </entry>
  <entry>
    <title>ib_outlet Doesn't Create Accessors</title>
    <link href="http://blog.teksol.info/2009/02/16/ib-outlet-doesnt-create-accessors.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-02-16:20090216083456</id>
    <updated>2009-02-16T08:34:56-05:00</updated>
    <content type="html">&lt;p&gt;Lately, I&amp;#8217;ve been having fun with RubyCocoa while reading &lt;a href="http://www.exampler.com/blog/"&gt;Brian Marick&lt;/a&gt;&amp;#8216;s &lt;a href="http://pragprog.com/titles/bmrc/rubycocoa"&gt;RubyCocoa&lt;/a&gt;.  It&amp;#8217;s an entertaining book, and I really like RubyCocoa itself.  But I hit a little gotcha.  Take the following code:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;PathController.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;osx/cocoa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt; 
&lt;span class="no"&gt; 3&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;PathController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;OSX&lt;/span&gt;::&lt;span class="co"&gt;NSObject&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;   ib_outlet &lt;span class="sy"&gt;:path&lt;/span&gt;, &lt;span class="sy"&gt;:events&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;   ib_action &lt;span class="sy"&gt;:choosePath&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;   
&lt;span class="no"&gt; 7&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;choosePath&lt;/span&gt;(sender)
&lt;span class="no"&gt; 8&lt;/span&gt;     panel = &lt;span class="co"&gt;OSX&lt;/span&gt;::&lt;span class="co"&gt;NSOpenPanel&lt;/span&gt;.openPanel
&lt;span class="no"&gt; 9&lt;/span&gt;     panel.canChooseFiles          = &lt;span class="pc"&gt;false&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;     panel.canChooseDirectories    = &lt;span class="pc"&gt;true&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt;     panel.allowsMultipleSelection = &lt;span class="pc"&gt;false&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt; 
&lt;span class="no"&gt;13&lt;/span&gt;     result = panel.runModal
&lt;span class="no"&gt;14&lt;/span&gt;     &lt;span class="r"&gt;if&lt;/span&gt; result == &lt;span class="co"&gt;OSX&lt;/span&gt;::&lt;span class="co"&gt;NSFileHandlingPanelOKButton&lt;/span&gt; &lt;span class="r"&gt;then&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt;       &lt;span class="pc"&gt;self&lt;/span&gt;.path.title = panel.filename
&lt;span class="no"&gt;16&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;17&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;18&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;What could be wrong with this?  Turns out &lt;code class="method"&gt;#ib_oulet&lt;/code&gt; does not define accessors for the instance variables it defines.  Look at the exception:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; OSX::OCMessageSendException: Can't get Objective-C method signature for selector 'events' of receiver #&amp;lt;PathController:0x2297be class='PathController' id=0x534ec0&amp;gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/oc_wrapper.rb:50:in `ocm_send'
&lt;span class="no"&gt; 3&lt;/span&gt;   /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/oc_wrapper.rb:50:in `method_missing'
&lt;span class="no"&gt; 4&lt;/span&gt;   /Users/francois/Projects/fsevtest/build/Debug/fsevtest.app/Contents/Resources/PathController.rb:27:in `choosePath'
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;   /Users/francois/Projects/fsevtest/build/Debug/fsevtest.app/Contents/Resources/rb_main.rb:22:in `NSApplicationMain'
&lt;span class="no"&gt; 6&lt;/span&gt;   /Users/francois/Projects/fsevtest/build/Debug/fsevtest.app/Contents/Resources/rb_main.rb:22
&lt;span class="no"&gt; 7&lt;/span&gt; /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/oc_wrapper.rb:50:in `ocm_send': Can't get Objective-C method signature for selector 'events' of receiver #&amp;lt;PathController:0x2297be class='PathController' id=0x534ec0&amp;gt; (OSX::OCMessageSendException)
&lt;span class="no"&gt; 8&lt;/span&gt;   from /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/oc_wrapper.rb:50:in `method_missing'
&lt;span class="no"&gt; 9&lt;/span&gt;   from /Users/francois/Projects/fsevtest/build/Debug/fsevtest.app/Contents/Resources/PathController.rb:27:in `choosePath'
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   from /Users/francois/Projects/fsevtest/build/Debug/fsevtest.app/Contents/Resources/rb_main.rb:22:in `NSApplicationMain'
&lt;span class="no"&gt;11&lt;/span&gt;   from /Users/francois/Projects/fsevtest/build/Debug/fsevtest.app/Contents/Resources/rb_main.rb:22

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;From the obscure message, I gathered this was some kind of &lt;a href="http://www.ruby-doc.org/core/classes/NoMethodError.html"&gt;NoMethodError&lt;/a&gt; (why couldn&amp;#8217;t they use an exception with a good name?)  That was Sunday morning.  Sunday evening, I suddenly had a flash that ib_outlet wouldn&amp;#8217;t define an accessor.  Changed my code to use the instance variable instead:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;PathController.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;if&lt;/span&gt; result == &lt;span class="co"&gt;OSX&lt;/span&gt;::&lt;span class="co"&gt;NSFileHandlingPanelOKButton&lt;/span&gt; &lt;span class="r"&gt;then&lt;/span&gt;
&lt;span class="no"&gt;2&lt;/span&gt;   &lt;span class="iv"&gt;@path&lt;/span&gt;.title = panel.filename
&lt;span class="no"&gt;3&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Lo and behold!  It worked beautifully.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/HNEpUV7N9fg" height="1" width="1"/&gt;</content>
    
    <category term="rubycocoa" label="Rubycocoa" />
      </entry>
  <entry>
    <title>Productivity Tip: Read Your Blogs in the Afternoon</title>
    <link href="http://blog.teksol.info/2009/02/16/productivity-tip-read-your-blogs-in-the-afternoon.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-02-16:20090216081858</id>
    <updated>2009-02-16T08:18:58-05:00</updated>
    <content type="html">&lt;p&gt;If you&amp;#8217;re in any way, shape or form like me, you usually have more energy in the morning (if you don&amp;#8217;t, skip this post).  My regular schedule puts me in my office around 9 AM every weekday.  First thing I do is pop open &lt;a href="http://google.com/reader"&gt;Google Reader&lt;/a&gt;, and start reading blogs.  What an utter waste of time&amp;#8230;  I waste perfectly good coding time to do an activity that&amp;#8217;s similar to watching television.  I can &lt;em&gt;waste&lt;/em&gt; my time in the afternoon, when I have less energy.  I say &lt;em&gt;waste&lt;/em&gt; but I really mean &lt;strong&gt;relax&lt;/strong&gt;.  Don&amp;#8217;t take me wrong: reading blogs is a very important time, but I should just do it when it&amp;#8217;s OK to do, not when I have the most energy.&lt;/p&gt;
&lt;p&gt;Yesterday evening, I was pondering on the why of the above.  Turns out I can procrastinate just as well as anyone else&amp;#8230;&lt;/p&gt;
&lt;p style="text-align:center"&gt;&lt;a href="http://media.photobucket.com/image/procrastinate/darkjustice2600/procrastinate.jpg"&gt;&lt;img src="http://i63.photobucket.com/albums/h133/darkjustice2600/procrastinate.jpg" width="400" height="320" alt="Procrastination is like masturbation&amp;hellip; it's good in the beginning, but in the end, you realize you've just fucked yourself"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/xsT2XeV7FOY" height="1" width="1"/&gt;</content>
    
    <category term="productivity" label="Productivity" />
      </entry>
  <entry>
    <title>How to Paste Without Indenting in Vim</title>
    <link href="http://blog.teksol.info/2009/02/11/how-to-paste-without-indenting-in-vim.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-02-11:20090211084032</id>
    <updated>2009-02-11T08:40:32-05:00</updated>
    <content type="html">&lt;p&gt;I wanted to paste some HTML into Vim, and I was having a hard time getting something that preserved existing indentation.&lt;/p&gt;
&lt;p&gt;After doing &lt;tt&gt;:help autoindent&lt;/tt&gt;, I found out about smartindent.  So, I turned both smartindent and autoindent off before doing my paste.  That worked wonders!&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; :set nosmartindent
&lt;span class="no"&gt;2&lt;/span&gt; :set noautoindent
&lt;span class="no"&gt;3&lt;/span&gt; i
&lt;span class="no"&gt;4&lt;/span&gt; &amp;lt;CMD&amp;gt;-V
&lt;span class="no"&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; &amp;lt;ESC&amp;gt;
&lt;span class="no"&gt;6&lt;/span&gt; :set smartindent
&lt;span class="no"&gt;7&lt;/span&gt; :set autoindent

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Voilà, a correctly pasted, non-indented version of whatever was in the clipboard!&lt;/p&gt;
&lt;p&gt;UPDATE: 2 minutes after I &lt;a href="http://twitter.com/fbeausoleil/status/1198970122"&gt;posted&lt;/a&gt; the link to this article on Twitter, &lt;a href="http://twitter.com/jpalardy"&gt;@jpalardy&lt;/a&gt; replied:&lt;/p&gt;
&lt;p&gt;&lt;iframe width="500" height="200" frameborder="0" src="http://tweetpaste.net/script/?t=1198983486" style="overflow: hidden; display: block; width: 500px; height: 200px;"&gt;&lt;p&gt;&lt;a href="http://twitter.com/jpalardy/statuses/1198983486" target="_blank"&gt;View jpalardy&amp;rsquo;s tweet&lt;/p&gt;&lt;/iframe&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/qC8GL1SlRFo" height="1" width="1"/&gt;</content>
    
    <category term="tips" label="Tips" />
    
    <category term="vim" label="Vim" />
      </entry>
  <entry>
    <title>Setting Up Passenger on Mac OS X Using Passenger Prefs Pane</title>
    <link href="http://blog.teksol.info/2009/02/09/setting-up-passenger-on-mac-os-x-using-passenger-prefs-pane.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-02-09:20090209111301</id>
    <updated>2009-02-09T11:13:01-05:00</updated>
    <content type="html">&lt;p&gt;Before today, I was using Thin to host my development servers.  This was a bother because I had to remember to start/stop Thins as required when I switched tasks.  Well, not anymore&amp;#8230;&lt;/p&gt;
&lt;p&gt;Reading my feeds today, I found &lt;a href="http://www.fngtps.com/passenger-preference-pane"&gt;Passenger Preference Pane&lt;/a&gt;, by &lt;a href="http://www.fngtps.com/"&gt;Fingertips&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download the preference pane itself&lt;/li&gt;
&lt;li&gt;Untar&lt;/li&gt;
&lt;li&gt;Follow the directives in the README about RubyCocoa (either Leopard 10.5.2 or install RubyCocoa yourself from source)&lt;/li&gt;
&lt;li&gt;Double click the prefpane: this will install the preference pane&lt;/li&gt;
&lt;li&gt;Open System Preferences, then Passenger in Other&lt;/li&gt;
&lt;li&gt;Drag and drop your Rails (or Rack-based) application folder into the list&lt;/li&gt;
&lt;li&gt;Click Apply&lt;/li&gt;
&lt;li&gt;Go back to System Preferences, and open Sharing&lt;/li&gt;
&lt;li&gt;Stop and start Apache using the checkbox for Web Sharing&lt;/li&gt;
&lt;li&gt;Visit http://your-apps-base-name.local/ and enjoy!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One caveat I found was that my home directory had to change permissions.  I had to grant &lt;tt&gt;a+rx&lt;/tt&gt; before it would work:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt;  $ ls -ld ~
&lt;span class="no"&gt;2&lt;/span&gt; drwxr-xr-x+ 78 francois  staff  2652  9 fév 11:13 /Users/francois

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Of course, if you have a multi-user machine, that opens an interesting can of worms for security.  A quick fix would be:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; $ chmod a-rx * &amp;amp;&amp;amp; chmod a+rx the-apps-base-name

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Modify  as needed.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/on7tuiJktuM" height="1" width="1"/&gt;</content>
    
    <category term="passenger" label="Passenger" />
      </entry>
  <entry>
    <title>Another Productivity Tip: Use iTerm and Vim</title>
    <link href="http://blog.teksol.info/2009/02/05/another-productivity-tip-use-iterm-and-vim.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-02-05:20090205112941</id>
    <updated>2009-02-05T11:29:41-05:00</updated>
    <content type="html">&lt;p&gt;I was pairing with &lt;a href="http://jamesgolick.com/"&gt;James Golick&lt;/a&gt; the other day, and he told me he was using &lt;a href="http://iterm.sourceforge.net/"&gt;iTerm&lt;/a&gt; in fullscreen mode.  I was pretty excited by that, and I immediately downloaded and installed it.&lt;/p&gt;
&lt;p&gt;I was already a &lt;a href="http://www.vim.org/"&gt;Vim&lt;/a&gt; user, through the excellent &lt;a href="http://code.google.com/p/macvim/"&gt;MacVim&lt;/a&gt;, but I started using Vim from the console instead.  My main screen is an &lt;a href="http://global.acer.com/products/monitor/X_series.htm"&gt;Acer X243w&lt;/a&gt;, and with Vim in full screen, I have more than enough space to do all my coding.  Splitting vertically is now a real pleasure, and I can even have a 4 way split that&amp;#8217;s not too claustrophobic.&lt;/p&gt;
&lt;p&gt;The other benefit of using iTerm instead of the default &lt;a href="http://www.apple.com/macosx/"&gt;Mac OS X&lt;/a&gt; Terminal is that iTerm can be used in 256 colors, enabling Vim to do even more syntax highlighting.  That wasn&amp;#8217;t a problem in MacVim though.&lt;/p&gt;
&lt;p&gt;Only caveat?  Scrolling is a bit slow.  Me and James both wonder about this&amp;#8230;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/syHZHRgcCw4" height="1" width="1"/&gt;</content>
    
    <category term="productivity" label="Productivity" />
      </entry>
  <entry>
    <title>Duplicate Bug #5404556: Git on Snow Leopard</title>
    <link href="http://blog.teksol.info/2009/02/04/duplicate-bug-number-5404556-git-on-snow-leopard.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-02-04:20090204203202</id>
    <updated>2009-02-04T20:32:02-05:00</updated>
    <content type="html">&lt;p&gt;Just received an email from Apple today:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;From: devbugs@apple.com&lt;br/&gt;Subject: Re: Bug ID 6543251: Git is not available by default on Leopard&lt;/p&gt;
&lt;p&gt;Hello François,&lt;/p&gt;
&lt;p&gt;This is a follow up to Bug ID# 6543251.  After further investigation it has been determined that this is a known issue, which is currently being investigated by engineering.  This issue has been filed in our bug database under the original Bug ID# 5404556. The original bug number being used to track this duplicate issue can be found in the State column, in this format:  Duplicate/OrigBug#.&lt;/p&gt;
&lt;p&gt;Thank you for submitting this bug report. We truly appreciate your assistance in helping us discover and isolate bugs.&lt;/p&gt;
&lt;p&gt;Best Regards,&lt;/p&gt;
&lt;p&gt;Kit Cheung&lt;br/&gt;Apple Developer Connection&lt;br/&gt;Worldwide Developer Relations&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a followup to &lt;a href="http://github.com/blog/327-want-git-preinstalled-on-next-mac-os-x"&gt;Want Git preinstalled on next Mac OS X?&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/iaI7jyhazsI" height="1" width="1"/&gt;</content>
    
    <category term="scm" label="Scm" />
    
    <category term="git" label="Git" />
    
    <category term="apple" label="Apple" />
      </entry>
  <entry>
    <title>Beware of the Habit Creature</title>
    <link href="http://blog.teksol.info/2009/01/23/beware-of-the-habit-creature.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-01-23:20090123101203</id>
    <updated>2009-01-23T10:12:03-05:00</updated>
    <content type="html">&lt;p&gt;You know, I write controller tests day in, day out.  I&amp;#8217;m used to seeing this:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; should &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;be valid&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;2&lt;/span&gt;   assert assigns(&lt;span class="sy"&gt;:user&lt;/span&gt;).valid?
&lt;span class="no"&gt;3&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;So, when I write a Rake task and I render some data, I need my assigns:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;lib/tasks/ssl.rake&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; namespace &lt;span class="sy"&gt;:ssl&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   task &lt;span class="sy"&gt;:build&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:environment&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     rm_rf &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;config/sites/&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;     mkdir &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;config/sites&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt; 6&lt;/span&gt;     &lt;span class="co"&gt;Dir&lt;/span&gt;[&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;config/ssl/*&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;].each &lt;span class="r"&gt;do&lt;/span&gt; |path|
&lt;span class="no"&gt; 7&lt;/span&gt;       site = &lt;span class="co"&gt;File&lt;/span&gt;.basename(path)
&lt;span class="no"&gt; 8&lt;/span&gt;       parts = site.split(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;span class="no"&gt; 9&lt;/span&gt; 
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;       puts &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Processing &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;site&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt;       assigns = {
&lt;span class="no"&gt;12&lt;/span&gt;         &lt;span class="sy"&gt;:wildcard_domain&lt;/span&gt;           =&amp;gt; parts[&lt;span class="i"&gt;0&lt;/span&gt;] == &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
&lt;span class="no"&gt;13&lt;/span&gt;         &lt;span class="sy"&gt;:domain_name&lt;/span&gt;               =&amp;gt; parts[&lt;span class="i"&gt;-2&lt;/span&gt;..&lt;span class="i"&gt;-1&lt;/span&gt;].join(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;),
&lt;span class="no"&gt;14&lt;/span&gt;         &lt;span class="sy"&gt;:ssl_key_file_path&lt;/span&gt;         =&amp;gt; &lt;span class="co"&gt;File&lt;/span&gt;.expand_path(&lt;span class="co"&gt;File&lt;/span&gt;.join(&lt;span class="co"&gt;Dir&lt;/span&gt;.pwd, path, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;site&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;.key&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)),
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt;         &lt;span class="sy"&gt;:ssl_certificate_file_path&lt;/span&gt; =&amp;gt; &lt;span class="co"&gt;File&lt;/span&gt;.expand_path(&lt;span class="co"&gt;File&lt;/span&gt;.join(&lt;span class="co"&gt;Dir&lt;/span&gt;.pwd, path, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;site&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;.crt&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;))}
&lt;span class="no"&gt;16&lt;/span&gt; 
&lt;span class="no"&gt;17&lt;/span&gt;       &lt;span class="r"&gt;next&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Skipping &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;site&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;: &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;assigns(&lt;span class="sy"&gt;:ssl_key_file_path&lt;/span&gt;)&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; does not exist or is unreadable&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;unless&lt;/span&gt; &lt;span class="co"&gt;File&lt;/span&gt;.file?(assigns(&lt;span class="sy"&gt;:ssl_key_file_path&lt;/span&gt;))
&lt;span class="no"&gt;18&lt;/span&gt;       &lt;span class="r"&gt;next&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Skipping &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;site&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;: &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;assigns(&lt;span class="sy"&gt;:ssl_certificate_file_path&lt;/span&gt;)&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; does not exist or is unreadable&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;unless&lt;/span&gt; &lt;span class="co"&gt;File&lt;/span&gt;.file?(assigns(&lt;span class="sy"&gt;:ssl_certificate_file_path&lt;/span&gt;))
&lt;span class="no"&gt;19&lt;/span&gt; 
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt;       view = &lt;span class="co"&gt;ActionView&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.new([&lt;span class="co"&gt;File&lt;/span&gt;.dirname(&lt;span class="pc"&gt;__FILE__&lt;/span&gt;)], assigns)
&lt;span class="no"&gt;21&lt;/span&gt; 
&lt;span class="no"&gt;22&lt;/span&gt;       &lt;span class="co"&gt;File&lt;/span&gt;.open(&lt;span class="co"&gt;File&lt;/span&gt;.join(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;sites&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;site&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;.ssl.conf&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;), &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;w&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class="r"&gt;do&lt;/span&gt; |site_config|
&lt;span class="no"&gt;23&lt;/span&gt;         puts &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Writing config/sites/&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;site&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;.ssl.conf&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;24&lt;/span&gt;         site_config.write view.render(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;ssl.conf.erb&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;span class="no"&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;26&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;27&lt;/span&gt; 
&lt;span class="no"&gt;28&lt;/span&gt;     view = &lt;span class="co"&gt;ActionView&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.new([&lt;span class="co"&gt;File&lt;/span&gt;.dirname(&lt;span class="pc"&gt;__FILE__&lt;/span&gt;)], {})
&lt;span class="no"&gt;29&lt;/span&gt;     &lt;span class="co"&gt;File&lt;/span&gt;.open(&lt;span class="co"&gt;File&lt;/span&gt;.join(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;sites&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;default.conf&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;), &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;w&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class="r"&gt;do&lt;/span&gt; |site_config|
&lt;span class="no"&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;/span&gt;       site_config.write view.render(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;default.conf.erb&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;span class="no"&gt;31&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;32&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;33&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Can you spot the error on lines 17 and 18?  Go on, I&amp;#8217;ll wait&amp;#8230;&lt;/p&gt;
&lt;p&gt;You want some help?  Here&amp;#8217;s the backtrace:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; ** Invoke ssl:build (first_time)
&lt;span class="no"&gt; 2&lt;/span&gt; ** Invoke environment (first_time)
&lt;span class="no"&gt; 3&lt;/span&gt; ** Execute environment
&lt;span class="no"&gt; 4&lt;/span&gt; ** Execute ssl:build
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; rm -rf config/sites/
&lt;span class="no"&gt; 6&lt;/span&gt; mkdir config/sites
&lt;span class="no"&gt; 7&lt;/span&gt; Processing _.xlsuite.com
&lt;span class="no"&gt; 8&lt;/span&gt; rake aborted!
&lt;span class="no"&gt; 9&lt;/span&gt; undefined method `assigns' for #&amp;lt;Object:0x389a0&amp;gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; /Users/francois/Documents/work/xlsuite-stable.svn/lib/tasks/ssl.rake:17

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Yes, took me a while too.  I was calling a method named assigns, not using the Hash named assigns.&lt;/p&gt;
&lt;p&gt;Habit, when you take hold of me&amp;#8230;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/qDwMkWkex6A" height="1" width="1"/&gt;</content>
    
    <category term="testing" label="Testing" />
      </entry>
  <entry>
    <title>Using Cucumber for Writing Stories for Command-Line Applications</title>
    <link href="http://blog.teksol.info/2009/01/18/using-cucumber-for-writing-stories-for-command-line-applications.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-01-18:20090118131956</id>
    <updated>2009-01-18T13:19:56-05:00</updated>
    <content type="html">&lt;p&gt;I just started using &lt;a href="http://cukes.info/"&gt;Cucumber&lt;/a&gt; to write acceptance tests for a new command-line application I&amp;#8217;m writing.  I used the wonderful &lt;a href="http://newgem.rubyforge.org/"&gt;newgem&lt;/a&gt; to generate the basics for me.&lt;/p&gt;
&lt;p&gt;Then I wrote my first stories:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;features/command_line_client.feature&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; Feature: Command Line Client
&lt;span class="no"&gt; 2&lt;/span&gt;   In order to use Nestor
&lt;span class="no"&gt; 3&lt;/span&gt;   A developer
&lt;span class="no"&gt; 4&lt;/span&gt;   Wants an interface to the program
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;   So that he can use it
&lt;span class="no"&gt; 6&lt;/span&gt; 
&lt;span class="no"&gt; 7&lt;/span&gt;   Scenario: Getting Help
&lt;span class="no"&gt; 8&lt;/span&gt;     When I start nestor with &amp;quot;--help&amp;quot;
&lt;span class="no"&gt; 9&lt;/span&gt;     Then I should see &amp;quot;Usage: nestor [options]&amp;quot;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt;11&lt;/span&gt;   Scenario: Getting the version number
&lt;span class="no"&gt;12&lt;/span&gt;     When I start nestor with &amp;quot;--version&amp;quot;
&lt;span class="no"&gt;13&lt;/span&gt;     Then I should find &amp;quot;nestor \d+\.\d+\.\d+&amp;quot;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;After writing those, I ran them:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; ~/nestor (master) $ rake features
&lt;span class="no"&gt; 2&lt;/span&gt; (in /Users/francois/nestor)
&lt;span class="no"&gt; 3&lt;/span&gt; ...F
&lt;span class="no"&gt; 4&lt;/span&gt; 
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt; 6&lt;/span&gt; Failed:
&lt;span class="no"&gt; 7&lt;/span&gt; 
&lt;span class="no"&gt; 8&lt;/span&gt; 1)
&lt;span class="no"&gt; 9&lt;/span&gt; &amp;lt;&amp;quot;nestor: version unknown\n&amp;quot;&amp;gt; expected to be =~
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; &amp;lt;/nestor \d+\.\d+\.\d+/&amp;gt;.
&lt;span class="no"&gt;11&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/assertions.rb:48:in `assert_block'
&lt;span class="no"&gt;12&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/assertions.rb:500:in `_wrap_assertion'
&lt;span class="no"&gt;13&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/assertions.rb:46:in `assert_block'
&lt;span class="no"&gt;14&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/assertions.rb:229:in `assert_match'
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/assertions.rb:495:in `_wrap_assertion'
&lt;span class="no"&gt;16&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/assertions.rb:221:in `assert_match'
&lt;span class="no"&gt;17&lt;/span&gt; ./features/step_definitions/assertions.rb:8:in `Then /^I should find &amp;quot;(.*)&amp;quot;$/'
&lt;span class="no"&gt;18&lt;/span&gt; features/command_line_client.feature:13:in `Then I should find &amp;quot;nestor \d+\.\d+\.\d+&amp;quot;'
&lt;span class="no"&gt;19&lt;/span&gt; rake aborted!
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt; Command failed with status (1): [/System/Library/Frameworks/Ruby.framework/...]
&lt;span class="no"&gt;21&lt;/span&gt; 
&lt;span class="no"&gt;22&lt;/span&gt; (See full trace by running task with --trace)

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Haha!  So, Cucumber caught the fact that newgem didn&amp;#8217;t set the new gem to return the version number immediately! Interesting.&lt;/p&gt;
&lt;p&gt;Anyway, here are my step definitions, for the curious:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;features/support/env.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;test/unit&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;2&lt;/span&gt; 
&lt;span class="no"&gt;3&lt;/span&gt; &lt;span class="co"&gt;World&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt;4&lt;/span&gt;   include &lt;span class="co"&gt;Test&lt;/span&gt;::&lt;span class="co"&gt;Unit&lt;/span&gt;::&lt;span class="co"&gt;Assertions&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="code"&gt;
&lt;h5&gt;features/step_definitions/nestor.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="co"&gt;When&lt;/span&gt; &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;^I start nestor with &amp;quot;(.*)&amp;quot;$&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |args|
&lt;span class="no"&gt;2&lt;/span&gt;   &lt;span class="sh"&gt;&lt;span class="dl"&gt;%x&amp;quot;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;&lt;span class="co"&gt;NESTOR_APP_ROOT&lt;/span&gt;&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;/bin/nestor &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;args&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; &amp;gt; &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;&lt;span class="co"&gt;NESTOR_LOG_ROOT&lt;/span&gt;&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;/nestor.log 2&amp;gt;&amp;amp;1&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;3&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="code"&gt;
&lt;h5&gt;features/step_definitions/assertions.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="co"&gt;Then&lt;/span&gt; &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;^I should see &amp;quot;(.*)&amp;quot;$&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |data|
&lt;span class="no"&gt; 2&lt;/span&gt;   stdout = &lt;span class="co"&gt;File&lt;/span&gt;.read(&lt;span class="co"&gt;File&lt;/span&gt;.join(&lt;span class="co"&gt;NESTOR_LOG_ROOT&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;nestor.log&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;))
&lt;span class="no"&gt; 3&lt;/span&gt;   assert_match &lt;span class="co"&gt;Regexp&lt;/span&gt;.new(&lt;span class="co"&gt;Regexp&lt;/span&gt;.escape(data)), stdout
&lt;span class="no"&gt; 4&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt; 6&lt;/span&gt; &lt;span class="co"&gt;Then&lt;/span&gt; &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;^I should find &amp;quot;(.*)&amp;quot;$&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |regexp|
&lt;span class="no"&gt; 7&lt;/span&gt;   stdout = &lt;span class="co"&gt;File&lt;/span&gt;.read(&lt;span class="co"&gt;File&lt;/span&gt;.join(&lt;span class="co"&gt;NESTOR_LOG_ROOT&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;nestor.log&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;))
&lt;span class="no"&gt; 8&lt;/span&gt;   assert_match &lt;span class="co"&gt;Regexp&lt;/span&gt;.new(regexp), stdout
&lt;span class="no"&gt; 9&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Notice that I really run the nestor binary to assert against the gem&amp;#8217;s STDOUT.  Redirecting the output to a log file I can open and search in really makes things easier.  I had a bit of difficulty wrapping my head around this idea (of starting the binary from Cucumber), so I hope that helps other people too.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/GiETrAefekY" height="1" width="1"/&gt;</content>
      </entry>
  <entry>
    <title>I Seem to Have Broken My RubyGems?</title>
    <link href="http://blog.teksol.info/2009/01/16/i-seem-to-have-broken-my-rubygems.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-01-16:20090116164305</id>
    <updated>2009-01-16T16:43:05-05:00</updated>
    <content type="html">&lt;p&gt;I really don&amp;#8217;t know how I did this.  I&amp;#8217;d like to say it&amp;#8217;s the &lt;a href="http://www.imdb.com/title/tt0087363/"&gt;Gremlins&lt;/a&gt; in the machine, but it must be some kind of human error.&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; ~ $ gem --version
&lt;span class="no"&gt; 2&lt;/span&gt; 1.2.0
&lt;span class="no"&gt; 3&lt;/span&gt; ~ $ gem env
&lt;span class="no"&gt; 4&lt;/span&gt; RubyGems Environment:
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;   - RUBYGEMS VERSION: 1.2.0
&lt;span class="no"&gt; 6&lt;/span&gt;   - RUBY VERSION: 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]
&lt;span class="no"&gt; 7&lt;/span&gt;   - INSTALLATION DIRECTORY: /Library/Ruby/Gems/1.8
&lt;span class="no"&gt; 8&lt;/span&gt;   - RUBY EXECUTABLE: /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
&lt;span class="no"&gt; 9&lt;/span&gt;   - EXECUTABLE DIRECTORY: /usr/bin
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   - RUBYGEMS PLATFORMS:
&lt;span class="no"&gt;11&lt;/span&gt;     - ruby
&lt;span class="no"&gt;12&lt;/span&gt;     - universal-darwin-9
&lt;span class="no"&gt;13&lt;/span&gt;   - GEM PATHS:
&lt;span class="no"&gt;14&lt;/span&gt;      - /Library/Ruby/Gems/1.8
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt;      - /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8
&lt;span class="no"&gt;16&lt;/span&gt;   - GEM CONFIGURATION:
&lt;span class="no"&gt;17&lt;/span&gt;      - :update_sources =&amp;gt; true
&lt;span class="no"&gt;18&lt;/span&gt;      - :verbose =&amp;gt; true
&lt;span class="no"&gt;19&lt;/span&gt;      - :benchmark =&amp;gt; false
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt;      - :backtrace =&amp;gt; false
&lt;span class="no"&gt;21&lt;/span&gt;      - :bulk_threshold =&amp;gt; 1000
&lt;span class="no"&gt;22&lt;/span&gt;      - &amp;quot;install&amp;quot; =&amp;gt; &amp;quot;--env-shebang&amp;quot;
&lt;span class="no"&gt;23&lt;/span&gt;      - &amp;quot;update&amp;quot; =&amp;gt; &amp;quot;--env-shebang&amp;quot;
&lt;span class="no"&gt;24&lt;/span&gt;      - :sources =&amp;gt; [&amp;quot;http://gems.rubyforge.org&amp;quot;, &amp;quot;http://gems.github.com/&amp;quot;]
&lt;span class="no"&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;/span&gt;   - REMOTE SOURCES:
&lt;span class="no"&gt;26&lt;/span&gt;      - http://gems.rubyforge.org
&lt;span class="no"&gt;27&lt;/span&gt;      - http://gems.github.com/
&lt;span class="no"&gt;28&lt;/span&gt; ~ $ sudo gem update --system
&lt;span class="no"&gt;29&lt;/span&gt; Password:
&lt;span class="no"&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;/span&gt; Updating RubyGems
&lt;span class="no"&gt;31&lt;/span&gt; Nothing to update
&lt;span class="no"&gt;32&lt;/span&gt; ~ $ rails newapp
&lt;span class="no"&gt;33&lt;/span&gt;       create  
&lt;span class="no"&gt;34&lt;/span&gt;       create  app/controllers
&lt;span class="no"&gt;&lt;strong&gt;35&lt;/strong&gt;&lt;/span&gt;       ...
&lt;span class="no"&gt;36&lt;/span&gt;       create  log/test.log
&lt;span class="no"&gt;37&lt;/span&gt; ~ $ cd newapp/
&lt;span class="no"&gt;38&lt;/span&gt; ~/newapp $ script/generate model person
&lt;span class="no"&gt;39&lt;/span&gt; Rails requires RubyGems &amp;gt;= 1.3.1 (you have 1.2.0). Please `gem update --system` and try again.
&lt;span class="no"&gt;&lt;strong&gt;40&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt;41&lt;/span&gt; $ ls -d /Library/Ruby/Gems/1.8/gems/rubygems*
&lt;span class="no"&gt;42&lt;/span&gt; /Library/Ruby/Gems/1.8/gems/rubygems-update-1.2.0  /Library/Ruby/Gems/1.8/gems/rubygems-update-1.3.1
&lt;span class="no"&gt;43&lt;/span&gt; /Library/Ruby/Gems/1.8/gems/rubygems-update-1.3.0

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;What&amp;#8217;s going on?  Anybody has an idea?  A quick Google search didn&amp;#8217;t turn up anything interesting.  Probably I&amp;#8217;m not searching for the right thing.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/CS-FhUQLzPE" height="1" width="1"/&gt;</content>
    
    <category term="strange" label="Strange" />
      </entry>
  <entry>
    <title>Productivity Tip: A Simple Change to ~/.bash_profile</title>
    <link href="http://blog.teksol.info/2009/01/12/productivity-tip-a-simple-change-to.html" type="text/html" />
    <id>tag:blog.teksol.info,2009-01-12:20090112101612</id>
    <updated>2009-01-12T10:16:12-05:00</updated>
    <content type="html">&lt;p&gt;I put everything I code into &lt;code class="dir"&gt;~/Documents/work&lt;/code&gt;.  Could be called projects, same thing.  Anyway, everytime I opened Terminal, I would have to type &lt;code class="dir"&gt;cd Documents/work&lt;/code&gt;.  Of course, with tab completion, it&amp;#8217;s not so bad.  I&amp;#8217;m a bit stupid though, and it takes me eons before I added &lt;code class="dir"&gt;cd ~/Documents/work&lt;/code&gt; to the end of my &lt;code class="path"&gt;~/.bash_profile&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Whenever I open a Terminal, I&amp;#8217;m really going to start doing something in this folder.  Now, why didn&amp;#8217;t I think of that earlier?  Next: retrain myself to not cd anywhere when I open a new Terminal&amp;hellip;&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s my full &lt;code class="path"&gt;~/.bash_profile:&lt;/code&gt;&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;~/.bash_profile&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; #!/usr/bin/sh
&lt;span class="no"&gt; 2&lt;/span&gt; 
&lt;span class="no"&gt; 3&lt;/span&gt; # Installed by Git OSX Installer from
&lt;span class="no"&gt; 4&lt;/span&gt; # http://code.google.com/p/git-osx-installer/
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home
&lt;span class="no"&gt; 6&lt;/span&gt; export ANT_HOME=/usr/share/ant
&lt;span class="no"&gt; 7&lt;/span&gt; export JRUBY_HOME=~/Library/Java/JRuby/current
&lt;span class="no"&gt; 8&lt;/span&gt; export MYSQL_PATH=/usr/local/mysql
&lt;span class="no"&gt; 9&lt;/span&gt; export PATH=~/bin:/opt/local/sphinx/bin:/usr/local/git/bin:$MYSQL_PATH/bin:$PATH:$JRUBY_HOME/bin
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; export MANPATH=/usr/local/git/man:$MANPATH
&lt;span class="no"&gt;11&lt;/span&gt; export EDITOR=vim
&lt;span class="no"&gt;12&lt;/span&gt; 
&lt;span class="no"&gt;13&lt;/span&gt; # And this is from:
&lt;span class="no"&gt;14&lt;/span&gt; # http://code.google.com/p/git-osx-installer/
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt; export PS1='\w$(git branch &amp;amp;&amp;gt;/dev/null; if [ $? -eq 0 ]; then echo &amp;quot; (\[\033[00m\]$(git branch | grep ^*|sed s/\*\ //))&amp;quot;; fi) \$\[\033[00m\] '
&lt;span class="no"&gt;16&lt;/span&gt; 
&lt;span class="no"&gt;17&lt;/span&gt; export VISUALWORKS=~/Applications/vw7.6nc
&lt;span class="no"&gt;18&lt;/span&gt; export AWS_ACCESS_KEY_ID= # your AWS access key, for cliaws gem
&lt;span class="no"&gt;19&lt;/span&gt; export AWS_SECRET_ACCESS_KEY= # your AWS secret access key, for cliaws gem again
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt;21&lt;/span&gt; # http://www.macosxhints.com/article.php?story=20060502160527780&amp;amp;query=terminal%2Btitle
&lt;span class="no"&gt;22&lt;/span&gt; function settitle() { echo -ne &amp;quot;\e]2;$@\a\e]1;$@\a&amp;quot;; }
&lt;span class="no"&gt;23&lt;/span&gt; 
&lt;span class="no"&gt;24&lt;/span&gt; if [ -f ~/.bash_aliases ]; then
&lt;span class="no"&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;/span&gt;   source ~/.bash_aliases
&lt;span class="no"&gt;26&lt;/span&gt; fi
&lt;span class="no"&gt;27&lt;/span&gt; 
&lt;span class="no"&gt;28&lt;/span&gt; cd ~/Documents/work

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/PUQesjXKWnE" height="1" width="1"/&gt;</content>
    
    <category term="tips" label="Tips" />
      </entry>
  <entry>
    <title>MySQL's MEMORY Engine Barely Faster than InnoDB's?</title>
    <link href="http://blog.teksol.info/2008/12/12/mysqls-memory-engine-barely-faster-than-innodbs.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-12-12:20081212080423</id>
    <updated>2008-12-12T08:04:23-05:00</updated>
    <content type="html">&lt;p&gt;Yesterday, I read &lt;a href="http://cardarella.blogspot.com/2008/12/case-against-mocking-and-stubbing.html"&gt;Brian Cardarella&lt;/a&gt;&amp;#8216;s post entitled &lt;a href="http://cardarella.blogspot.com/"&gt;Brian Cardarella&lt;/a&gt;&amp;#8217;s post entitled &lt;a href="http://cardarella.blogspot.com/2008/12/case-against-mocking-and-stubbing.html"&gt;A case against Mocking and Stubbing&lt;/a&gt;.  In the article, Brian says:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;SQLite3 can be an in-memory database. Problem solved, right? Not quite. SQLite3 is pretty limited. Most people are probably using MySQL and rely upon many of the SQL functions that are included.&lt;/p&gt; 
&lt;p&gt; What would be nice (and well beyond my ability) is to have a Gem that simulated the database you use, only it is in-memory. Optimized for small data sets. No need to go through a heavy hashing algorithm. Keep it light. Keep it fast.&lt;/p&gt;
&lt;p class="signature"&gt;Brian Cardarella in &lt;a href="http://cardarella.blogspot.com/2008/12/case-against-mocking-and-stubbing.html"&gt;A case against Mocking and Stubbing&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;MySQL already has the &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/memory-storage-engine.html"&gt;MEMORY&lt;/a&gt; storage engine, and I wanted to see if that would help for testing purposes.  Since we&amp;#8217;re staying in MySQL-land, this should have been a simple matter.&lt;/p&gt;
&lt;p&gt;First, the good news.  I had to change only a couple of lines:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/vendor/rails/activerecor
&lt;span class="no"&gt; 2&lt;/span&gt; index 1e452ae..c207080 100644
&lt;span class="no"&gt; 3&lt;/span&gt; --- a/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
&lt;span class="no"&gt; 4&lt;/span&gt; +++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; @@ -442,7 +442,13 @@ module ActiveRecord
&lt;span class="no"&gt; 6&lt;/span&gt;        end
&lt;span class="no"&gt; 7&lt;/span&gt;  
&lt;span class="no"&gt; 8&lt;/span&gt;        def create_table(table_name, options = {}) #:nodoc:
&lt;span class="no"&gt; 9&lt;/span&gt; -        super(table_name, options.reverse_merge(:options =&amp;gt; &amp;quot;ENGINE=InnoDB&amp;quot;))
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; +        engine = case Rails.env
&lt;span class="no"&gt;11&lt;/span&gt; +        when &amp;quot;test&amp;quot;
&lt;span class="no"&gt;12&lt;/span&gt; +          &amp;quot;MEMORY&amp;quot;
&lt;span class="no"&gt;13&lt;/span&gt; +        else
&lt;span class="no"&gt;14&lt;/span&gt; +          &amp;quot;InnoDB&amp;quot;
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt; +        end
&lt;span class="no"&gt;16&lt;/span&gt; +        super(table_name, options.reverse_merge(:options =&amp;gt; &amp;quot;ENGINE=#{engine}&amp;quot;))
&lt;span class="no"&gt;17&lt;/span&gt;        end
&lt;span class="no"&gt;18&lt;/span&gt;  
&lt;span class="no"&gt;19&lt;/span&gt;        def rename_table(table_name, new_name)
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt; diff --git a/vendor/rails/railties/lib/tasks/databases.rake b/vendor/rails/railties/lib/tasks/databases.rake
&lt;span class="no"&gt;21&lt;/span&gt; index 5cb27f1..c520d4a 100644
&lt;span class="no"&gt;22&lt;/span&gt; --- a/vendor/rails/railties/lib/tasks/databases.rake
&lt;span class="no"&gt;23&lt;/span&gt; +++ b/vendor/rails/railties/lib/tasks/databases.rake
&lt;span class="no"&gt;24&lt;/span&gt; @@ -368,9 +368,9 @@ namespace :db do
&lt;span class="no"&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;/span&gt;  
&lt;span class="no"&gt;26&lt;/span&gt;      desc 'Check for pending migrations and load the test schema'
&lt;span class="no"&gt;27&lt;/span&gt;      task :prepare =&amp;gt; 'db:abort_if_pending_migrations' do
&lt;span class="no"&gt;28&lt;/span&gt; -      if defined?(ActiveRecord) &amp;amp;&amp;amp; !ActiveRecord::Base.configurations.blank?
&lt;span class="no"&gt;29&lt;/span&gt; -        Rake::Task[{ :sql  =&amp;gt; &amp;quot;db:test:clone_structure&amp;quot;, :ruby =&amp;gt; &amp;quot;db:test:load&amp;quot; }[ActiveRecord::Base.schema_format]].i
&lt;span class="no"&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;/span&gt; -      end
&lt;span class="no"&gt;31&lt;/span&gt; +      # if defined?(ActiveRecord) &amp;amp;&amp;amp; !ActiveRecord::Base.configurations.blank?
&lt;span class="no"&gt;32&lt;/span&gt; +      #   Rake::Task[{ :sql  =&amp;gt; &amp;quot;db:test:clone_structure&amp;quot;, :ruby =&amp;gt; &amp;quot;db:test:load&amp;quot; }[ActiveRecord::Base.schema_format]]
&lt;span class="no"&gt;33&lt;/span&gt; +      # end
&lt;span class="no"&gt;34&lt;/span&gt;      end
&lt;span class="no"&gt;&lt;strong&gt;35&lt;/strong&gt;&lt;/span&gt;    end
&lt;span class="no"&gt;36&lt;/span&gt;  

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Great, but there&amp;#8217;s little benefit.  First, a regular run (InnoDB):&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; $ time rake
&lt;span class="no"&gt; 2&lt;/span&gt; (in /Users/francois/Documents/work/fasttest)
&lt;span class="no"&gt; 3&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test &amp;quot;/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb&amp;quot; &amp;quot;test/unit/password_reset_mailer_test.rb&amp;quot; &amp;quot;test/unit/person_test.rb&amp;quot; 
&lt;span class="no"&gt; 4&lt;/span&gt; Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; Started
&lt;span class="no"&gt; 6&lt;/span&gt; ............................
&lt;span class="no"&gt; 7&lt;/span&gt; Finished in 0.520087 seconds.
&lt;span class="no"&gt; 8&lt;/span&gt; 
&lt;span class="no"&gt; 9&lt;/span&gt; 28 tests, 33 assertions, 0 failures, 0 errors
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test &amp;quot;/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb&amp;quot; &amp;quot;test/functional/accounts_controller_test.rb&amp;quot; &amp;quot;test/functional/password_resets_controller_test.rb&amp;quot; &amp;quot;test/functional/people_controller_test.rb&amp;quot; &amp;quot;test/functional/sessions_controller_test.rb&amp;quot; 
&lt;span class="no"&gt;11&lt;/span&gt; [DEPRECATION] should_be_restful is deprecated.  Please see http://thoughtbot.lighthouseapp.com/projects/5807/tickets/78 for more information.
&lt;span class="no"&gt;12&lt;/span&gt; [DEPRECATION] should_be_restful is deprecated.  Please see http://thoughtbot.lighthouseapp.com/projects/5807/tickets/78 for more information.
&lt;span class="no"&gt;13&lt;/span&gt; Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
&lt;span class="no"&gt;14&lt;/span&gt; Started
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt; .....................................................
&lt;span class="no"&gt;16&lt;/span&gt; Finished in 1.828175 seconds.
&lt;span class="no"&gt;17&lt;/span&gt; 
&lt;span class="no"&gt;18&lt;/span&gt; 53 tests, 72 assertions, 0 failures, 0 errors
&lt;span class="no"&gt;19&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test &amp;quot;/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb&amp;quot;  
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt;21&lt;/span&gt; real  0m10.365s
&lt;span class="no"&gt;22&lt;/span&gt; user  0m6.582s
&lt;span class="no"&gt;23&lt;/span&gt; sys  0m1.925s

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Next, a run with the MEMORY engine:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; $ time rake
&lt;span class="no"&gt; 2&lt;/span&gt; (in /Users/francois/Documents/work/fasttest)
&lt;span class="no"&gt; 3&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test &amp;quot;/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb&amp;quot; &amp;quot;test/unit/password_reset_mailer_test.rb&amp;quot; &amp;quot;test/unit/person_test.rb&amp;quot; 
&lt;span class="no"&gt; 4&lt;/span&gt; Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; Started
&lt;span class="no"&gt; 6&lt;/span&gt; ............................
&lt;span class="no"&gt; 7&lt;/span&gt; Finished in 0.602607 seconds.
&lt;span class="no"&gt; 8&lt;/span&gt; 
&lt;span class="no"&gt; 9&lt;/span&gt; 28 tests, 33 assertions, 0 failures, 0 errors
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test &amp;quot;/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb&amp;quot; &amp;quot;test/functional/accounts_controller_test.rb&amp;quot; &amp;quot;test/functional/password_resets_controller_test.rb&amp;quot; &amp;quot;test/functional/people_controller_test.rb&amp;quot; &amp;quot;test/functional/sessions_controller_test.rb&amp;quot; 
&lt;span class="no"&gt;11&lt;/span&gt; [DEPRECATION] should_be_restful is deprecated.  Please see http://thoughtbot.lighthouseapp.com/projects/5807/tickets/78 for more information.
&lt;span class="no"&gt;12&lt;/span&gt; [DEPRECATION] should_be_restful is deprecated.  Please see http://thoughtbot.lighthouseapp.com/projects/5807/tickets/78 for more information.
&lt;span class="no"&gt;13&lt;/span&gt; Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
&lt;span class="no"&gt;14&lt;/span&gt; Started
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt; .....................................................
&lt;span class="no"&gt;16&lt;/span&gt; Finished in 1.132142 seconds.
&lt;span class="no"&gt;17&lt;/span&gt; 
&lt;span class="no"&gt;18&lt;/span&gt; 53 tests, 72 assertions, 0 failures, 0 errors
&lt;span class="no"&gt;19&lt;/span&gt; /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test &amp;quot;/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb&amp;quot;  
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt;21&lt;/span&gt; real  0m9.111s
&lt;span class="no"&gt;22&lt;/span&gt; user  0m6.862s
&lt;span class="no"&gt;23&lt;/span&gt; sys  0m1.870s

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Note that in &lt;code class="filename"&gt;test/test_helper.rb&lt;/code&gt;, I had to disable transactional fixtures.  This would account for a lot the lost time difference.&lt;/p&gt;
&lt;p&gt;If you want to play with this further, the sample application&amp;#8217;s code is available at &lt;a href="http://github.com/francois/fasttest"&gt;http://github.com/francois/fasttest&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I would be interested in seeing other people&amp;#8217;s runs, to know if it&amp;#8217;s just my machine that runs at essentially the same speed.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/kKPB8thmOpI" height="1" width="1"/&gt;</content>
    
    <category term="activerecord" label="Activerecord" />
    
    <category term="unit-testing" label="Unit-testing" />
      </entry>
  <entry>
    <title>How to Use AlterEgo With ActiveRecord</title>
    <link href="http://blog.teksol.info/2008/12/09/how-to-use-alterego-with-activerecord.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-12-09:20081209095217</id>
    <updated>2008-12-09T09:52:17-05:00</updated>
    <content type="html">&lt;p&gt;I stumbled on &lt;a href="http://alter-ego.rubyforge.org/"&gt;AlterEgo&lt;/a&gt; last week, after Avdi announced it.  This library implements the &lt;a href="http://en.wikipedia.org/wiki/State_pattern"&gt;state pattern&lt;/a&gt; for any object, not just ActiveRecord models, which &lt;a href="http://agilewebdevelopment.com/plugins/acts_as_state_machine"&gt;acts_as_state_machine&lt;/a&gt; (and it&amp;#8217;s successor, &lt;a href="http://github.com/rubyist/aasm"&gt;aasm&lt;/a&gt;) do.&lt;/p&gt;
&lt;p&gt;But, many people, myself included, want to use AlterEgo in the context of ActiveRecord models.  Fortunately, AlterEgo provides us with all the necessary plumbing to do that very easily.&lt;/p&gt;
&lt;p&gt;I will reuse Avdi&amp;#8217;s traffic light example from the specifications.  Avdi already points us to 90% of the solution in his example:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;TrafficLightWithCustomStorage&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;state&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     gyr = [
&lt;span class="no"&gt; 4&lt;/span&gt;       &lt;span class="iv"&gt;@hardware_controller&lt;/span&gt;.green,
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;       &lt;span class="iv"&gt;@hardware_controller&lt;/span&gt;.yellow,
&lt;span class="no"&gt; 6&lt;/span&gt;       &lt;span class="iv"&gt;@hardware_controller&lt;/span&gt;.red
&lt;span class="no"&gt; 7&lt;/span&gt;     ]
&lt;span class="no"&gt; 8&lt;/span&gt; 
&lt;span class="no"&gt; 9&lt;/span&gt;     &lt;span class="r"&gt;case&lt;/span&gt; gyr
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;     &lt;span class="r"&gt;when&lt;/span&gt; [&lt;span class="pc"&gt;true&lt;/span&gt;, &lt;span class="pc"&gt;false&lt;/span&gt;, &lt;span class="pc"&gt;false&lt;/span&gt;] &lt;span class="r"&gt;then&lt;/span&gt; &lt;span class="sy"&gt;:proceed&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt;     &lt;span class="r"&gt;when&lt;/span&gt; [&lt;span class="pc"&gt;false&lt;/span&gt;, &lt;span class="pc"&gt;true&lt;/span&gt;, &lt;span class="pc"&gt;false&lt;/span&gt;] &lt;span class="r"&gt;then&lt;/span&gt; &lt;span class="sy"&gt;:caution&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt;     &lt;span class="r"&gt;when&lt;/span&gt; [&lt;span class="pc"&gt;false&lt;/span&gt;, &lt;span class="pc"&gt;false&lt;/span&gt;, &lt;span class="pc"&gt;true&lt;/span&gt;] &lt;span class="r"&gt;then&lt;/span&gt; &lt;span class="sy"&gt;:stop&lt;/span&gt;
&lt;span class="no"&gt;13&lt;/span&gt;     &lt;span class="r"&gt;else&lt;/span&gt; raise &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Invalid state!&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;14&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;16&lt;/span&gt; 
&lt;span class="no"&gt;17&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;state=&lt;/span&gt;(value)
&lt;span class="no"&gt;18&lt;/span&gt;     gyr = &lt;span class="r"&gt;case&lt;/span&gt; value
&lt;span class="no"&gt;19&lt;/span&gt;           &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="sy"&gt;:proceed&lt;/span&gt;  &lt;span class="r"&gt;then&lt;/span&gt; [&lt;span class="pc"&gt;true&lt;/span&gt;, &lt;span class="pc"&gt;false&lt;/span&gt;, &lt;span class="pc"&gt;false&lt;/span&gt;]
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt;           &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="sy"&gt;:caution&lt;/span&gt;  &lt;span class="r"&gt;then&lt;/span&gt; [&lt;span class="pc"&gt;false&lt;/span&gt;, &lt;span class="pc"&gt;true&lt;/span&gt;, &lt;span class="pc"&gt;false&lt;/span&gt;]
&lt;span class="no"&gt;21&lt;/span&gt;           &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="sy"&gt;:stop&lt;/span&gt;     &lt;span class="r"&gt;then&lt;/span&gt; [&lt;span class="pc"&gt;false&lt;/span&gt;, &lt;span class="pc"&gt;false&lt;/span&gt;, &lt;span class="pc"&gt;true&lt;/span&gt;]
&lt;span class="no"&gt;22&lt;/span&gt;           &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;23&lt;/span&gt;     &lt;span class="iv"&gt;@hardware_controller&lt;/span&gt;.green  = gyr[&lt;span class="i"&gt;0&lt;/span&gt;]
&lt;span class="no"&gt;24&lt;/span&gt;     &lt;span class="iv"&gt;@hardware_controller&lt;/span&gt;.yellow = gyr[&lt;span class="i"&gt;1&lt;/span&gt;]
&lt;span class="no"&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;/span&gt;     &lt;span class="iv"&gt;@hardware_controller&lt;/span&gt;.red    = gyr[&lt;span class="i"&gt;2&lt;/span&gt;]
&lt;span class="no"&gt;26&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;27&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;When an object implements #state and #state=, AlterEgo will serialize it&amp;#8217;s state using these two methods.  Let&amp;#8217;s hook this to ActiveRecord (note, I changed the code for a quick example, so it&amp;#8217;s not exactly the same, but very similar):&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;app/models/traffic_light.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;TrafficLight&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;state&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="r"&gt;case&lt;/span&gt; read_attribute(&lt;span class="sy"&gt;:color&lt;/span&gt;)
&lt;span class="no"&gt; 4&lt;/span&gt;     &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;;   &lt;span class="sy"&gt;:proceed&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;     &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;yellow&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;;  &lt;span class="sy"&gt;:caution&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;     &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;;     &lt;span class="sy"&gt;:stop&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt;     &lt;span class="r"&gt;else&lt;/span&gt;;           raise &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Invalid color: &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;read_attribute(&lt;span class="sy"&gt;:color&lt;/span&gt;).inspect&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 8&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt;11&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;state=&lt;/span&gt;(value)
&lt;span class="no"&gt;12&lt;/span&gt;     color_value = &lt;span class="r"&gt;case&lt;/span&gt; value
&lt;span class="no"&gt;13&lt;/span&gt;     &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="sy"&gt;:proceed&lt;/span&gt;;  &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;14&lt;/span&gt;     &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="sy"&gt;:caution&lt;/span&gt;;  &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;yellow&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt;     &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="sy"&gt;:stop&lt;/span&gt;;     &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;16&lt;/span&gt;     &lt;span class="r"&gt;else&lt;/span&gt;;           raise &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Don't know how to convert &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;value.inspect&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; to color value&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;17&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;18&lt;/span&gt;     write_attribute(&lt;span class="sy"&gt;:color&lt;/span&gt;, color_value)
&lt;span class="no"&gt;19&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The most important thing you should check is that your database model has a valid state on model instantiation.  Whether you do it using the :default key in your migration or some other way is irrelevant, but the value has to be provided, or the #state method will barf (or implement default processing there?)  In the example app, I opted to use a default value:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;db/migrate/20081209150159_create_traffic_lights.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;CreateTrafficLights&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Migration&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.up
&lt;span class="no"&gt; 3&lt;/span&gt;     create_table &lt;span class="sy"&gt;:traffic_lights&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|
&lt;span class="no"&gt; 4&lt;/span&gt;       t.string &lt;span class="sy"&gt;:color&lt;/span&gt;, &lt;span class="sy"&gt;:default&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt; 
&lt;span class="no"&gt; 8&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.down
&lt;span class="no"&gt; 9&lt;/span&gt;     drop_table &lt;span class="sy"&gt;:traffic_lights&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You may check the full code in action in the GitHub repository:  &lt;a href="http://github.com/francois/alter_ego_plus_active_record"&gt;alter_ego_plus_active_record&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/BxgwMiGGhc8" height="1" width="1"/&gt;</content>
    
    <category term="active-record" label="Active-record" />
    
    <category term="alter-ego" label="Alter-ego" />
      </entry>
  <entry>
    <title>A Simple LinkedList Implementation in Ruby</title>
    <link href="http://blog.teksol.info/2008/11/11/a-simple-linkedlist-implementation-in-ruby.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-11-11:20081111144031</id>
    <updated>2008-11-11T14:40:31-05:00</updated>
    <content type="html">&lt;p&gt;Being bored out of my mind on Monday night, and not having anything interesting to watch on TV, I decided I&amp;#8217;d try my hand at implementing a linked list in Ruby.  For the impatient:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; $ sudo gem install linked_list

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Unfortunately, performance is very poor:&lt;/p&gt;
&lt;pre&gt;Building a list of 1 million random strings
Took 4.389 seconds
Converting to an array
Took 1.686 seconds
                      user     system      total        real
list#push/pop     0.350000   0.000000   0.350000 (  0.364001)
array#push/pop    0.000000   0.000000   0.000000 (  0.009894)
                      user     system      total        real
list#last         0.000000   0.000000   0.000000 (  0.000027)
array#last        0.000000   0.000000   0.000000 (  0.000012)
                      user     system      total        real
list#first        0.010000   0.000000   0.010000 (  0.001188)
array#first       0.000000   0.000000   0.000000 (  0.000449)
                      user     system      total        real
list#reverse      7.540000   0.170000   7.710000 (  7.806306)
array#reverse     0.010000   0.010000   0.020000 (  0.008443)
                      user     system      total        real
list#dup         23.080000   0.380000  23.460000 ( 24.039316)
array#dup         0.000000   0.000000   0.000000 (  0.000017)
&lt;/pre&gt;
&lt;p&gt;NOTE:  &lt;code&gt;#dup&lt;/code&gt; is implemented as &lt;code&gt;self.reverse.reverse&lt;/code&gt;, which is pretty inefficient, I know.  Thanks for asking.&lt;/p&gt;
&lt;p&gt;I probably have implementation issues, but I expected &lt;code&gt;#push&lt;/code&gt; and &lt;code&gt;#pop&lt;/code&gt; to be as fast as the Array implementation.  Anyway, if anyone&amp;#8217;s interested, there&amp;#8217;s a &lt;a href="http://github.com/francois/linked_list/tree/master"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/hvF3B_1CZmo" height="1" width="1"/&gt;</content>
      </entry>
  <entry>
    <title>FAIL: Can't Migrate Because Pending Migrations..</title>
    <link href="http://blog.teksol.info/2008/11/02/fail-cant-migrate-because-pending-migrations-dot.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-11-02:20081102210655</id>
    <updated>2008-11-02T21:06:55-05:00</updated>
    <content type="html">&lt;p&gt;How&amp;#8217;s that for being helpful?&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; $ rake db:migrate --trace
&lt;span class="no"&gt; 2&lt;/span&gt; (in /Users/francois/Documents/work/family_budget)
&lt;span class="no"&gt; 3&lt;/span&gt; ** Invoke db:test:prepare (first_time)
&lt;span class="no"&gt; 4&lt;/span&gt; ** Invoke environment (first_time)
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; ** Execute environment
&lt;span class="no"&gt; 6&lt;/span&gt; ** Invoke db:abort_if_pending_migrations (first_time)
&lt;span class="no"&gt; 7&lt;/span&gt; ** Invoke environment 
&lt;span class="no"&gt; 8&lt;/span&gt; ** Execute db:abort_if_pending_migrations
&lt;span class="no"&gt; 9&lt;/span&gt; You have 13 pending migrations:
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;      1 CreateFamilies
&lt;span class="no"&gt;11&lt;/span&gt;      2 CreatePeople
&lt;span class="no"&gt;12&lt;/span&gt;      3 CreateAccounts
&lt;span class="no"&gt;13&lt;/span&gt;      4 CreateTransfers
&lt;span class="no"&gt;14&lt;/span&gt;      5 CreateTransferMembers
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt;      6 AddFamilyToPeople
&lt;span class="no"&gt;16&lt;/span&gt;      7 DestroyTransferMember
&lt;span class="no"&gt;17&lt;/span&gt;      8 AddDebitAccountIdCreditAccountIdAndAmountToTransfers
&lt;span class="no"&gt;18&lt;/span&gt;      9 CreateBudgets
&lt;span class="no"&gt;19&lt;/span&gt;     10 AddAdminFlagToPerson
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt;     11 DefaultBudgetAmountToZeroFromNull
&lt;span class="no"&gt;21&lt;/span&gt;   20081103020010 CreateTransactions
&lt;span class="no"&gt;22&lt;/span&gt;   20081103020142 CreateBankAccounts
&lt;span class="no"&gt;23&lt;/span&gt; Run &amp;quot;rake db:migrate&amp;quot; to update your database then try again.

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Hmm, I&amp;#8217;m trying to run migrations, stupid&amp;hellip;&lt;/p&gt;
&lt;p&gt;I finally traced the problem (using &lt;a href="http://linux.die.net/man/1/git-bisect"&gt;git bisect&lt;/a&gt; !) to this:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;lib/tasks/cucumber.rake&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="gv"&gt;$:&lt;/span&gt;.unshift(&lt;span class="co"&gt;RAILS_ROOT&lt;/span&gt; + &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/vendor/plugins/cucumber/lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="no"&gt; 2&lt;/span&gt; require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;cucumber/rake/task&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt; 
&lt;span class="no"&gt; 4&lt;/span&gt; namespace &lt;span class="sy"&gt;:test&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt;   desc &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Runs the Cucumber features tests (integration tests)&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;   &lt;span class="co"&gt;Cucumber&lt;/span&gt;::&lt;span class="co"&gt;Rake&lt;/span&gt;::&lt;span class="co"&gt;Task&lt;/span&gt;.new(&lt;span class="sy"&gt;:features&lt;/span&gt;) &lt;span class="r"&gt;do&lt;/span&gt; |t|
&lt;span class="no"&gt; 7&lt;/span&gt;     &lt;span class="co"&gt;ENV&lt;/span&gt;[&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;] = &lt;span class="co"&gt;RAILS_ENV&lt;/span&gt; = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 8&lt;/span&gt;     &lt;span class="co"&gt;Rake&lt;/span&gt;::&lt;span class="co"&gt;Task&lt;/span&gt;[&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;db:test:prepare&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;].invoke
&lt;span class="no"&gt; 9&lt;/span&gt;     &lt;span class="co"&gt;Rake&lt;/span&gt;::&lt;span class="co"&gt;Task&lt;/span&gt;[&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;db:fixtures:load&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;].invoke
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;     t.cucumber_opts = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;--format pretty&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Can you spot the error?  The problem originates with setting RAILS_ENV in this code block.  But isn&amp;#8217;t the block supposed to be executed later?  When the block is #call&amp;#8217;ed?  Very strange.  At the moment, I simply commented out the line, but that&amp;#8217;s going to bite me in a couple moments when I&amp;#8217;m ready to test my features again&amp;hellip;  Oh joy of debugging!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/ypa90YEx7u8" height="1" width="1"/&gt;</content>
    
    <category term="funny" label="Funny" />
      </entry>
  <entry>
    <title>Why no #should_allow_attributes?</title>
    <link href="http://blog.teksol.info/2008/11/01/why-no-should_allow_attributes.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-11-01:20081101223319</id>
    <updated>2008-11-01T22:33:19-04:00</updated>
    <content type="html">&lt;p&gt;Thoughtbot&amp;#8217;s &lt;a href="http://www.thoughtbot.com/projects/shoulda"&gt;Shoulda&lt;/a&gt; is a very nice piece of work.  Their &lt;a href="http://api.rubyonrails.org/classes/ActiveRecord/Base.html"&gt;ActiveRecord&lt;/a&gt; macros are also a god-send:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; should_protect_attributes &lt;span class="sy"&gt;:family_id&lt;/span&gt;, &lt;span class="sy"&gt;:debit_account_id&lt;/span&gt;, &lt;span class="sy"&gt;:credit_account_id&lt;/span&gt;, &lt;span class="sy"&gt;:created_at&lt;/span&gt;, &lt;span class="sy"&gt;:updated_at&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This code will assert that ActiveRecord attributes are somehow protected (either though attr_accessible or attr_protected).  But what about the reverse?  There isn&amp;#8217;t a macro to do that.  I happened to need it, so I implemented it on my own &lt;a href="http://github.com/francois/shoulda/tree/should_allow_attributes"&gt;fork&lt;/a&gt; of Shoulda.&lt;/p&gt;
&lt;p&gt;This allows us to specify:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt;1&lt;/span&gt; should_protect_attributes &lt;span class="sy"&gt;:family_id&lt;/span&gt;, &lt;span class="sy"&gt;:debit_account_id&lt;/span&gt;, &lt;span class="sy"&gt;:credit_account_id&lt;/span&gt;, &lt;span class="sy"&gt;:created_at&lt;/span&gt;, &lt;span class="sy"&gt;:updated_at&lt;/span&gt;
&lt;span class="no"&gt;2&lt;/span&gt; should_allow_attributes &lt;span class="sy"&gt;:family&lt;/span&gt;, &lt;span class="sy"&gt;:debit_account&lt;/span&gt;, &lt;span class="sy"&gt;:credit_account&lt;/span&gt;, &lt;span class="sy"&gt;:amount&lt;/span&gt;, &lt;span class="sy"&gt;:posted_on&lt;/span&gt;, &lt;span class="sy"&gt;:description&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;NOTE: Example taken from my &lt;a href="http://github.com/francois/family_budget"&gt;family budget&lt;/a&gt; application.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/hxV1NtC4YbQ" height="1" width="1"/&gt;</content>
    
    <category term="unit-tests" label="Unit-tests" />
    
    <category term="shoulda" label="Shoulda" />
      </entry>
  <entry>
    <title>We Are Winners!</title>
    <link href="http://blog.teksol.info/2008/11/01/we-are-winners.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-11-01:20081101215032</id>
    <updated>2008-11-01T21:50:32-04:00</updated>
    <content type="html">&lt;p&gt;&lt;strong&gt;WOW&lt;/strong&gt;  Initially, &lt;a href="http://whatdoesthiserrormean.com/"&gt;What Does this Error Mean?&lt;/a&gt; had a pretty strong presence in the top leaderboard.  As the days went on, we slipped further and further down the scale.  We hovered between 10th and 15th on the general leaderboard.&lt;/p&gt;
&lt;p&gt;I knew we could sort by different criterias, and I occasionaly looked at the leaderboard by other criterias, but I can&amp;#8217;t remember at what ranks we were.&lt;/p&gt;
&lt;p&gt;In the end, we won &lt;strong&gt;Most Useful&lt;/strong&gt;.  That is a very good thing for us.&lt;/p&gt;
&lt;p&gt;Again, congratulations to all teams that participated, and I&amp;#8217;m hoping to see another repeat for 2009.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/1Qi7qB5bdv4" height="1" width="1"/&gt;</content>
    
    <category term="railsrumble" label="Railsrumble" />
      </entry>
  <entry>
    <title>Evolution of a Home Page: What Does This Error Mean?</title>
    <link href="http://blog.teksol.info/2008/10/23/evolution-of-a-home-page-what-does-this-error-mean.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-10-23:20081023195819</id>
    <updated>2008-10-23T19:58:19-04:00</updated>
    <content type="html">&lt;p&gt;To celebrate the start of voting for the &lt;a href="http://railsrumble.com/"&gt;RailsRumble 2008&lt;/a&gt; competition, I thought I&amp;#8217;d make a new video.  Thank god for version control!  This allowed me to go back in time, all the way to the initial checkin, and take a snapshot of each evolution of our design.&lt;/p&gt;
&lt;p&gt;It was a very iterative process.  I did a couple of things in the bus from Sherbrooke to Montreal, then James picked up from there.  The ugly green and red is mine, the nice white, orange and greys are his.&lt;/p&gt;
&lt;p&gt;I hope you enjoy this show!&lt;/p&gt;
&lt;p&gt;&lt;object width="400" height="300"&gt;	&lt;param name="allowfullscreen" value="true" /&gt;	&lt;param name="allowscriptaccess" value="always" /&gt;	&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=2051057&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" /&gt;	&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=2051057&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;a href="http://vimeo.com/2051057?pg=embed&amp;amp;sec=2051057"&gt;Evolution of a home page: What Does this Error Mean?&lt;/a&gt; from &lt;a href="http://vimeo.com/user784889?pg=embed&amp;amp;sec=2051057"&gt;François Beausoleil&lt;/a&gt; on &lt;a href="http://vimeo.com?pg=embed&amp;amp;sec=2051057"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you prefer the high-quality video: &lt;a href="http://s3.amazonaws.com/teksol/evolution-of-a-home-page--what-does-this-error-mean.mov"&gt;evolution-of-a-home-page&amp;#8212;what-does-this-error-mean.mov&lt;/a&gt; (15 Mb, QuickTime 4)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/Yiu-XwHgOiE" height="1" width="1"/&gt;</content>
    
    <category term="railsrumble" label="Railsrumble" />
      </entry>
  <entry>
    <title>Future creep?  We simply *had* to Stay Focused</title>
    <link href="http://blog.teksol.info/2008/10/20/future-creep-we-had-to-stay-focused.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-10-20:20081020122439</id>
    <updated>2008-10-20T12:24:39-04:00</updated>
    <content type="html">&lt;p&gt;During the &lt;a href="http://railsrumble.com/"&gt;RailsRumble&lt;/a&gt;, we really had to guard against future creep.  Jamis summarized it well in &lt;a href="http://www.37signals.com/svn/posts/1324-beware-of-future-creep"&gt;Beware of future creep&lt;/a&gt;&lt;/p&gt;
&lt;blockquote cite="http://www.37signals.com/svn/posts/1324-beware-of-future-creep"&gt;&lt;p&gt;First, never implement more than you need to. That sounds harsh, in a grasshopper-and-the-ant kind of way, except it really isn’t. It isn’t a mandate to slack off, it’s a command to do what you know. Implement the feature you’re working on, not the feature you hope will land someday. Keep it simple, keep it minimal, and keep it &lt;em&gt;real&lt;/em&gt;.&lt;/p&gt;
&lt;p style="text-align:right"&gt;Jamis in &lt;a href="http://www.37signals.com/svn/posts/1324-beware-of-future-creep"&gt;Beware of future creep&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While pairing with &lt;a href="http://danielharan.com/"&gt;Daniel&lt;/a&gt;, it happened to us a couple of times when he or I would say &amp;#8220;What does our story say already?  Oh, let&amp;#8217;s add a story for later&amp;#8221;.  That was really the essence of the competition.  Delay &lt;strong&gt;anything&lt;/strong&gt; (features, infrastructure, what have you) that could be delayed.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;re really excited about &lt;a href="http://whatdoesthiserrormean.com/"&gt;What Does this Error Mean?&lt;/a&gt; and the way it panned out.  Happy &lt;a href="http://railsrumble.com/teams/team-giraffesoft"&gt;voting&lt;/a&gt; !&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/B4Z4D99DAbM" height="1" width="1"/&gt;</content>
    
    <category term="railsrumble" label="Railsrumble" />
      </entry>
  <entry>
    <title>Don't Write Tests?  Are You Crazy?</title>
    <link href="http://blog.teksol.info/2008/10/19/dont-write-tests-are-you-crazy.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-10-19:20081019234637</id>
    <updated>2008-10-19T23:46:37-04:00</updated>
    <content type="html">&lt;p&gt;Reading up on RailsRumble, I found &lt;a href="http://blog.rayvinly.com/"&gt;Raymond Law&lt;/a&gt;&amp;#8217;s &lt;a href="http://blog.rayvinly.com/articles/2008/10/19/railsrumble-2008"&gt;RailsRumble 2008&lt;/a&gt; post-mortem.&lt;/p&gt;
&lt;p&gt;Raymond writes:&lt;/p&gt;
&lt;blockquote cite="http://blog.rayvinly.com/articles/2008/10/19/railsrumble-2008"&gt;&lt;p&gt;Forget about writing tests – Look, I am in support of agile and test-driven development, with one exception. That is, you are not participating in RailsRumble. There’s just no time for it. If you are a good enough programmer, you can build a small application with no tests. If you can’t, I don’t want to work with you. Writing tests for a new application starting from scratch in just 48 hours is simple not feasible. No bargain!&lt;/p&gt;&lt;/blockquote&gt;
&lt;p style="text-align:right"&gt;Raymond Law in &lt;a href="http://blog.rayvinly.com/articles/2008/10/19/railsrumble-2008"&gt;RailsRumble 2008&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Sorry Raymond, but that is plain &lt;strong&gt;crazy&lt;/strong&gt;.  We took the time to write tests, and it gave us confidence.  We even ended up with a 1:1.9 ratio of code to test.  I do believe in agile (like you do), and I was really happy for those tests.  Especially as we got more and more tired, the tests were a safety vest that kept us afloat while time was running out.&lt;/p&gt;
&lt;blockquote cite="http://blog.rayvinly.com/articles/2008/10/19/railsrumble-2008"&gt;&lt;p&gt;No ticketing system like Unfuddle and Lighthouse – One team member had tried to use Unfuddle to create tickets to get ourselves more organized. While it was a sincere attempt, it simply didn’t work. There’s no time to accept a ticket, create a new ticket, &amp;#8230; We pretty much abandoned it soon after we started.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p style="text-align:right"&gt;Raymond Law in &lt;a href="http://blog.rayvinly.com/articles/2008/10/19/railsrumble-2008"&gt;RailsRumble 2008&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We used &lt;a href="http://pivotaltracker.com/"&gt;Pivotal Tracker&lt;/a&gt; to organize our stories.  As Raymond said, he didn&amp;#8217;t have a lot of time to plan ahead of the competition.  We got together 10 days prior to the competition and we used Pivotal Tracker to remember a lot of things for us.  Accepting a story was as simple as clicking &lt;em&gt;Start&lt;/em&gt;.  I think having the stories spelled out gave us another safety vest by keeping us focused and on track.&lt;/p&gt;
&lt;p&gt;Before the rumble, I didn&amp;#8217;t know about Pivotal Tracker, but now that I do, I&amp;#8217;ll be sure to use it more and more.  It was a very low ceremony way to remember things.  Even while coding the tests for a story, if we uncovered a missing requirement, we just Apple-Tab&amp;#8217;d to Pivotal Tracker, added the story, Apple-Tab&amp;#8217;d back and went on.&lt;/p&gt;
&lt;p&gt;Hope you had a great weekend!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/790l86CDawA" height="1" width="1"/&gt;</content>
    
    <category term="railsrumble" label="Railsrumble" />
      </entry>
  <entry>
    <title>Fixture Validations in Using Shoulda</title>
    <link href="http://blog.teksol.info/2008/10/19/fixture-validations-in-using-shoulda.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-10-19:20081019220847</id>
    <updated>2008-10-19T22:08:47-04:00</updated>
    <content type="html">&lt;p&gt;I was working on my &lt;a href="http://github.com/francois/family_budget"&gt;family budget&lt;/a&gt; application and wanted to validate existing fixtures.  I wrote the following:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;h5&gt;test/shoulda_macros/validation_macros.rb&lt;/h5&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;ValidationMacros&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.included(base)
&lt;span class="no"&gt; 3&lt;/span&gt;     base.send &lt;span class="sy"&gt;:extend&lt;/span&gt;, &lt;span class="co"&gt;ClassMethods&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt; 6&lt;/span&gt;   &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;ClassMethods&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt;     &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;should_have_valid_fixtures&lt;/span&gt;(klass=&lt;span class="pc"&gt;self&lt;/span&gt;)
&lt;span class="no"&gt; 8&lt;/span&gt;       should &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;have all valid fixtures&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;         klass.name.sub(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Test&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;).constantize.all.each &lt;span class="r"&gt;do&lt;/span&gt; |object|
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;           assert object.valid?, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Fixture &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;object.inspect&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; is invalid&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt;         &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;13&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;14&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;16&lt;/span&gt; 
&lt;span class="no"&gt;17&lt;/span&gt; &lt;span class="co"&gt;Test&lt;/span&gt;::&lt;span class="co"&gt;Unit&lt;/span&gt;::&lt;span class="co"&gt;TestCase&lt;/span&gt;.send &lt;span class="sy"&gt;:include&lt;/span&gt;, &lt;span class="co"&gt;ValidationMacros&lt;/span&gt;
&lt;span class="no"&gt;18&lt;/span&gt; 
&lt;span class="no"&gt;19&lt;/span&gt; &lt;span class="c"&gt;# Usage Example&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;AccountTest&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;Test&lt;/span&gt;::&lt;span class="co"&gt;Unit&lt;/span&gt;::&lt;span class="co"&gt;TestCase&lt;/span&gt;
&lt;span class="no"&gt;21&lt;/span&gt;   should_have_valid_fixtures
&lt;span class="no"&gt;22&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;23&lt;/span&gt; 
&lt;span class="no"&gt;24&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;StrangeNameTest&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;Test&lt;/span&gt;::&lt;span class="co"&gt;Unit&lt;/span&gt;::&lt;span class="co"&gt;TestCase&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;/span&gt;   &lt;span class="c"&gt;# Pass the ActiveRecord (or anything that respond_to?(:valid?)&lt;/span&gt;
&lt;span class="no"&gt;26&lt;/span&gt;   &lt;span class="c"&gt;# and respond_to?(:all)) class to validate against.&lt;/span&gt;
&lt;span class="no"&gt;27&lt;/span&gt;   should_have_valid_fixtures &lt;span class="co"&gt;Account&lt;/span&gt;
&lt;span class="no"&gt;28&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This might be interesting if you use fixtures.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/Yutmifszlzw" height="1" width="1"/&gt;</content>
    
    <category term="shoulda" label="Shoulda" />
    
    <category term="unit-testing" label="Unit-testing" />
      </entry>
  <entry>
    <title>RailsRumble 2008: Done &amp; Deployed!</title>
    <link href="http://blog.teksol.info/2008/10/19/railsrumble-2008-done-and-deployed.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-10-19:20081019204027</id>
    <updated>2008-10-19T20:40:27-04:00</updated>
    <content type="html">&lt;p&gt;Well, those were an interesting 48 hours.  We built and deployed &lt;a href="http://whatdoesthiserrormean.com/"&gt;What Does this Error Mean?&lt;/a&gt; within the time constraints that were imposed by the rules.&lt;/p&gt;
&lt;p&gt;What is &lt;a href="http://whatdoesthiserrormean.com/"&gt;What Does this Error Mean?&lt;/a&gt; ?  It&amp;#8217;s a way to search for errors and solutions, but without &lt;a href="http://google.com/"&gt;Google&lt;/a&gt;&amp;#8217;s page ranking algorithm being in the way.  You want to know what it looks like?&lt;/p&gt;
&lt;p style="text-align:center"&gt;&lt;a href="/2008/10/19/what-does-this-error-mean.png"&gt;&lt;img width="400" src="/2008/10/19/what-does-this-error-mean.png" alt="Obligatory What Does this Error Mean? screenshot.  Our app is simple enough that it only needs a paste box on the home page"/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style="text-align:center"&gt;&lt;a href="/2008/10/19/what-does-this-error-mean.png"&gt;Click for larger version&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The concept behind &lt;a href="http://whatdoesthiserrormean.com/"&gt;What Does this Error Mean?&lt;/a&gt; is simple: you receive some obscure error message you don&amp;#8217;t understand.  You copy it, go to &lt;a href="http://whatdoesthiserrormean.com/"&gt;What Does this Error Mean?&lt;/a&gt;, paste and then click &amp;#8220;Find me some help!&amp;#8221;.  If we find similar error messages in the system from previous posters, we&amp;#8217;ll present you with ranked solutions.  If we don&amp;#8217;t find a solution, we allow you to follow the error so you get notified of new solutions on your error.&lt;/p&gt;
&lt;p&gt;To help us poor developers, &lt;a href="http://giraffesoft.com/"&gt;Team GiraffeSoft&lt;/a&gt; even took the time to create two plugins:  &lt;a href="http://github.com/giraffesoft/what_does_this_error_mean-rails"&gt;What Does this Error Mean? Rails Edition&lt;/a&gt; and &lt;a href="http://github.com/giraffesoft/what_does_this_error_mean-merb"&gt;What Does this Error Mean? Merb Edition&lt;/a&gt; (both plugins have only been tested on the latest released branch).  Install the plugins, and suddenly your development error page becomes a direct link to &lt;a href="http://whatdoesthiserrormean.com/"&gt;What Does this Error Mean?&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To top it all off, &lt;a href="http://jamesgolick.com/"&gt;James&lt;/a&gt;  made a screencast for your viewing pleasure:  &lt;a href="http://s3.amazonaws.com/giraffecasts/wdtem_screencast.mov"&gt;wdtem_screencast.mov&lt;/a&gt; (QuickTime MP4, 75 Mb, 3 minutes).&lt;/p&gt;
&lt;p&gt;If you like our idea, but most of all it&amp;#8217;s implementation, &lt;a href="http://www.railsrumble.com/teams/team-giraffesoft"&gt;vote for us&lt;/a&gt;.  We will be very thankful.&lt;/p&gt;
&lt;h2&gt;Things that worked&lt;/h2&gt;
&lt;p&gt;So, what worked about this competition?  Well&amp;hellip;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Being in the same physical location helped, &lt;strong&gt;a lot&lt;/strong&gt;.  This meant we could bounce ideas off each other very quickly;&lt;/li&gt;
	&lt;li&gt;Pair programming;&lt;/li&gt;
	&lt;li&gt;Unit testing (we used &lt;a href="http://thoughtbot.com/projects/shoulda"&gt;Shoulda&lt;/a&gt; and &lt;a href="http://rubyforge.org/projects/test-unit"&gt;Test::Unit&lt;/a&gt;);&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are so self-evident, I don&amp;#8217;t know why people don&amp;#8217;t do that &lt;strong&gt;all the time&lt;/strong&gt;.  The &lt;a href="http://railsrumble.com/"&gt;RailsRumble&lt;/a&gt; team encouraged us to do unit testing, and I pity anyone who didn&amp;#8217;t do that.  We&amp;#8217;re confident that our application is pretty solid, given the amount of tests we have versus the simplicity of the application:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;div class="CodeRay"&gt;&lt;pre&gt;
&lt;span class="no"&gt; 1&lt;/span&gt; $ rake stats
&lt;span class="no"&gt; 2&lt;/span&gt; (in /Users/francois/Documents/work/team-giraffesoft)
&lt;span class="no"&gt; 3&lt;/span&gt; +----------------------+-------+-------+---------+---------+-----+-------+
&lt;span class="no"&gt; 4&lt;/span&gt; | Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |
&lt;span class="no"&gt; &lt;strong&gt;5&lt;/strong&gt;&lt;/span&gt; +----------------------+-------+-------+---------+---------+-----+-------+
&lt;span class="no"&gt; 6&lt;/span&gt; | Controllers          |   278 |   223 |       8 |      25 |   3 |     6 |
&lt;span class="no"&gt; 7&lt;/span&gt; | Helpers              |   153 |    85 |       0 |      12 |   0 |     5 |
&lt;span class="no"&gt; 8&lt;/span&gt; | Models               |   241 |   188 |       7 |      31 |   4 |     4 |
&lt;span class="no"&gt; 9&lt;/span&gt; | Libraries            |   251 |   136 |       1 |      28 |  28 |     2 |
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; | Functional tests     |   730 |   588 |      11 |      13 |   1 |    43 |
&lt;span class="no"&gt;11&lt;/span&gt; | Unit tests           |   767 |   622 |       7 |       2 |   0 |   309 |
&lt;span class="no"&gt;12&lt;/span&gt; +----------------------+-------+-------+---------+---------+-----+-------+
&lt;span class="no"&gt;13&lt;/span&gt; | Total                |  2420 |  1842 |      34 |     111 |   3 |    14 |
&lt;span class="no"&gt;14&lt;/span&gt; +----------------------+-------+-------+---------+---------+-----+-------+
&lt;span class="no"&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/span&gt;   Code LOC: 632     Test LOC: 1210     Code to Test Ratio: 1:1.9

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I kept an eye out on our statistics during the competition, and our code to test ratio steadily climbed.  At the end of Saturday, we were at 1.2 / 1.3, and now 1.9?  Wow, hadn&amp;#8217;t really realized that.&lt;/p&gt;
&lt;h2&gt;Things that didn&amp;#8217;t work&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;While pairing with &lt;a href="http://danielharan.com/"&gt;Daniel&lt;/a&gt;, we were stumped a couple of times.  One of the most stressful ones was when we were trying to set properties on Person, and they wouldn&amp;#8217;t be set.  We were really stumped, and just stumbled upon for nearly 20 minutes.  You know what?  The error was that Person had an &lt;code&gt;attr_accessible&lt;/code&gt; declaration.  Once we realized that, our test passed immediately.  Daniel said he&amp;#8217;d write a plugin so that assigning to a protected attribute in test mode would raise an exception.  This will be a welcome relief!&lt;/li&gt;
	&lt;li&gt;Realizing Sunday morning that 1.5 hours of work from Saturday was a bad design decision.  Initially, we would put a 10 year cookie in your browsers.  Then James realized that this had security implications.  We were going to use the cookies to remember what errors and solutions you had posted so that if you ever came back and finally logged on or signed up we could just associate the anonymous postings to your person record.  So, 1.5 hours down the drain&amp;#8230;  We eventually replaced that with some values saved in the session.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thank you to the RailsRumble organizers for putting up this event for the second year in a row.  I count myself very lucky to have participated in this competition with such talented coders as Daniel and James.&lt;/p&gt;
&lt;p&gt;And good luck everyone on your own applications.  May the best one win!&lt;/p&gt;
&lt;p&gt;BTW, if you&amp;#8217;re wondering, we only built 17 story points on Sunday.  Bug fixing, you know&amp;hellip;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/DesoZN66OUY" height="1" width="1"/&gt;</content>
    
    <category term="railsrumble" label="Railsrumble" />
      </entry>
  <entry>
    <title>Team GiraffeSoft Built 28 Story Points in a Day</title>
    <link href="http://blog.teksol.info/2008/10/18/team-giraffesoft-built-28-story-points-in-a-day.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-10-18:20081018195246</id>
    <updated>2008-10-18T19:52:46-04:00</updated>
    <content type="html">&lt;p&gt;We used &lt;a href="http://pivotaltracker.com/"&gt;Pivotal Tracker&lt;/a&gt; to track our stories to build.  Initially, I thought we&amp;#8217;d do maybe 15 points in a day.  Looks like I was really, really, wrong:&lt;/p&gt;
&lt;p style="text-align:center"&gt;&lt;img src="/2008/10/18/28-points-in-a-day.png" alt="28 points in a day, according to Pivotal Tracker"/&gt;&lt;/p&gt;
&lt;p&gt;We delivered 28 story points in a single day.  Team GiraffeSoft really rocks!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/qxUhEEjT_Mk" height="1" width="1"/&gt;</content>
    
    <category term="railsrumble" label="Railsrumble" />
      </entry>
  <entry>
    <title>RailsRumble Started: OpenID FAIL</title>
    <link href="http://blog.teksol.info/2008/10/17/railsrumble-started-openid-fail.html" type="text/html" />
    <id>tag:blog.teksol.info,2008-10-17:20081017232707</id>
    <updated>2008-10-17T23:27:07-04:00</updated>
    <content type="html">&lt;p&gt;Well, the competition is started, but I can&amp;#8217;t access my account.  I use &lt;a href="http://technorati.com/"&gt;Technorati&lt;/a&gt; as my &lt;a href="http://openid.net/"&gt;OpenID&lt;/a&gt; provider, but Technorati won&amp;#8217;t let me grant &lt;a href="http://railsrumble.com/"&gt;RailsRumble&lt;/a&gt; permission to read my profile.&lt;/p&gt;
&lt;p style="background:#999;padding:2px;border:1px solid black;text-align:center;width:100%"&gt;&lt;img src="/2008/10/17/technorati-openid-failed-assoc.png" width="533" height="160" alt="Error: You do not have permission to authorize this url. Please sign in to the appropriate account."/&gt;&lt;/p&gt;
&lt;p&gt;Yee ha!  The wonders of OpenID!  But the saddest fact is that I &lt;strong&gt;did&lt;/strong&gt; authorized RailsRumble at Technorati at least once, since I could sign in and register.&lt;/p&gt;
&lt;p&gt;Who said OpenID solved all problems?  If I had an account at RailsRumble, this wouldn&amp;#8217;t be a problem&amp;#8230;&lt;/p&gt;
&lt;p&gt;Anyway, that won&amp;#8217;t prevent me from kickin&amp;#8217; asses!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/teksol/~4/jWwmCi05pOg" height="1" width="1"/&gt;</content>
    
    <category term="railsrumble-2008" label="Railsrumble-2008" />
      </entry>
</feed>
