<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss version="2.0">
  <channel>
    <title>the rails loft</title>
    <link>http://railsloft.com</link>
    <description />
    <language>en-us</language>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/railsloft" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="railsloft" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
      <title>Find or find_by_id</title>
      <description>&lt;div&gt;If you are a Rails dev, you probably know that there are two &amp;quot;finder&amp;quot; behaviors in Rails. There is User.find(1) that raises an ActiveRecord::RecordNotFound if the record with the given ID was not found. There are also generated finders like User.find_by_id(1) that return nil instead. Inconsistency? I don't think so. Let's take the typical controller update method:&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="code"&gt;     &lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt; 1&lt;/span&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;update&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 2&lt;/span&gt;    &lt;span class="attribute"&gt;@user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:id&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 3&lt;/span&gt;    &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;format&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 4&lt;/span&gt;      &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;update_attributes&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:user&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 5&lt;/span&gt;        &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:notice&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;User was successfully updated.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 6&lt;/span&gt;        &lt;span class="ident"&gt;format&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;html&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;redirect_to&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 7&lt;/span&gt;      &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 8&lt;/span&gt;        &lt;span class="ident"&gt;format&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;html&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;render&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;edit&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 9&lt;/span&gt;      &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;10&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;11&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;   &lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;The find method will raise RecordNotFound if for any reason the searched user cannot be found. Here is the same thing but checking if the returned user instance is null:&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="code"&gt;     &lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt; 1&lt;/span&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;update&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 2&lt;/span&gt;    &lt;span class="attribute"&gt;@user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find_by_id&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:id&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 3&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 4&lt;/span&gt;      &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;format&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 5&lt;/span&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;update_attributes&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:user&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 6&lt;/span&gt;          &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:notice&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;User was successfully updated.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 7&lt;/span&gt;          &lt;span class="ident"&gt;format&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;html&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;redirect_to&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 8&lt;/span&gt;        &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 9&lt;/span&gt;          &lt;span class="ident"&gt;format&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;html&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;render&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;edit&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;10&lt;/span&gt;        &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;11&lt;/span&gt;      &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;12&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;13&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;   &lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;I think the two options both have their place based on the following consideration:&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Probability that the record will be found or not. Maybe the object is not a User but something more volatile like an Object which deleted from the database when it's &amp;quot;consumed&amp;quot;.&lt;/li&gt;&lt;li&gt;There is a general catch mechanism by Rails when an exception raises up to the last possible moment. If you decide not to catch something you know a courtesy page can be presented and the error will be written in the log&lt;/li&gt;&lt;li&gt;If you decide to catch the exception you need to know what to check and maybe you need to look at the documentation.&lt;/li&gt;&lt;li&gt;Finally, there are cases where you really need to do something different if the record was not found than leave the error to rise. In this case you have the two options: find with catch or find_by_id with null checking.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Find should be used without catch in 90% of the cases. Because most of the time the ID you're searching for is just the result of a previous request on an object that is still there almost for sure. You can accept the 0.01% of cases where the find call rises an error that you can later track in the error log. If find had the same behavior of find_by_id (let's say for consistency reason), you'll be forced to check for null. If you let it go you end up with &amp;quot;nil when you didn't expect it&amp;quot; which is not as clear as a RecordNotFound.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If you need to catch that the record doesn't exist, then you have two options: catch the RecordNotFound or use find_by_id and check for null. I'm lazy and I prefer the second one: I don't need to check the documentation to remember what to catch.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Happy coding coders!&lt;/div&gt;</description>
      <pubDate>Tue, 27 Jan 2009 20:28:00 -0500</pubDate>
      <link>http://railsloft.com/posts/find-or-find_by_id</link>
      <guid>http://railsloft.com/posts/find-or-find_by_id</guid>
      <author>Renzo Borgatti</author>
    </item>
    <item>
      <title>What a difference a line makes</title>
      <description>&lt;div&gt;I&amp;rsquo;m all for keeping the business logic in places where it can be easily isolated and eventually reused. I&amp;rsquo;m sure I&amp;rsquo;m not saying anything new, but there are cases where it makes a lot of sense to delegate. Examples of those cases are stored procedures and complex queries. Relational databases are pretty smart in parsing and optimizing SQL and when the database is a separate process on another machine it&amp;rsquo;s even more efficient to use a different CPU than the main application process.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Today I had the pleasure to apply one fast and simple change that drastically reduced the computation load. Here&amp;rsquo;s the &amp;ldquo;before&amp;rdquo;:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: helvetica" class="Apple-style-span"&gt;&lt;pre&gt;&lt;code&gt;&lt;span style="color: #8660b0" class="constant"&gt;Song&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;.&lt;/span&gt;&lt;span style="color: #444444" class="ident"&gt;find&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;(&lt;/span&gt;&lt;span style="color: #b2655b" class="symbol"&gt;:all&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;).&lt;/span&gt;&lt;span style="color: #444444" class="ident"&gt;select&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;(&amp;amp;&lt;/span&gt;&lt;span style="color: #b2655b" class="symbol"&gt;:rating&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;).&lt;/span&gt;&lt;span style="color: #444444" class="ident"&gt;sort_by&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;(&amp;amp;&lt;/span&gt;&lt;span style="color: #b2655b" class="symbol"&gt;:rating&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;).&lt;/span&gt;&lt;span style="color: #444444" class="ident"&gt;reverse&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;.&lt;/span&gt;&lt;span style="color: #444444" class="ident"&gt;f&lt;/span&gt;&lt;span style="color: #444444" class="ident"&gt;irst&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;(&lt;/span&gt;&lt;span style="color: #b2655b" class="number"&gt;10&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;and here&amp;rsquo;s the &amp;ldquo;after&amp;rdquo;:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: helvetica" class="Apple-style-span"&gt;&lt;pre&gt;&lt;code&gt;&lt;span style="color: #8660b0" class="constant"&gt;Song&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;.&lt;/span&gt;&lt;span style="color: #444444" class="ident"&gt;find&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;(&lt;/span&gt;&lt;span style="color: #b2655b" class="symbol"&gt;:all&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;,&lt;/span&gt; &lt;span style="color: #b2655b" class="symbol"&gt;:joins&lt;/span&gt; &lt;span style="color: #666666; font-weight: bold" class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #666666; font-weight: bold" class="punct"&gt;[&lt;/span&gt;&lt;span style="color: #b2655b" class="symbol"&gt;:ratings&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;],&lt;/span&gt; &lt;span style="color: #b2655b" class="symbol"&gt;:order&lt;/span&gt; &lt;span style="color: #666666; font-weight: bold" class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #666666; font-weight: bold" class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #62b060" class="string"&gt;rating DESC&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span style="color: #b2655b" class="symbol"&gt;:limit&lt;/span&gt; &lt;span style="color: #666666; font-weight: bold" class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #b2655b" class="number"&gt;10&lt;/span&gt;&lt;span style="color: #666666; font-weight: bold" class="punct"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Assuming N is the number of songs in the database in the first case you need to load N object instances and issue N + 1 queries to load information about the rating of a specific song. In the second case 1 query is needed and all the filtering is delegated to the database. The nice thing is also the way the SQL string is hidden beautifully in the DSL-ish method call which is very similar to the first version. I remember the old days (for me!) in 2002 when this was just something like connection.executeStatement(&amp;ldquo;SELECT * BLAH&amp;hellip;&amp;rdquo;) or something and I had to create my own SQLBuilder to reuse and test some of that SQL. O/R mappings tools and fluent interfaces of today are just a huge leap forward.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The take away for the Rails developer: think twice whenever you write or see a find(:all) with no options. Look carefully the console output to see what SQL Rails is generating and how long does it take. If the result set it operates on is reasonably small, no big deal. If the collection is huge, please delegate to the database.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Hey, by the way, do you see what the query is doing? :)&lt;/div&gt;</description>
      <pubDate>Fri, 12 Dec 2008 23:24:00 -0500</pubDate>
      <link>http://railsloft.com/posts/what-a-difference-a-line-makes</link>
      <guid>http://railsloft.com/posts/what-a-difference-a-line-makes</guid>
      <author>Renzo Borgatti</author>
    </item>
    <item>
      <title>Poor Men Story Runner</title>
      <description>&lt;p&gt;Say that for some reasons like for example time constraints on the project, lack of customer collaboration or lack of business analysts you can&amp;rsquo;t maintain a full fledged automated story base. In that case one option could be to maintain the story runner format to describe the feature to implement but not try to make that executable. I know, I know, what a waste for such a great opportunity. But I&amp;rsquo;m talking about a desperate case.&lt;br /&gt;&lt;br /&gt;As funky as it sounds, you can try to implement the plain text story in rspec as controller specs with integrated view. For example:&lt;/p&gt;&lt;div class="code"&gt;     &lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt; 1&lt;/span&gt;  &lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="constant"&gt;BlogsController&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;blog details page&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 2&lt;/span&gt;    &lt;span class="ident"&gt;integrate_views&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 3&lt;/span&gt;    &lt;br /&gt;&lt;span class="line_number"&gt; 4&lt;/span&gt;    &lt;span class="ident"&gt;before&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:each&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 5&lt;/span&gt;      &lt;span class="constant"&gt;Given&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;the user is logged in&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 6&lt;/span&gt;      &lt;span class="constant"&gt;When&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;I click on a blog post&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 7&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 8&lt;/span&gt;    &lt;br /&gt;&lt;span class="line_number"&gt; 9&lt;/span&gt;    &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="constant"&gt;Then&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;the blog detail view is displayed&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;10&lt;/span&gt;    &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="constant"&gt;And&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;the full text of the blog is shown&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;11&lt;/span&gt;    &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="constant"&gt;And&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;the comment list section is shown&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;12&lt;/span&gt;      &lt;br /&gt;&lt;span class="line_number"&gt;13&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;14&lt;/span&gt;  &lt;br /&gt;&lt;span class="line_number"&gt;15&lt;/span&gt;  &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:Given&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:When&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:Then&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:And&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;method_name&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;16&lt;/span&gt;    &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;class&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;send&lt;/span&gt; &lt;span class="symbol"&gt;:define_method&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;method_name&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;what&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;17&lt;/span&gt;      &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;what&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;gsub&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt; &lt;/span&gt;&lt;span class="punct"&gt;','&lt;/span&gt;&lt;span class="string"&gt;_&lt;/span&gt;&lt;span class="punct"&gt;'))&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;18&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;19&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;   &lt;/div&gt;&lt;p&gt;Yes, very very not-readable. It just destroy all the effort to create stories in plain text. But it&amp;rsquo;s something a developer can read. The next step is to create a method for each of the descriptive texts, replacing blanks with underscores:&lt;/p&gt;&lt;div class="code"&gt;     &lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt; 1&lt;/span&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;the_user_is_logged_in&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 2&lt;/span&gt;    &lt;span class="attribute"&gt;@user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;login&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 3&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 4&lt;/span&gt;  &lt;br /&gt;&lt;span class="line_number"&gt; 5&lt;/span&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;I_click_on_a_blog_post&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 6&lt;/span&gt;    &lt;span class="attribute"&gt;@blog&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;stub_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Blog&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;br /&gt;&lt;span class="line_number"&gt; 7&lt;/span&gt;      &lt;span class="symbol"&gt;:title&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Random thoughts.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 8&lt;/span&gt;      &lt;span class="symbol"&gt;:comments&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;comments&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;br /&gt;&lt;span class="line_number"&gt; 9&lt;/span&gt;      &lt;span class="symbol"&gt;:text&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;MIDWAY upon the journey of our life&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;10&lt;/span&gt;    &lt;span class="constant"&gt;Blog&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should_receive&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:find_by_id&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;with&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;and_return&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@blog&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;11&lt;/span&gt;    &lt;span class="ident"&gt;get&lt;/span&gt; &lt;span class="symbol"&gt;:show&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:id&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;12&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;13&lt;/span&gt;  &lt;br /&gt;&lt;span class="line_number"&gt;14&lt;/span&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;the_blog_detail_view_is_displayed&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;15&lt;/span&gt;    &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;have_tag&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;div#blog&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;16&lt;/span&gt;      &lt;span class="ident"&gt;with_tag&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;strong&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="attribute"&gt;@blog&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;title&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;17&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;18&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;19&lt;/span&gt;  &lt;br /&gt;&lt;span class="line_number"&gt;20&lt;/span&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;the_full_text_of_the_blog_is_shown&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;21&lt;/span&gt;    &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;have_tag&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;div#blog&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;22&lt;/span&gt;      &lt;span class="ident"&gt;with_tag&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;div#text&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="attribute"&gt;@blog&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;text&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;23&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;24&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;25&lt;/span&gt;  &lt;br /&gt;&lt;span class="line_number"&gt;26&lt;/span&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;the_comment_list_section_is_shown&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;27&lt;/span&gt;    &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;have_tag&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;div#blog&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;28&lt;/span&gt;      &lt;span class="ident"&gt;with_tag&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;div#comments&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;29&lt;/span&gt;        &lt;span class="ident"&gt;with_tag&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;div#comment&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="number"&gt;5&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;30&lt;/span&gt;      &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;31&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;32&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;   &lt;/div&gt;&lt;p&gt;As you probably guessed already, this is the first scenario for a blog section on some web page. You can click on the &amp;ldquo;more&amp;rdquo; link to read more and access comments to the blog post. The plan is to implement the other scenarios in the same spec file changing the describe block to change the target controller. The specs are just scratching the surface of the view, then you&amp;rsquo;ll need to change spec to implement other part of the system.&lt;br /&gt;&lt;br /&gt;Another interesting thing is that for this to be called &amp;ldquo;integration&amp;rdquo; test, I shouldn&amp;rsquo;t have used any stub_model instruction. Correct. First step I want to create it fast, no database thinking. Second step I&amp;rsquo;ll remove all stubs and using some way of talking to the database (FixtureReplacement for example) I&amp;rsquo;ll be using real connections to the model and therefore to the database. At the end, a kind of acceptance testing.&lt;br /&gt;&lt;br /&gt;I know what you&amp;rsquo;d like to ask now: is this really any better than just plain run the Story Runner itself? Not at all. It&amp;rsquo;s absolutely a bad practice to use RSpec like this, I don&amp;rsquo;t want to suggest in any way that this can substitute the story runner. But in case of emergency (read, fixed scope, fixed price, fixed time projects) where the customer is not helping writing stories, where there are no business analysts and everything should be done by developers, dropping the story runner is probably the less of all possible pains. You should also consider that if you are using Watir or Selenium on Rails connected to the story runner, the effort of maintaining such an environment sensible acceptance framework is big.&lt;br /&gt;&lt;br /&gt;Specifically:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You don&amp;rsquo;t need the infrastructure to run stories which is usually a separated task from running specs&lt;/li&gt;&lt;li&gt;You don&amp;rsquo;t need to maintain steps either&lt;/li&gt;&lt;li&gt;You don&amp;rsquo;t need to attach the story runner binary to the continuous integration, stories will run as normal specs.&lt;/li&gt;&lt;li&gt;You are free to maintain story texts outside the project, on a wiki or online somewhere.&lt;/li&gt;&lt;li&gt;Coverage with rcov is just part of the package. For stories you can&amp;rsquo;t know how well are you covering that given aspect of the application. At least, I thought about it and I don&amp;rsquo;t know how to deal with the problem.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;Well, hope you can forgive me for this post but find the idea useful for your project. &lt;/p&gt;</description>
      <pubDate>Thu, 30 Oct 2008 10:26:00 -0400</pubDate>
      <link>http://railsloft.com/posts/poor-men-story-runner</link>
      <guid>http://railsloft.com/posts/poor-men-story-runner</guid>
      <author>Renzo Borgatti</author>
    </item>
    <item>
      <title>matchers_in_have_tag_blocks.should be(/with_tag/)</title>
      <description>&lt;h2&gt;The Setup&lt;/h2&gt; &lt;p&gt;When writing test code for Rails apps, I prefer Rspec.&amp;nbsp; For me the spec definition syntax feels more natural.&amp;nbsp; Though, the other day I got tripped up by the Rspec equivalent of assert_select.&amp;nbsp; With standard rails tests, the assert_select method can be nested to help ensure properly structured html elements.&lt;/p&gt; &lt;div class="code"&gt;     &lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt;1&lt;/span&gt;  &lt;span class="comment"&gt;# This tests that both users show up in the yes_list.&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;2&lt;/span&gt;  &lt;span class="comment"&gt;# As opposed to the no_list I guess.&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;3&lt;/span&gt;  &lt;span class="ident"&gt;assert_select&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;div#yes_list&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;4&lt;/span&gt;    &lt;span class="ident"&gt;assert_select&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;div#user&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="expr"&gt;#{@user1}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;5&lt;/span&gt;    &lt;span class="ident"&gt;assert_select&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;div#user&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="expr"&gt;#{@user2}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;6&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;   &lt;/div&gt;&lt;p&gt;Rspec uses have_tag as a wrapper to this method, but uses with_tag and without_tag to wrap the method when called inside the nesting.&amp;nbsp; The without_tag allows for simpler negative assertions, in case we wanted to test that user3 was not in the list for example.&lt;/p&gt;&lt;div class="code"&gt;     &lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt;1&lt;/span&gt;  &lt;span class="comment"&gt;# This also tests that both users show up in the yes_list.&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;2&lt;/span&gt;  &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;have_tag&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;div#yes_list&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;3&lt;/span&gt;    &lt;span class="ident"&gt;with_tag&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;div#user&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:text&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="expr"&gt;#{@user1}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;/})&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;4&lt;/span&gt;    &lt;span class="ident"&gt;with_tag&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;div#user&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:text&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="expr"&gt;#{@user2}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;/})&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;5&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt; &lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;   &lt;/div&gt;&lt;p&gt;Once you know this, I think the spec code looks very clear.&amp;nbsp; Unfortunately, calling have_tag within a have_tag block won't raise an error.&amp;nbsp; There's no mechanism that will warn you that your inner have_tag is ineffective.&amp;nbsp; The results of the inner have_tag will simply be ignored.&lt;/p&gt;&lt;h2&gt;The Pitfall&lt;/h2&gt;&lt;p&gt;When I first wrote the above spec, I used have_tag and have_text instead of with_tag.&amp;nbsp; And to my na&amp;iuml;ve eyes, everything looked great.&amp;nbsp; The code ran and the tests passed.&amp;nbsp; Done and done.&lt;/p&gt;&lt;div class="code"&gt;     &lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt; 1&lt;/span&gt;  &lt;span class="comment"&gt;# This merely asserts the existence of a yes_list,&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 2&lt;/span&gt;  &lt;span class="comment"&gt;#   no matter who's on the list.&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 3&lt;/span&gt;  &lt;span class="comment"&gt;# Here at the Rails Loft, we're more discerning than that.&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 4&lt;/span&gt;  &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;have_tag&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;div#yes_list&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 5&lt;/span&gt;    &lt;span class="ident"&gt;have_tag&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;div#user&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 6&lt;/span&gt;      &lt;span class="ident"&gt;have_text&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="expr"&gt;#{@user1}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;/)&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 7&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 8&lt;/span&gt;    &lt;span class="ident"&gt;have_tag&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;div#user&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 9&lt;/span&gt;      &lt;span class="ident"&gt;have_text&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="expr"&gt;#{@user2}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;/)&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;10&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;11&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;   &lt;/div&gt;&lt;p&gt;This was completely wrong.&amp;nbsp; The tests were passing, but they still passed when I removed @user1 from the yes_list one.&amp;nbsp; When used inside another have_tag block, a have_tag's assertions was completely ignored.&amp;nbsp; I think this had something to do with the inner have_tag setting up a new AssertSelect object that then got ignored by the outer AssertSelect object.&amp;nbsp; The reason for my misunderstanding doesn't really matter, though.&amp;nbsp; What matters is that I thought everything was fine; all the tests were passing after all.&amp;nbsp; Luckily, I fiddled with the code a bit before committing, and discovered that reversing an important condition in my application code didn't affect my test one bit.&amp;nbsp; Whoops.&amp;nbsp; This was a mistake that continuous integration and testing metrics could have never caught, which emphasized to me how important it is to be thorough and careful when writing tests.&lt;/p&gt;&lt;h2&gt;The Lesson&lt;/h2&gt;&lt;p&gt;Always, always, always make sure your tests fail before you make them pass.&amp;nbsp; Your tests are there to ensure proper functionality of application code, and this is the only way you can be certain your test code is doing what you expect.&amp;nbsp; This doesn't necessarily mean you always have to use test-first development, though you should certainly give TDD a try.&amp;nbsp; Making your tests fails could be as simple as toggling a conditional in your application code or changing the id of a tag against which you're matching.&amp;nbsp; This way you'll be confident that your tests are truly working the way they appear to work. &lt;/p&gt;</description>
      <pubDate>Wed, 15 Oct 2008 10:00:00 -0400</pubDate>
      <link>http://railsloft.com/posts/matchers_in_have_tag_blocks-should-be-with_tag</link>
      <guid>http://railsloft.com/posts/matchers_in_have_tag_blocks-should-be-with_tag</guid>
      <author>Phil Matarese</author>
    </item>
    <item>
      <title>Thinking in Legacy</title>
      <description>&lt;p&gt;If you get my &lt;a href="http://www.mindview.net/Books/TIJ/"&gt;reference&lt;/a&gt; in the title of this post, then you have probably been around for a while and may have fallen into the thought trap I did the other day.&amp;nbsp; Those of you that don't know Bruce Eckel's book probably are free from the &amp;quot;experience&amp;quot; (more accurately described as &amp;quot;baggage&amp;quot;) that causes people to think in &lt;a href="http://blog.thinkrelevance.com/2008/4/1/ending-legacy-code-in-our-lifetime"&gt;legacy&lt;/a&gt; patterns.&lt;/p&gt;&lt;p&gt;I was working on the metrics reporting section of a Rails application recently.&amp;nbsp; We needed to display a value which was derived by summing up a subset of the rows in a metrics table.&amp;nbsp; The metaphorical lightbulb went off over my head - I can do all this work in the database and just return the final result to my application!&lt;/p&gt;&lt;p&gt;So, I got busy and when I was done I had a a perfectly ugly SQL string using the &lt;a href="http://www.w3schools.com/sql/func_sum.asp"&gt;sum&lt;/a&gt; function hardcoded into my model.&amp;nbsp; I then used the always nice call of &amp;quot;ActiveRecord::Base.connection.execute(hardcoded_sql_string)&amp;quot; to get my result back.&amp;nbsp; I thought job well done and moved on with my life.&lt;/p&gt;         &lt;p&gt;&lt;img src="/content_images/50/univac2-small.gif" alt="Big Iron" title="Big Iron" hspace="10" width="213" height="159" align="right" /&gt; &lt;/p&gt;&lt;p&gt;Well, one of my less scarred co-workers saw the commit email go through and asked me why I was tainting the project with such hideous code.&amp;nbsp; Well, I proudly proclaimed, thinking I was about to teach a lesson, you always want to push the processing as far down the stack as possible - after all, those database boxes are Big Iron.&amp;nbsp; In addition, if you do the calculation in the database, you don't have to push all the rows over the wire in order to do a simple calculation in the app. &lt;/p&gt;&lt;p&gt;But, Scott, my co-worker exclaimed, the database is on the same box as the application server.&amp;nbsp; And as he said it, I realized I had pre-optimized based on a set of assumptions that didn't make sense for this application.&amp;nbsp; This application wasn't in some bloated enterprise infrastructure where there was a huge Oracle box lurking in the background with a firewall or two between it and the application server.&amp;nbsp; So, we rewrote the code in a more Railsy way, pulling back the data and populating the objects and iterating through them to arrive at our results, and, lo and behold, there wasn't a performance difference between the two methods - and the Rails way was much easier on the eyes and for any future developers to understand.&lt;/p&gt;&lt;p&gt;I'm not advocating that you never drop down to SQL in your Rails apps.&amp;nbsp; ActiveRecord is definitely a &lt;a href="http://www.joelonsoftware.com/articles/LeakyAbstractions.html"&gt;leaky abstraction&lt;/a&gt; (though a handy one) and it is well known that you can get into performance problems quickly if you don't understand the queries that are being made behind the scenes.&amp;nbsp; However, in this particular case, I had no reason to drop down an abstraction level to direct SQL.&amp;nbsp; I gained nothing and made the code less maintainable.&amp;nbsp; This specific example really just made me realize that we all carry around internalized assumptions that can turn out to not be valid for all situations.&amp;nbsp; It's good to remember this as we move from project to project, language to language, infrastructure to infrastructure, and environment to environment. &lt;/p&gt;</description>
      <pubDate>Wed, 21 May 2008 14:15:00 -0400</pubDate>
      <link>http://railsloft.com/posts/thinking-in-legacy</link>
      <guid>http://railsloft.com/posts/thinking-in-legacy</guid>
      <author>Scott Roth</author>
    </item>
    <item>
      <title>Setting up Multiple Site Instances on Joyent Accelerators</title>
      <description>&lt;p&gt;I recently set up a new Rails site on a Joyent Accelerator for a client.&amp;nbsp; The accelerator size selected had more than enough capacity, so we decided to set up the staging instance of the site on the same box.&amp;nbsp; There is a great &lt;a href="http://wiki.joyent.com/accelerators:deploying_rails_apps"&gt;set of instructions&lt;/a&gt; on the Joyent wiki for how to properly set up a Rails site that will be deployed via Capistrano to an Accelerator, so start there if you've never set up a site on Joyent before.&amp;nbsp; There are actually very few tweaks to those instructions you need to follow to set up multiple site instances on one Accelerator - in my case a &amp;quot;staging&amp;quot; and a &amp;quot;production&amp;quot; environment for a single application.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;mongrel_cluster.yml&lt;/strong&gt;&lt;br /&gt;The default place to store this file is under /config.&amp;nbsp; We are actually going to set up a separate mongrel file for each environment, so create a file called mongrel_cluster_[environment].yml&amp;nbsp; (e.g. mongrel_cluster_staging.yml) under /config/accelerator/environments in your Rails application.&amp;nbsp; Set the number of servers, the port, and the environment variables as you would normally.&amp;nbsp; We'll be moving this file back to its expected location during the deployment process.&lt;/p&gt;&lt;p&gt;Now copy over the three files mentioned on the wiki into the /accelerator folder and make the needed changes from the instructions.&amp;nbsp; We'll just be adding a few additional modifications.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;accelerator_tasks.rb&lt;/strong&gt;&lt;br /&gt;Since we have moved the location of the mongrel configuration file we need to let the &amp;quot;create_vhost&amp;quot; task know where to read in the YAML file from as it uses variables from that file to properly configure apache.&amp;nbsp; So update this line:&lt;/p&gt;&lt;div class="code"&gt;     &lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt;1&lt;/span&gt;  &lt;span class="ident"&gt;cluster_info&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;br /&gt;   &lt;span class="constant"&gt;YAML&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;load&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;read&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;config/accelerator/environments/mong&lt;br /&gt;   rel_cluster_&lt;span class="expr"&gt;#{application}&lt;/span&gt;.yml&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;   &lt;/div&gt;      &lt;!-- &lt;textarea class="formatted-html" rows="4"&gt;&lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt;1&lt;/span&gt;  &lt;span class="ident"&gt;cluster_info&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt;     &lt;span class="constant"&gt;YAML&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;load&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;read&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;config/accelerator/environments/mong    rel_cluster_&lt;span class="expr"&gt;#{application}&lt;/span&gt;.yml&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/textarea&gt; --&gt;   &lt;p&gt;&lt;strong&gt;apache_vhost.erb&lt;/strong&gt;&lt;br /&gt;No changes needed.&amp;nbsp; W00t!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;smf_template.erb&lt;/strong&gt;&lt;br /&gt;This file is the template used to set up all the dependant services that your Rails app will need.&amp;nbsp; The problem with the default version is that the mongrel service needs to have a distinct name for each application that is set up on the box otherwise bad conflicts happen.&amp;nbsp; (This is also mentioned on the Joyent wiki page.)&amp;nbsp; Update this line to make sure you have a distinct name for each environment:&lt;/p&gt;&lt;div class="code"&gt;     &lt;div class="syntax syntax_xml"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt;1&lt;/span&gt;  &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt;&lt;span class="tag"&gt;dependent&lt;/span&gt; &lt;span class="attribute"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;='&lt;/span&gt;&lt;span class="string"&gt;mongrel_multi-user_&amp;lt;%= application %&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;br /&gt;   &lt;span class="attribute"&gt;restart_on&lt;/span&gt;&lt;span class="punct"&gt;='&lt;/span&gt;&lt;span class="string"&gt;none&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="attribute"&gt;grouping&lt;/span&gt;&lt;span class="punct"&gt;='&lt;/span&gt;&lt;span class="string"&gt;optional_all&lt;/span&gt;&lt;span class="punct"&gt;'&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;   &lt;/div&gt;&lt;p&gt;Now we move onto the final step.&amp;nbsp; We have moved the location of the mongrel configuration file so that we can support multiples of that file in our source control system.&amp;nbsp; However, the mongrel service on the deployed server is still looking for that file in the old location with the old name.&amp;nbsp; So, we are going to add a simple task to &lt;strong&gt;deploy.rb&lt;/strong&gt; to fix this at deploy time.&amp;nbsp; At the bottom of your file add a new custom task:&lt;/p&gt;&lt;div class="code"&gt;     &lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt;1&lt;/span&gt;  &lt;span class="ident"&gt;namespace&lt;/span&gt; &lt;span class="symbol"&gt;:custom&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;2&lt;/span&gt;    &lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:movemongrelconfig&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:roles&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:app&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;3&lt;/span&gt;      &lt;span class="comment"&gt;# Move the env specific file to the general location&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;4&lt;/span&gt;      &lt;span class="ident"&gt;run&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;mv &lt;br /&gt;       &lt;span class="expr"&gt;#{release_path}&lt;/span&gt;/config/accelerator/environments/mongr&lt;br /&gt;       el_cluster_&lt;span class="expr"&gt;#{application}&lt;/span&gt;.yml &lt;br /&gt;       &lt;span class="expr"&gt;#{release_path}&lt;/span&gt;/config/mongrel_cluster.yml&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;5&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;6&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;   &lt;/div&gt;      &lt;!-- &lt;textarea class="formatted-html" rows="4"&gt;&lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt;1&lt;/span&gt;  &lt;span class="ident"&gt;namespace&lt;/span&gt; &lt;span class="symbol"&gt;:custom&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="line_number"&gt;2&lt;/span&gt;    &lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:movemongrelconfig&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:roles&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:app&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="line_number"&gt;3&lt;/span&gt;      &lt;span class="comment"&gt;# Move the env specific file to the general location&lt;/span&gt; &lt;span class="line_number"&gt;4&lt;/span&gt;      &lt;span class="ident"&gt;run&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;mv         &lt;span class="expr"&gt;#{release_path}&lt;/span&gt;/config/accelerator/environments/mongr        el_cluster_&lt;span class="expr"&gt;#{application}&lt;/span&gt;.yml         &lt;span class="expr"&gt;#{release_path}&lt;/span&gt;/config/mongrel_cluster.yml&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="line_number"&gt;5&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="line_number"&gt;6&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/textarea&gt; --&gt;   &lt;p&gt;And call the new task at the end of the deployment process:&lt;/p&gt;&lt;div class="code"&gt;     &lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt;1&lt;/span&gt;  &lt;span class="ident"&gt;after&lt;/span&gt; &lt;span class="symbol"&gt;:deploy&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;custom:movemongrelconfig&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;   &lt;/div&gt;&lt;p&gt;I'm sure that there are other ways to get multiple site instances running, but this one was relatively simple and has been working well for us for some time now. &lt;/p&gt;</description>
      <pubDate>Wed, 14 May 2008 14:13:00 -0400</pubDate>
      <link>http://railsloft.com/posts/setting-up-multiple-site-instances-on-joyent-accelerators</link>
      <guid>http://railsloft.com/posts/setting-up-multiple-site-instances-on-joyent-accelerators</guid>
      <author>Scott Roth</author>
    </item>
    <item>
      <title>Making FTP Code Compatible With SFTP Servers</title>
      <description>&lt;h2&gt;Why?&lt;/h2&gt;&lt;p&gt;We recently needed to toggle between support for SFTP and traditional FTP in one of our applications.&amp;nbsp; In a Rails app, this is more of an annoyance than necessary because of the interface differences between &lt;a href="http://www.ruby-doc.org/stdlib/libdoc/net/ftp/rdoc/index.html"&gt;Net::FTP&lt;/a&gt; and &lt;a href="http://net-ssh.rubyforge.org/sftp/faq.html"&gt;Net::SFTP&lt;/a&gt;.&amp;nbsp; Not only are the transfer and navigation methods arbitrarily different, but the way the current directory on the server is dealt with is not consistent.&amp;nbsp; I suspect these differences grow from the underlying differences in the technologies, but it would still be nice to code just one branch of code that does file transfers for either type of server.&amp;nbsp; This is Ruby, right?&lt;/p&gt;&lt;h2&gt;What?&lt;/h2&gt;&lt;p&gt;This was the impetus for writing the &lt;a href="http://rubyforge.org/projects/sftp-compat/"&gt;sftp-compat&lt;/a&gt; plugin to extend Net::SFTP, giving it all the same interfaces as Net::FTP.&amp;nbsp; The plugin is very new, but it was extracted from a real, working project we developed.&amp;nbsp; It's functional, and it's being used in production right now as we speak (rather as I write and as you read - nobody is speaking right now - it's actually very quiet here - nice and tranquil - mmm.)&lt;/p&gt;&lt;h2&gt;How?&lt;/h2&gt;&lt;p&gt;All of the documentation can be found at &lt;a href="http://sftp-compat.rubyforge.org/"&gt;http://sftp-compat.rubyforge.org/&lt;/a&gt;.&amp;nbsp; There may be some functionality missing from the interface depending on your use case.&amp;nbsp; The current version was designed for writing to remote FTP hosts, so there are a few methods for reading that I never needed to implement.&amp;nbsp; If you are interested in using sftp-compat, but need the missing functions or would like to commit extra functions, please drop me an email.&lt;/p&gt;</description>
      <pubDate>Wed, 30 Apr 2008 17:30:00 -0400</pubDate>
      <link>http://railsloft.com/posts/making-ftp-code-compatible-with-sftp-servers</link>
      <guid>http://railsloft.com/posts/making-ftp-code-compatible-with-sftp-servers</guid>
      <author>Phil Matarese</author>
    </item>
    <item>
      <title>Cinema Redux Redux</title>
      <description>&lt;p&gt;After the &lt;a href="/posts/create-your-own-cinema-redux-poster-with-ruby-and-rmagick"&gt;Cinema Redux&lt;/a&gt; presentation at &lt;a href="http://2008.goruco.com/"&gt;GoRuCo&lt;/a&gt; was posted at &lt;a href="http://goruco2008.confreaks.com/07_various.html"&gt;ConFreaks&lt;/a&gt;, I couldn't resist reduxing the redux.&amp;nbsp; I mean, this is totally meta.&amp;nbsp; And, meta goes with Ruby like analogies go with blog posts.&amp;nbsp; So, here it is... Cinema Redux Redux.&lt;/p&gt;&lt;div align="center"&gt;&lt;div style="padding-top: 15px; padding-right: 18px; padding-bottom: 15px; padding-left: 18px; width: 314px; background-color: #000000; font-size: 0.9em; color: #bbbbbb; text-align: center"&gt;            &lt;div style="float: left; width: 150px; margin-right: 7px"&gt;&lt;a rel="lightbox" href="/content_images/46/goruco.500x750.png" title="Cinema Redux at GoRuCo"&gt;&lt;img src="/content_images/47/goruco.150x225.png" alt="Redux Redux" width="150" height="225" /&gt;&lt;/a&gt;Cinema Redux at GoRuCo&lt;/div&gt;   &lt;div style="float: left; width: 150px; margin-right: 7px"&gt;&lt;a rel="lightbox" href="/content_images/48/goruco_detail.png" title="Cinema Redux (Detail)"&gt;&lt;img src="/content_images/49/goruco_detail_thumb.png" alt="Redux Redux" width="150" height="225" /&gt;&lt;/a&gt;Cinema Redux (Detail)&lt;/div&gt;      &lt;div class="clear"&gt;&amp;nbsp;&lt;/div&gt;  &lt;/div&gt;&lt;/div&gt;</description>
      <pubDate>Wed, 30 Apr 2008 16:45:00 -0400</pubDate>
      <link>http://railsloft.com/posts/cinema-redux-redux</link>
      <guid>http://railsloft.com/posts/cinema-redux-redux</guid>
      <author>Phil Matarese</author>
    </item>
    <item>
      <title>Create Your Own Cinema Redux Poster with Ruby and RMagick</title>
      <description>&lt;p&gt;A few years back, the talented &lt;a href="http://www.brendandawes.com"&gt;Brendan Dawes&lt;/a&gt; created &lt;a href="http://www.brendandawes.com/sketches/redux/"&gt;Cinema Redux&lt;/a&gt;, which lays out a film as a series of stills captured at 1 frame per second.  The result is a matrix of images resembling a DNA print of the film.  By arranging these stills into rows of 1 minute, you get an interesting overview of how a film flows from start to finish.&lt;/p&gt;&lt;p&gt;While there are still some prints available for sale - &lt;a href="http://coudal.com/archives/2007/06/brendan_dawes_c.php"&gt;2001: A Space Odyssey is available through Coudal Partners&lt;/a&gt; - I wanted to do my own. Here are a few:  &lt;/p&gt;&lt;div style="padding: 15px 18px; width: 464px; background-color: #000000; font-size: 0.9em; color: #bbbbbb; text-align: center"&gt;            &lt;div style="float: left; width: 150px; margin-right: 7px"&gt;&lt;a rel="lightbox" href="/content_images/27/montage-2001.jpg" title="2001: A Space Odyssey"&gt;&lt;img src="/content_images/26/montage-2001-thumb.jpg" alt="2001: A Space Odyssey" width="150" height="225" /&gt;&lt;/a&gt;2001: A Space Odyssey&lt;/div&gt;       &lt;div style="float: left; width: 150px; margin-right: 7px"&gt;&lt;a rel="lightbox" href="/content_images/33/montage-blood.jpg" title="There Will Be Blood"&gt;&lt;img src="/content_images/32/montage-blood-thumb.jpg" alt="There Will Be Blood" width="150" height="225" /&gt;&lt;/a&gt;There Will Be Blood&lt;/div&gt;  &lt;div style="float: left; width: 150px; margin-right: 0pt"&gt;&lt;a rel="lightbox" href="/content_images/40/montage-strangelove.jpg" title="Dr. Strangelove"&gt;&lt;img src="/content_images/39/montage-strangelove-thumb.jpg" alt="Dr. Strangelove" width="150" height="225" /&gt;&lt;/a&gt;Dr. Strangelove&lt;/div&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;div style="float: left; width: 150px; margin-right: 7px"&gt;&lt;a rel="lightbox" href="/content_images/31/montage-helvetica.jpg" title="Helvetica: A documentary by Gary Hustwit"&gt;&lt;img src="/content_images/30/montage-helvetica-thumb.jpg" alt="Helvetica" /&gt;&lt;/a&gt; Helvetica&lt;br /&gt;by Gary Hustwit  &lt;/div&gt;   &lt;div style="float: left; width: 150px; margin-right: 7px"&gt;&lt;a rel="lightbox" href="/content_images/45/montage-mario.jpg" title="Super Mario 1, 2, 3, World &amp;amp; 64 speed runs"&gt;&lt;img src="/content_images/44/montage-mario-thumb.jpg" alt="Super Mario 1, 2, 3, World &amp;amp; 64 speed runs" /&gt;&lt;/a&gt; Super Mario 1, 2, 3, &lt;br /&gt;World &amp;amp; 64 speed runs  &lt;/div&gt;  &lt;div style="float: left; width: 150px; margin-right: 0pt"&gt;&lt;a rel="lightbox" href="/content_images/35/montage-incredibles.jpg" title="The Incredibles"&gt;&lt;img src="/content_images/34/montage-incredibles-thumb.jpg" alt="The Incredibles" /&gt;&lt;/a&gt; The Incredibles&lt;/div&gt;  &lt;div class="clear"&gt;&amp;nbsp;&lt;/div&gt;  &lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;  &lt;p style="background-color: #000000; color: #cccccc; text-align: center; padding-bottom: 10px; font-size: 0.9em"&gt;&lt;a rel="lightbox" href="/content_images/42/montage-2001-detail.jpg" title="Detail view of 2001: A Space Odyssey "&gt;&lt;img src="/content_images/41/montage-2001-detail-thumb.jpg" alt="Detail view of 2001: A Space Odyssey" /&gt;&lt;/a&gt;&lt;br /&gt;Detail view of 2001: A Space Odyssey&lt;/p&gt;&lt;h2&gt;What the Script Does&lt;/h2&gt;&lt;p&gt;montage.rb has 2 major processes.&amp;nbsp; First, it reads each of the rendered stills and resizes them to computed size (based on configuration options), and writes them to a subdirectory.&amp;nbsp; Next, it reads in these scaled images into an &lt;a href="http://www.imagemagick.org/RMagick/doc/ilist.html"&gt;RMagick ImageList&lt;/a&gt; - using the &lt;a href="http://www.imagemagick.org/RMagick/doc/ilist.html#montage"&gt;montage method&lt;/a&gt; - then writes the resulting file.&amp;nbsp; The montage method would handle resizing for you, but the memory required to handle all frames from the movie at full resolution is prohibitively high.&amp;nbsp; Manually scaling the images allows for more efficient memory management. &lt;/p&gt;  &lt;h2&gt;Step 0&lt;/h2&gt;&lt;p&gt;Before you get started, you'll of course need &lt;a href="http://rmagick.rubyforge.org/"&gt;RMagick 2&lt;/a&gt; (and thus &lt;a href="http://www.imagemagick.org/script/index.php"&gt;ImageMagick&lt;/a&gt;).&amp;nbsp; While I'm sure you could hack the script to run with RMagick 1.x, I strongly advise you don't.&amp;nbsp; RMagick 2 does a much better job of memory management, and supports the .destroy! method, allowing the script to free memory in the image resize loop.&amp;nbsp; RMagick 1.x caused all sorts of problems for me early on.&lt;/p&gt;&lt;h2&gt;Step 1: Download montage.rb&lt;br /&gt;&lt;/h2&gt;&lt;p&gt;That's it, just download &lt;a href="/content_images/43/montage.rb.gz"&gt;montage.rb&lt;/a&gt;.&amp;nbsp; Well, then unzip it.&lt;/p&gt;  &lt;h2&gt;Step 2: Export The Movie as a Series of Stills&lt;/h2&gt;  &lt;p&gt;Create a new directory and place in it your movie and montage.rb.  This next export step will create thousands of still images, so you might want to get a little organized.&lt;/p&gt;  &lt;p&gt; &lt;strong&gt;If you don't have QuickTime Pro:&lt;/strong&gt;&lt;br /&gt;You'll need &lt;a href="http://ffmpeg.mplayerhq.hu/"&gt;FFmpeg&lt;/a&gt;.  The command you'll use will be similar to: &lt;/p&gt;&lt;p&gt;ffmpeg -i YOUR_MOVIE.avi -r 1 -f image2 movie_%4d.png&amp;nbsp;&lt;/p&gt;&lt;p&gt;Once this is done, you can skip to Step 3.&amp;nbsp;&lt;/p&gt;  &lt;p&gt; &lt;strong&gt;If you do have QuickTime Pro:&lt;/strong&gt;&lt;br /&gt; Select &amp;quot;&lt;strong&gt;File -&amp;gt; Export...&lt;/strong&gt;&amp;quot;&lt;/p&gt;&lt;p&gt;From the Export select menu, chose &amp;quot;&lt;strong&gt;Movie to Image Sequence&lt;/strong&gt;&amp;quot;&lt;br /&gt;&lt;img style="border: 2px solid #dddddd" src="/content_images/36/montage-export-1.jpg" alt="" /&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;  &lt;p&gt; &lt;strong&gt;Click the Options button&lt;/strong&gt;, and use the following settings&lt;br /&gt;&lt;img style="border: 2px solid #dddddd" src="/content_images/37/montage-export-2.jpg" alt="" /&gt;&lt;br /&gt;(For these options, I chose Best Depth and Medium Quality)&lt;/p&gt;  &lt;p&gt; &lt;strong&gt;Choose your target directory and save&lt;/strong&gt;.&amp;nbsp; This step will create those few thousand still images, so make sure you're putting them in the newly created directory.&lt;/p&gt;  &lt;h2&gt;Step 3: Choose montage.rb options&amp;nbsp;&lt;/h2&gt;&lt;p&gt;Run montage.rb without any arguments to get a listing of the usage options.&amp;nbsp; The only required argument is the file pattern that will match against the movie stills.&lt;/p&gt;&lt;h2&gt;What Now?&lt;/h2&gt;&lt;p&gt;I went with &lt;a href="http://www.qoop.com/"&gt;QOOP&lt;/a&gt; as a printing service, and chose a 20&amp;quot; x 30&amp;quot; format.&amp;nbsp; Some jpeg compression was in order to get the filesize below their threshold, but not much. The print is high quality and I'm quite pleased.&lt;/p&gt;&lt;p&gt;Note that if you're into &lt;a href="http://www.processing.org/"&gt;Processing&lt;/a&gt;, you can find a sketch that appears to do similar work &lt;a href="http://plasticbugs.com/index.php?p=278"&gt;here&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Fri, 25 Apr 2008 21:44:00 -0400</pubDate>
      <link>http://railsloft.com/posts/create-your-own-cinema-redux-poster-with-ruby-and-rmagick</link>
      <guid>http://railsloft.com/posts/create-your-own-cinema-redux-poster-with-ruby-and-rmagick</guid>
      <author>John Berry</author>
    </item>
    <item>
      <title>Rspec Without db:test:prepare</title>
      <description>&lt;h2&gt;First Things First&lt;/h2&gt;&lt;p&gt;&lt;a href="http://www.flickr.com/photos/philipmatarese/2125095903/" title="At the Hotel Matarese by philipmatarese, on Flickr"&gt;&lt;img class="bordered-right" src="http://farm3.static.flickr.com/2101/2125095903_5d2e8277bd_m.jpg" alt="At the Hotel Matarese" width="240" height="180" align="right" /&gt;&lt;/a&gt;Once upon a time, after deploying an Rspec'd app for the first time, we wanted to run all the specs on our staging server to see that everything was running as expected.&amp;nbsp; Only, there was a problem with dependencies.&lt;/p&gt;&lt;p&gt;At the time we had a pretty gross, gem-dependency soup cooking up in our app.&amp;nbsp; We had &lt;code&gt;rspec&lt;/code&gt; as a frozen gem and &lt;code&gt;rspec_on_rails&lt;/code&gt; as a plugin, and this was causing (unconfirmed) load-order problems for rake.&amp;nbsp; After checking the rspec documentation again, we changed rspec from a gem to a plugin, which straightened everything out (I think). &lt;/p&gt;&lt;h2&gt;The Basics&lt;/h2&gt;&lt;p&gt;Basically, our current setup is to have &lt;a href="http://piston.rubyforge.org/usage.html"&gt;&lt;code&gt;piston&lt;/code&gt;&lt;/a&gt; (essential for plugin management), &lt;a href="http://eigenclass.org/hiki.rb?rcov"&gt;&lt;code&gt;rcov&lt;/code&gt;&lt;/a&gt; (keep the coverage above 50% or the bus blows up), and &lt;a href="http://www.zenspider.com/ZSS/Products/ZenTest/"&gt;&lt;code&gt;ZenTest&lt;/code&gt;&lt;/a&gt; (because autotest is fun) installed locally as gems on all development machines - there is no reason for us to use these on our production or staging servers.&amp;nbsp; Within the app we have &lt;code&gt;rspec_on_rails&lt;/code&gt;, &lt;code&gt;rspec_autotest&lt;/code&gt;, and &lt;code&gt;rails_rcov&lt;/code&gt; (which &lt;a href="http://svn.codahale.com/rails_rcov/tasks/rails_rcov.rake"&gt;uses magic&lt;/a&gt;) installed in the plugins directory (under the watchful eye of piston.)&amp;nbsp; These are part of the app, not only because the rspec docs like it that way, but because we'd like to ensure all the tests pass after a deployment.&amp;nbsp; Everything is all nice and neat now.&amp;nbsp; Shouldn't have anymore problems.&amp;nbsp; Nope.&amp;nbsp; No sir.&amp;nbsp; No way.&amp;nbsp; Uh-uh.&lt;/p&gt;&lt;h2&gt;The Problem&lt;/h2&gt;&lt;p&gt;&lt;a href="http://www.flickr.com/photos/philipmatarese/2022388756/" title="Gimmelwald, Population 130 by philipmatarese, on Flickr"&gt;&lt;img class="bordered-right" src="http://farm3.static.flickr.com/2249/2022388756_afe8b16fdb_m.jpg" alt="Gimmelwald, Population 130" width="240" height="180" align="right" /&gt;&lt;/a&gt;When running &lt;code&gt;rake spec&lt;/code&gt;, the database can't be found.&amp;nbsp; But... we installed it... right? Of course, it's right there.&amp;nbsp; And... the permissions? They're good, too.&amp;nbsp; Uhh... Hey, did that say the &amp;quot;development&amp;quot; database can't be found?&amp;nbsp; What the...&lt;br /&gt;&lt;br /&gt;Unbeknownst to us, the &lt;code&gt;spec&lt;/code&gt; task had as a prerequisite &lt;code&gt;db:test:prepare&lt;/code&gt;, which takes a schema_dump from the development database and uses it to create a test database.&amp;nbsp; This is something we don't need.&amp;nbsp; Unfortunately, there's no option or flag that you can use to disable this prerequisite.&lt;/p&gt;&lt;h2&gt;The Solution&lt;/h2&gt;&lt;p&gt;So this one got a little hairy.&amp;nbsp; The load order for rake tasks is &lt;code&gt;rails&lt;/code&gt;, custom code in &lt;code&gt;lib/tasks/*&lt;/code&gt;, and then &lt;code&gt;rspec&lt;/code&gt;.&amp;nbsp; So, I couldn't drop the &lt;code&gt;spec&lt;/code&gt; prerequisites with my custom code, because they didn't exist yet.&amp;nbsp; My first attempt to solve this involved wiping out &lt;code&gt;db:test:prepare&lt;/code&gt; completely, no matter what.&amp;nbsp; I was a bit hesitant to do this, because it had nothing to do with what I was really trying to accomplish &amp;ndash; I've got nothing against &lt;code&gt;db:test:prepare&lt;/code&gt;, I just want it decoupled from &lt;code&gt;rspec&lt;/code&gt;.&amp;nbsp; Here is our current solution which only wipes out &lt;code&gt;db:test:prepare&lt;/code&gt; when the spec task is invoked.&amp;nbsp; Not only does this solve the problem of testing on a staging server, but it also speeds up testing in our local development environments.&amp;nbsp; Enjoy.&lt;/p&gt;&lt;div id="code-format"&gt;    &lt;div class="code"&gt;     &lt;div class="syntax syntax_ruby"&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="line_number"&gt; 1&lt;/span&gt;  &lt;span class="comment"&gt;# bock_db_test_prepare.rb&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 2&lt;/span&gt;  &lt;br /&gt;&lt;span class="line_number"&gt; 3&lt;/span&gt;  &lt;span class="comment"&gt;# without this, you could never&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 4&lt;/span&gt;  &lt;span class="comment"&gt;# remove elements of a task&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 5&lt;/span&gt;  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Rake::Task&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 6&lt;/span&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;clear_it&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 7&lt;/span&gt;      &lt;span class="attribute"&gt;@actions&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;clear&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 8&lt;/span&gt;      &lt;span class="attribute"&gt;@prerequisites&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;clear&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt; 9&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;10&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;11&lt;/span&gt;  &lt;br /&gt;&lt;span class="line_number"&gt;12&lt;/span&gt;  &lt;span class="comment"&gt;# set up the blocking as a rake task,&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;13&lt;/span&gt;  &lt;span class="comment"&gt;# so we can add it as a prereq to spec&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;14&lt;/span&gt;  &lt;span class="ident"&gt;namespace&lt;/span&gt; &lt;span class="symbol"&gt;:ap&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;15&lt;/span&gt;    &lt;span class="ident"&gt;namespace&lt;/span&gt; &lt;span class="symbol"&gt;:spec&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;16&lt;/span&gt;      &lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:skip_db_test_prepare&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;17&lt;/span&gt;        &lt;span class="constant"&gt;Rake&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Task&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;db:test:prepare&lt;/span&gt;&lt;span class="punct"&gt;'].&lt;/span&gt;&lt;span class="ident"&gt;clear_it&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;18&lt;/span&gt;      &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;19&lt;/span&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;20&lt;/span&gt;  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;21&lt;/span&gt;  &lt;br /&gt;&lt;span class="line_number"&gt;22&lt;/span&gt;  &lt;span class="comment"&gt;# block it!&lt;/span&gt;&lt;br /&gt;&lt;span class="line_number"&gt;23&lt;/span&gt;  &lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:spec&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;ap:spec:skip_db_test_prepare&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;   &lt;/div&gt;&lt;/div&gt;&lt;p&gt;The final line works even though the &lt;code&gt;spec&lt;/code&gt; task doesn't exist yet.  Essentially, our custom code creates an empty &lt;code&gt;spec&lt;/code&gt; task with our single prerequisite.  When rspec loads, the real task and prerequisites will be appended &lt;em&gt;after&lt;/em&gt; ours.  Success.&lt;/p&gt;</description>
      <pubDate>Wed, 23 Apr 2008 14:30:00 -0400</pubDate>
      <link>http://railsloft.com/posts/rspec-without-db:test:prepare</link>
      <guid>http://railsloft.com/posts/rspec-without-db:test:prepare</guid>
      <author>Phil Matarese</author>
    </item>
  </channel>
</rss>

