<?xml version="1.0" encoding="ISO-8859-1"?>
<?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" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">

  <title>Jeff Kreeftmeijer</title>
  
  <link href="http://jeffkreeftmeijer.com/" />
  <updated>2013-04-29T16:20:52+02:00</updated>
  <id>http://jeffkreeftmeijer.com/</id>
  <author>
    <name>Jeff Kreeftmeijer</name>
  </author>

  
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/jeffkreeftmeijer" /><feedburner:info uri="jeffkreeftmeijer" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
    <title type="html">On the death of ifs</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/10IQQ51vZQM/on-the-death-of-ifs" />
    
    <id>http://jeffkreeftmeijer.com/2013/on-the-death-of-ifs</id>
    <updated>2013-04-29T00:00:00+02:00</updated>
    <summary>As a response to an article named &amp;ldquo;The death of ifs&amp;rdquo;, we'll try to refactor a method with a long if statement, without too much overengineering.</summary>
    <content type="html">
      
        &lt;p&gt;I came across &amp;#8220;&lt;a href='http://jumpstartlab.com/news/archives/2013/04/23/the-death-of-ifs'&gt;The Death of Ifs&lt;/a&gt;&amp;#8221; last week, in which &lt;a href='http://twitter.com/franklinwebber' title='Franklin Webber'&gt;@franklinwebber&lt;/a&gt; explains the steps he took when refactoring this piece of code to try to get rid of the if statement;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;process&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;input&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;q&amp;#39;&lt;/span&gt;
    &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Goodbye&amp;#39;&lt;/span&gt;
  &lt;span class='k'&gt;elsif&lt;/span&gt; &lt;span class='n'&gt;input&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;tweet&amp;#39;&lt;/span&gt;
    &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;tweeting&amp;#39;&lt;/span&gt;
  &lt;span class='k'&gt;elsif&lt;/span&gt; &lt;span class='n'&gt;input&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;dm&amp;#39;&lt;/span&gt;
    &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;direct messaging&amp;#39;&lt;/span&gt;
  &lt;span class='k'&gt;elsif&lt;/span&gt; &lt;span class='n'&gt;input&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;help&amp;#39;&lt;/span&gt;
    &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;helping&amp;#39;&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/5478520/9d509d6e1ce7e1550d89c2c58fbd2fdeeb862158'&gt;https://gist.github.com/5478520/9d509d&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;The video starts out by explaining that if statements like this one might be a sign of bad design, and that this piece of code will probably grow over time and become an eyesore. While I don&amp;#8217;t think the possibility of something growing over time and becoming an eyesore sometime in the future is a good reason to start making the code more extensible, I do believe that this code could use some refactoring.&lt;/p&gt;

&lt;p&gt;Franklin starts out by moving some of the code to a seperate class named &lt;code&gt;QuitCommand&lt;/code&gt;;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;QuitCommand&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;match?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;input&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;q&amp;#39;&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;exectute&lt;/span&gt;
    &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Goodbye&amp;#39;&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After creating a class for every one of the options in the if statement above, we have &lt;code&gt;QuitCommand&lt;/code&gt;, &lt;code&gt;TweetCommand&lt;/code&gt;, &lt;code&gt;DirectMessageCommand&lt;/code&gt; and &lt;code&gt;HelpCommand&lt;/code&gt;, and we can update the &lt;code&gt;#process&lt;/code&gt; method we started out with to look like this;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;process&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;quit&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;QuitCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;tweet&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;TweetCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;dm&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;DirectMessageCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;help&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;HelpCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;

  &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;quit&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;match?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; 
    &lt;span class='n'&gt;quit&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;execute&lt;/span&gt;
  &lt;span class='k'&gt;elsif&lt;/span&gt; &lt;span class='n'&gt;tweet&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;match?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; 
    &lt;span class='n'&gt;tweet&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;execute&lt;/span&gt;
  &lt;span class='k'&gt;elsif&lt;/span&gt; &lt;span class='n'&gt;dm&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;match?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;dm&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;execute&lt;/span&gt;
  &lt;span class='k'&gt;elsif&lt;/span&gt; &lt;span class='n'&gt;help&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;match?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;help&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;execute&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/5478520/7e6ad6774e44c17920caf9a126d4686ffa065189'&gt;https://gist.github.com/5478520/7e6ad6&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;As you can see, the if statement is still there, but now the commands have all been abstracted into their own classes, which splits responsibilities and allows you to test them in isolation and extend the execution of the commands without having to touch any other parts of our application.&lt;/p&gt;

&lt;p&gt;Franklin then gets to work on that pesky if statement;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;process&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;quit&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;QuitCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;tweet&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;TweetCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;dm&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;DirectMessageCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;help&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;HelpCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;

  &lt;span class='n'&gt;commands&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='n'&gt;quit&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;tweet&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;dm&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;help&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;

  &lt;span class='n'&gt;found_command&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;commands&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;command&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; 
    &lt;span class='n'&gt;command&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;match?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='n'&gt;found_command&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;execute&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/5478520/66770439c9beb7e63fea8f9dc3253486a312082e'&gt;https://gist.github.com/5478520/667704&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;By putting the command instances into an array and using &lt;code&gt;Enumerable#find&lt;/code&gt;, we can find the first instance that returns true from its &lt;code&gt;#match&lt;/code&gt; method to get the command we need. Then we can simply call &lt;code&gt;#execute&lt;/code&gt; on it.&lt;/p&gt;

&lt;p&gt;But, because &lt;code&gt;found_command&lt;/code&gt; could potentially be &lt;code&gt;nil&lt;/code&gt; if somebody passes an unknown value, Franklin creates a new command class he names &lt;code&gt;NoActionCommand&lt;/code&gt; that has a &lt;code&gt;#match&lt;/code&gt; method which always returns true, and an empty &lt;code&gt;#execute&lt;/code&gt; method. By putting it last in the commands list in &lt;code&gt;#process&lt;/code&gt;, it will always be chosen when the other command classes don&amp;#8217;t match. Because we can safely call &lt;code&gt;#execute&lt;/code&gt; on an instance of &lt;code&gt;NoActionCommand&lt;/code&gt;, the code will now work, even if &lt;code&gt;input&lt;/code&gt; is something unexpected;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;NoActionCommand&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;match?&lt;/span&gt;
    &lt;span class='kp'&gt;true&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;execute&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;process&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;quit&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;QuitCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;tweet&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;TweetCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;dm&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;DirectMessageCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;help&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;HelpCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;no_action&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;NoActionCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  
  &lt;span class='n'&gt;commands&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='n'&gt;quit&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;tweet&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;dm&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;help&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;no_action&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;

  &lt;span class='n'&gt;found_command&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;commands&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;command&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; 
    &lt;span class='n'&gt;command&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;match?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='n'&gt;found_command&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;execute&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/5478520/33215c0c993e361e68bab76568de7fa0a5afb26f'&gt;https://gist.github.com/5478520/33215c&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;There. No more if statements, and the code works exactly like before. Franklin splits the &lt;code&gt;#process&lt;/code&gt; method up some more in his video, but let&amp;#8217;s take another look to see if we can clean this up some more.&lt;/p&gt;

&lt;p&gt;For example, doesn&amp;#8217;t having to implement &lt;code&gt;NoActionCommand&lt;/code&gt; show that using &lt;code&gt;Enumerable#find&lt;/code&gt; is not the best solution to choose one of the commands? Wouldn&amp;#8217;t it be simpler and cleaner just to make sure &lt;code&gt;found_command&lt;/code&gt; is not &lt;code&gt;nil&lt;/code&gt; before trying to call &lt;code&gt;#execute&lt;/code&gt; on it? I&amp;#8217;d probably do something like this;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;process&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;quit&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;QuitCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;tweet&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;TweetCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;dm&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;DirectMessageCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;help&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;HelpCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;

  &lt;span class='n'&gt;commands&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='n'&gt;quit&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;tweet&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;dm&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;help&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;

  &lt;span class='n'&gt;found_command&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;command&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;command&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; 
    &lt;span class='n'&gt;command&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;match?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='n'&gt;found_command&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;execute&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;found_command&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/5478520/fe5a045c70b84c1c05eb7dcb7ca8f06fedcd6be1'&gt;https://gist.github.com/5478520/fe5a04&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;Not taking the if statement refactoring idea too seriously, we can get rid of our &lt;code&gt;NoActionCommand&lt;/code&gt; class again, which removes of a lot of dummy code we don&amp;#8217;t really need.&lt;/p&gt;

&lt;p&gt;Going a bit further, while I agree on moving the commands to their own separate classes when they&amp;#8217;ve grown to a size where that&amp;#8217;s sensible, I don&amp;#8217;t think it&amp;#8217;s a good idea to have those classes, which are in charge of &lt;em&gt;executing&lt;/em&gt; the commands, decide which command should be chosen for which input. Personally, I would keep the command selection in the &lt;code&gt;#process&lt;/code&gt; method and use an a bit more functional approach;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;process&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;commands&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='s1'&gt;&amp;#39;q&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='no'&gt;GoodbyeCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
    &lt;span class='s1'&gt;&amp;#39;tweet&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='no'&gt;TweetingCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
    &lt;span class='s1'&gt;&amp;#39;dm&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='no'&gt;DirectMessageCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
    &lt;span class='s1'&gt;&amp;#39;help&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='no'&gt;HelpingCommand&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
  
  &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;command&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;commands&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
    &lt;span class='n'&gt;command&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;execute&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/5478520/3be249c8b70f9ce968b7d368d047fe95b60c838d'&gt;https://gist.github.com/5478520/3be249&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;And, until there would be an actual reason to go that far, I would refactor the if statement we started out with to something that&amp;#8217;s easier to maintain, and maybe even easier to &lt;em&gt;understand&lt;/em&gt;. Without worrying too much about what might happen in the future, we could try to turn the code into the simplest thing that works, and that would end me up with something like this;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;process&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;commands&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='s1'&gt;&amp;#39;q&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Goodbye&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='s1'&gt;&amp;#39;tweet&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;tweeting&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='s1'&gt;&amp;#39;dm&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;direct messaging&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='s1'&gt;&amp;#39;help&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;helping&amp;#39;&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
  
  &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;command&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;commands&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
    &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='n'&gt;command&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/5478520/eeef47f24e7e9fbb0e802e6b667253cc827e0f75'&gt;https://gist.github.com/5478520/eeef47&#x2026;&lt;/a&gt;&lt;/span&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/10IQQ51vZQM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2013/on-the-death-of-ifs</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Preloading dependencies for faster test suite start-up times</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/D0iv_oHNy_g/preloading-dependencies-for-faster-test-suite-start-up-times" />
    
    <id>http://jeffkreeftmeijer.com/2012/preloading-dependencies-for-faster-test-suite-start-up-times</id>
    <updated>2012-06-11T00:00:00+02:00</updated>
    <summary>To speed up our start-up times between test runs, we'll write a really simple dependency preloader in about twenty lines of Ruby.</summary>
    <content type="html">
      
        &lt;p&gt;Tools like &lt;a href='https://github.com/jstorimer/spin'&gt;Spin&lt;/a&gt; and &lt;a href='https://github.com/sporkrb/spork'&gt;Spork&lt;/a&gt; help you speed up your test runs by preloading your application&amp;#8217;s dependencies and running your test suite without reloading the whole stack each time. That sounds like magic, but it&amp;#8217;s actually quite simple to build a tool that can do something like that. In this article, we&amp;#8217;ll write a dependency preloader in about twenty lines of Ruby. Ready? Let&amp;#8217;s go!&lt;/p&gt;

&lt;p&gt;To keep things understandable, We&amp;#8217;ll start out with a simple file named &lt;code&gt;test.rb&lt;/code&gt;, pretending to be a test file by printing out a hundred dots:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;slow&amp;#39;&lt;/span&gt;

&lt;span class='mi'&gt;100&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;times&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='nb'&gt;sleep&lt;/span&gt; &lt;span class='nb'&gt;rand&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='mo'&gt;01&lt;/span&gt;
  &lt;span class='nb'&gt;print&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;.&amp;#39;&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It requires a file named &lt;code&gt;slow.rb&lt;/code&gt;, which will simulate your suite&amp;#8217;s startup time:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;sleep&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/2909445/5cabc0559d3a3d9aad90721306bac4aa8f4cfa9b'&gt;https://gist.github.com/2909445/5cabc0&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;As you might have guessed, when you run &lt;code&gt;ruby test.rb&lt;/code&gt;, it&amp;#8217;ll take two seconds before anything happens. That probably looks a lot like your Rails application&amp;#8217;s test suite, right? Now, waiting for two seconds is no real problem unless you&amp;#8217;re running your suite multiple times. If you run &lt;code&gt;ruby test.rb&lt;/code&gt; three times now, you&amp;#8217;ll notice what I mean.&lt;/p&gt;

&lt;h3 id='forking'&gt;Forking&lt;/h3&gt;

&lt;p&gt;In this case, &lt;code&gt;slow.rb&lt;/code&gt; is a dependency that doesn&amp;#8217;t change too often, like Rails or some other library your application depends on. That means it&amp;#8217;s safe to preload it and run the test suite with it a couple of times, instead of reloading it for each run. We can do that by requiring it once and forking the process every time we run our test suite. A simple example of that could look like this, which will run &lt;code&gt;test.rb&lt;/code&gt; three times without reloading &lt;code&gt;slow.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;slow&amp;#39;&lt;/span&gt;

&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;times&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; 
  &lt;span class='nb'&gt;fork&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; 
    &lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;test&amp;#39;&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='no'&gt;Process&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;wait&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/2909445/ec2f2245bb8b8ee411723f02eccaa0e9c1e65f30'&gt;https://gist.github.com/2909445/ec2f22&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;After preloading &lt;code&gt;slow.rb&lt;/code&gt; on the first line, we&amp;#8217;ll go into a loop which creates a subprocess using &lt;code&gt;fork&lt;/code&gt;. We&amp;#8217;ll require &lt;code&gt;test.rb&lt;/code&gt; in this subprocess and finally, we&amp;#8217;ll call &lt;code&gt;Process.wait&lt;/code&gt; in the main process, which will halt to wait for the subprocess to exit. Because &lt;code&gt;slow.rb&lt;/code&gt; is already required in the main process before forking off, it won&amp;#8217;t be loaded again by &lt;code&gt;test.rb&lt;/code&gt; in the forked subprocesses.&lt;/p&gt;

&lt;h3 id='server'&gt;Server&lt;/h3&gt;

&lt;p&gt;On to the real thing. To make this usable as an actual tool, we need to turn it into a server that can receive messages and run tests when prompted to. To be able to talk between two Ruby processes, we&amp;#8217;ll use &lt;a href='http://www.ruby-doc.org/stdlib-1.9.3/libdoc/drb/rdoc/DRb.html'&gt;DRb&lt;/a&gt;. Let&amp;#8217;s create a file named &lt;code&gt;server.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;drb&amp;#39;&lt;/span&gt;
&lt;span class='no'&gt;ARGV&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;each&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;file&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt; &lt;span class='n'&gt;file&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;env&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;test&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;defined?&lt;/span&gt; &lt;span class='no'&gt;Rails&lt;/span&gt; 

&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Runner&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;run&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;files&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nb'&gt;fork&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
      &lt;span class='n'&gt;files&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;each&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;file&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt; &lt;span class='n'&gt;file&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
    &lt;span class='no'&gt;Process&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;wait&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='no'&gt;DRb&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;start_service&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;druby://:4321&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Runner&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;span class='no'&gt;DRb&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;thread&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;join&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, &lt;code&gt;Runner#run&lt;/code&gt; looks a lot like what we&amp;#8217;ve done before. The big difference is that we put it in a class named &lt;code&gt;Runner&lt;/code&gt; and started a DRb service with a reference to an instance of it. This allows a client connected to the service to directly call methods on the &lt;code&gt;Runner&lt;/code&gt; instance. Let&amp;#8217;s create another file, named &lt;code&gt;client.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;drb&amp;#39;&lt;/span&gt;

&lt;span class='n'&gt;runner&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;DRbObject&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt; &lt;span class='kp'&gt;nil&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;druby://:4321&amp;#39;&lt;/span&gt;
&lt;span class='n'&gt;runner&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;run&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;ARGV&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/2909445/185119a265744aea8d69a1df2fc60fdd7a97164b'&gt;https://gist.github.com/2909445/185119a265744aea8&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;The only thing the client does is create a new &lt;code&gt;DRbObject&lt;/code&gt; &amp;#8211;which returns the instance of &lt;code&gt;Runner&lt;/code&gt; we initialized in &lt;code&gt;server.rb&lt;/code&gt;&amp;#8211; and call the &lt;code&gt;run&lt;/code&gt; method on it.&lt;/p&gt;

&lt;p&gt;To try it out, start your server and tell it to preload &lt;code&gt;slow.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; ruby server.rb slow
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And run our test file using the client in another terminal window:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; ruby client.rb &lt;span class='nb'&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you&amp;#8217;ll see, your tests will start almost instantly.&lt;/p&gt;

&lt;p&gt;Note: if you want to use your new preloader in a Rails application, you&amp;#8217;ll probably want to preload &lt;code&gt;config/application&lt;/code&gt;, since that&amp;#8217;s the file that starts your application and tells Bundler to require your dependencies.&lt;/p&gt;

&lt;p&gt;See how easy it is to preload dependencies instead of loading them before each test run? Of course, this could use a lot of work, but this was just an attempt to show you a really simple way to ease the pain of your application&amp;#8217;s startup time when running your tests. If you&amp;#8217;re looking for something more polished, check out &lt;a href='https://github.com/jstorimer/spin'&gt;Spin&lt;/a&gt;, by &lt;a href='http://twitter.com/jstorimer' title='Jesse Storimer'&gt;@jstorimer&lt;/a&gt;.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/D0iv_oHNy_g" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2012/preloading-dependencies-for-faster-test-suite-start-up-times</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Relative line numbers in Vim for super-fast movement</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/LX_ZC4LKqGI/relative-line-numbers-in-vim-for-super-fast-movement" />
    
    <id>http://jeffkreeftmeijer.com/2012/relative-line-numbers-in-vim-for-super-fast-movement</id>
    <updated>2012-01-30T00:00:00+01:00</updated>
    <summary>Vim's relativenumber option is great, but sometimes it gets into your way a bit. With some clever settings, let's set it up so it just works.</summary>
    <content type="html">
      
        &lt;p&gt;Vim&amp;#8217;s &lt;code&gt;relativenumber&lt;/code&gt; is a great setting to help you move around files quickly, but sometimes it can get into your way a bit. Luckily, it&amp;#8217;s really easy to add some clever settings, so Vim knows when to toggle between relative and absolute line numbers.&lt;/p&gt;

&lt;p&gt;Moving around is probably the most useful thing to learn when you&amp;#8217;re starting to use Vim. A good starting point would be to get &lt;a href='http://twitter.com/#!/tednaleid' title='Ted Naleid'&gt;@tednaleid&lt;/a&gt;&amp;#8217;s &lt;a href='http://naleid.com/blog/2010/10/04/vim-movement-shortcuts-wallpaper'&gt;movement shortcuts wallpaper&lt;/a&gt;, a great cheat sheet you can use to get familiar with some of the commands.&lt;/p&gt;

&lt;p&gt;&lt;img alt='Relative line numbers in Vim' src='http://jeffkreeftmeijer.com/images/relativenumber.png' /&gt; &lt;span class='small'&gt;Relative line numbers in Vim&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;After a while, you&amp;#8217;ll be able to move by lines, paragraphs and whole screens. Using &lt;code&gt;gg&lt;/code&gt; and &lt;code&gt;G&lt;/code&gt;, you can move to the start and end of the file and you can use &lt;code&gt;/&lt;/code&gt; and &lt;code&gt;?&lt;/code&gt; to quickly find some text. Awesome.&lt;/p&gt;

&lt;p&gt;But, what do you do when you know where to go and you need to move five, seven, or thirteen lines down? Maybe you want to delete seven lines. What about that?&lt;/p&gt;

&lt;p&gt;Commands like &lt;code&gt;j&lt;/code&gt; and &lt;code&gt;k&lt;/code&gt; can be prefixed with numbers, so &lt;code&gt;5j&lt;/code&gt; will jump down five lines. While that&amp;#8217;s pretty cool, chances are you won&amp;#8217;t actually do that, since counting lines usually takes longer than pressing &lt;code&gt;j&lt;/code&gt; until you&amp;#8217;re on the right line, especially when you need to jump more than five lines. The same goes for deleting lines, as you probably switch to visual mode and press &lt;code&gt;j&lt;/code&gt; a couple of times instead of counting lines and pressing &lt;code&gt;d7d&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id='relative_or_absolute'&gt;Relative or absolute?&lt;/h3&gt;

&lt;p&gt;Since 7.3, Vim has a setting called &lt;code&gt;relativenumber&lt;/code&gt; (you can set it up with &lt;code&gt;:set relativenumber&lt;/code&gt; or &lt;code&gt;:set rnu&lt;/code&gt;), which is a lot like the &lt;code&gt;number&lt;/code&gt; setting you&amp;#8217;re probably using to have line numbers already. But, instead of showing the absolute line numbers from the top of the file, it shows them relative to the line you&amp;#8217;re currently on. That means the line below the current one is marked with 1, as is the line above. Now it&amp;#8217;s quite easy to find out how many lines you need to jump up or down.&lt;/p&gt;

&lt;p&gt;You can&amp;#8217;t use absolute and relative line numbers at the same time, so let&amp;#8217;s set something up to quickly switch back to absolute line numbers if we need them. A simple function will do this for us and we&amp;#8217;ll use &lt;code&gt;&amp;lt;C-n&amp;gt;&lt;/code&gt; (that&amp;#8217;s control + n) to call it:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='vim'&gt;&lt;span class='k'&gt;function&lt;/span&gt;&lt;span class='p'&gt;!&lt;/span&gt; NumberToggle&lt;span class='p'&gt;()&lt;/span&gt;
  &lt;span class='k'&gt;if&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&amp;amp;&lt;span class='nb'&gt;relativenumber&lt;/span&gt; &lt;span class='p'&gt;==&lt;/span&gt; &lt;span class='m'&gt;1&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;set&lt;/span&gt; &lt;span class='k'&gt;number&lt;/span&gt;
  &lt;span class='k'&gt;else&lt;/span&gt;
    &lt;span class='k'&gt;set&lt;/span&gt; &lt;span class='nb'&gt;relativenumber&lt;/span&gt;
  &lt;span class='k'&gt;endif&lt;/span&gt;
endfunc

&lt;span class='nb'&gt;nnoremap&lt;/span&gt; &lt;span class='p'&gt;&amp;lt;&lt;/span&gt;C&lt;span class='p'&gt;-&lt;/span&gt;&lt;span class='k'&gt;n&lt;/span&gt;&lt;span class='p'&gt;&amp;gt;&lt;/span&gt; :&lt;span class='k'&gt;call&lt;/span&gt; NumberToggle&lt;span class='p'&gt;()&amp;lt;&lt;/span&gt;&lt;span class='k'&gt;cr&lt;/span&gt;&lt;span class='p'&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='sometimes_you_need_absolute_line_numbers'&gt;Sometimes you need absolute line numbers&lt;/h3&gt;

&lt;p&gt;Relative line numbers are only useful when moving, so there are a couple of situations where you&amp;#8217;d probably want to switch back. Something I ran into was running tests.&lt;/p&gt;

&lt;p&gt;Most testing libraries allow you to run a subset of tests based on their line number by running something like &lt;code&gt;rspec spec/models/user_spec.rb:15&lt;/code&gt;. Whenever I&amp;#8217;d switch to my terminal window to run a single test, I looked back at Vim to see which exact line number I needed to run. Because my line numbers were in relative mode, I needed to switch back to Vim, press &lt;code&gt;&amp;lt;C-n&amp;gt;&lt;/code&gt; to get the absolute line numbers again, and then switch back to my terminal to actually run the spec.&lt;/p&gt;

&lt;p&gt;A quick solution is to automatically switch to absolute line numbers whenever Vim loses focus, since we don&amp;#8217;t really care about the relative line numbers unless we&amp;#8217;re moving around:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='vim'&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='k'&gt;au&lt;/span&gt; &lt;span class='nb'&gt;FocusLost&lt;/span&gt; * :&lt;span class='k'&gt;set&lt;/span&gt; &lt;span class='k'&gt;number&lt;/span&gt;
&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='k'&gt;au&lt;/span&gt; &lt;span class='nb'&gt;FocusGained&lt;/span&gt; * :&lt;span class='k'&gt;set&lt;/span&gt; &lt;span class='nb'&gt;relativenumber&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There, now we only have relative numbers when we&amp;#8217;re in Vim. But there are more cases when we probably prefer absolute line numbers. In insert mode, for example.&lt;/p&gt;

&lt;p&gt;I &lt;a href='https://github.com/jeffkreeftmeijer/dotfiles/blob/master/home/.vim/config/hjkl.vim'&gt;disabled my arrow keys in insert mode&lt;/a&gt;, to force myself to use &lt;code&gt;5j&lt;/code&gt; in normal mode instead of just pressing &lt;code&gt;&#x2193;&#x2193;&#x2193;&#x2193;&#x2193;&lt;/code&gt;. I never move around in insert mode, so relative numbers would be useless and I would probably prefer absolute line numbers. Let&amp;#8217;s tell Vim to automatically use absolute line numbers when we&amp;#8217;re in insert mode and relative numbers when we&amp;#8217;re in normal mode:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='vim'&gt;autocmd &lt;span class='nb'&gt;InsertEnter&lt;/span&gt; * :&lt;span class='k'&gt;set&lt;/span&gt; &lt;span class='k'&gt;number&lt;/span&gt;
autocmd &lt;span class='nb'&gt;InsertLeave&lt;/span&gt; * :&lt;span class='k'&gt;set&lt;/span&gt; &lt;span class='nb'&gt;relativenumber&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There. Now your line numbers are absolute by default, unless you&amp;#8217;re moving around in normal mode. This allows you to use the full power of relative line numbers, without them getting in your way.&lt;/p&gt;

&lt;p&gt;Because you&amp;#8217;re so special to me, I&amp;#8217;ve put the function and autocommands we talked about into a &lt;a href='https://github.com/jeffkreeftmeijer/vim-numbertoggle'&gt;plugin&lt;/a&gt;, so you can install it easily.&lt;/p&gt;

&lt;p&gt;Also, I&amp;#8217;d love to know how these settings (or the whole plugin) work for you, so be sure to let me know in the comments or in the &lt;a href='https://github.com/jeffkreeftmeijer/vim-numbertoggle/issues'&gt;plugin&amp;#8217;s issue tracker&lt;/a&gt;.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/LX_ZC4LKqGI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2012/relative-line-numbers-in-vim-for-super-fast-movement</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Method chaining and lazy evaluation in Ruby</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/kbgVgT0zE3g/method-chaining-and-lazy-evaluation-in-ruby" />
    
    <id>http://jeffkreeftmeijer.com/2011/method-chaining-and-lazy-evaluation-in-ruby</id>
    <updated>2011-11-28T00:00:00+01:00</updated>
    <summary>To figure out how method chaining and lazy evaluation work, we&#x2019;ll write a library that can chain method calls to build up a MongoDB query.</summary>
    <content type="html">
      
        &lt;p&gt;Method chaining has been all the rage lately and every database wrapper or aything else that&amp;#8217;s uses queries seems to be doing it. But, how does it work? To figure that out, we&amp;#8217;ll write a library that can chain method calls to build up a MongoDB query in this article. Let&amp;#8217;s get started!&lt;/p&gt;

&lt;p&gt;Oh, and don&amp;#8217;t worry if you haven&amp;#8217;t used MongoDB before, I&amp;#8217;m just using it as an example to query on. If you&amp;#8217;re using this guide to build a querying library for something else, the MongoDB part should be easy to swap out.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s say we&amp;#8217;re working with a user collection and we want to be able to query it somewhat like this:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Jeff&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;5&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We&amp;#8217;ll create a &lt;code&gt;Criteria&lt;/code&gt; class to build queries. As you might have guessed, it needs two instance methods named &lt;code&gt;where&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When calling one of these methods, all our object needs to do is remember the criteria that were passed, so we&amp;#8217;ll need to set up an instance variable &amp;#8211; named &lt;code&gt;@criteria&lt;/code&gt; &amp;#8211; to store them in.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;where&lt;/code&gt; method is used to specify conditions and we want it to return an empty array when none have been specified yet, so we&amp;#8217;ll add an empty array to our criteria hash by default:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Criteria&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;criteria&lt;/span&gt;
    &lt;span class='vi'&gt;@criteria&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:conditions&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{}}&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1397738/946ce04625042250697601fd30f269a495a4b4dc'&gt;https://gist.github.com/1397738/946ce0&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;Now we&amp;#8217;re able to remember conditions, we need a way to set them. We&amp;#8217;ll create a &lt;code&gt;where&lt;/code&gt; method that adds its arguments to the conditions array:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Criteria&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;criteria&lt;/span&gt;
    &lt;span class='vi'&gt;@criteria&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:conditions&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{}}&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;criteria&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:conditions&lt;/span&gt;&lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;merge!&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1397738/dacc040f5aeb35a90f5963d3920464fe28642806'&gt;https://gist.github.com/1397738/dacc04&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;Great! Let&amp;#8217;s give it a try:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='irb'&gt;&lt;span class='go'&gt;ruby-1.9.3-p0 :001 &amp;gt; require File.expand_path &amp;#39;criteria&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; true&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :002 &amp;gt; c = Criteria.new&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; #&amp;lt;Criteria:0x007ff9db8bf1f0&amp;gt;&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :003 &amp;gt; c.where(:name =&amp;gt; &amp;#39;Jeff&amp;#39;)&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; {:name=&amp;gt;&amp;quot;Jeff&amp;quot;}&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :004 &amp;gt; c&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; #&amp;lt;Criteria:0x007ff9db8bf1f0 @criteria={:conditions=&amp;gt;{:name=&amp;gt;&amp;quot;Jeff&amp;quot;}}&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, our &lt;code&gt;Criteria&lt;/code&gt; object successfully stores our condition in the &lt;code&gt;@criteria&lt;/code&gt; variable. Let&amp;#8217;s try to chain another &lt;code&gt;where&lt;/code&gt; call:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='irb'&gt;&lt;span class='go'&gt;ruby-1.9.3-p0 :001 &amp;gt; require File.expand_path &amp;#39;criteria&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; true&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :002 &amp;gt; c = Criteria.new&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; #&amp;lt;Criteria:0x007fbf5296d098&amp;gt;&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :003 &amp;gt; c.where(:name =&amp;gt; &amp;#39;Jeff&amp;#39;).where(:login =&amp;gt; &amp;#39;jkreeftmeijer&amp;#39;)&lt;/span&gt;
&lt;span class='go'&gt;NoMethodError: undefined method `where&amp;#39; for {:name=&amp;gt;&amp;quot;Jeff&amp;quot;}:Hash&lt;/span&gt;
&lt;span class='go'&gt;    from (irb):3&lt;/span&gt;
&lt;span class='go'&gt;    from /Users/jeff/.rvm/rubies/ruby-1.9.3-p0/bin/irb:16:in `&amp;lt;main&amp;gt;&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Hm. That didn&amp;#8217;t work, because &lt;code&gt;where&lt;/code&gt; returns a hash and &lt;code&gt;Hash&lt;/code&gt; doesn&amp;#8217;t have a &lt;code&gt;where&lt;/code&gt; method. We need to make sure the &lt;code&gt;where&lt;/code&gt; method returns the &lt;code&gt;Criteria&lt;/code&gt; object. Let&amp;#8217;s update the &lt;code&gt;where&lt;/code&gt; method so it returns &lt;code&gt;self&lt;/code&gt; instead of the conditions variable:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Criteria&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;criteria&lt;/span&gt;
    &lt;span class='vi'&gt;@criteria&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:conditions&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{}}&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;criteria&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:conditions&lt;/span&gt;&lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;merge!&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nb'&gt;self&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1397738/c5d22217f3856eb5e9e336a27c82874f3801031a'&gt;https://gist.github.com/1397738/c5d222&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;Okay, let&amp;#8217;s try it again:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='irb'&gt;&lt;span class='go'&gt;ruby-1.9.3-p0 :001 &amp;gt; require File.expand_path &amp;#39;criteria&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; true&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :002 &amp;gt; c = Criteria.new&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; #&amp;lt;Criteria:0x007fe91117c738&amp;gt;&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :003 &amp;gt; c.where(:name =&amp;gt; &amp;#39;Jeff&amp;#39;).where(:login =&amp;gt; &amp;#39;jkreeftmeijer&amp;#39;)&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; #&amp;lt;Criteria:0x007fe91117c738 @criteria={:conditions=&amp;gt;{:name=&amp;gt;&amp;quot;Jeff&amp;quot;, :login=&amp;gt;&amp;quot;jkreeftmeijer&amp;quot;}}&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Ha! Now we can chain as many conditions as we want. Let&amp;#8217;s go ahead and implement that &lt;code&gt;limit&lt;/code&gt; method right away, so we can limit our query&amp;#8217;s results.&lt;/p&gt;

&lt;p&gt;Of course, we only need one limit, as multiple limits wouldn&amp;#8217;t make sense. This means we don&amp;#8217;t need an array, we can just set &lt;code&gt;criteria[:limit]&lt;/code&gt; instead of merging hashes, like we did with the conditions before:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Criteria&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;criteria&lt;/span&gt;
    &lt;span class='vi'&gt;@criteria&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:conditions&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{}}&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;criteria&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:conditions&lt;/span&gt;&lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;merge!&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nb'&gt;self&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;criteria&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:limit&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;limit&lt;/span&gt;
    &lt;span class='nb'&gt;self&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1397738/d289697a3a85deb9cc3710ddac181bf2e97d8c3b'&gt;https://gist.github.com/1397738/d28969&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;Now we can chain conditions and even throw in a limit:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='irb'&gt;&lt;span class='go'&gt;ruby-1.9.3-p0 :001 &amp;gt; require File.expand_path &amp;#39;criteria&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; true&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :002 &amp;gt; c = Criteria.new&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; #&amp;lt;Criteria:0x007fdb1b0ca528&amp;gt;&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :003 &amp;gt; c.where(:name =&amp;gt; &amp;#39;Jeff&amp;#39;).limit(5)&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; #&amp;lt;Criteria:0x007fdb1b0ca528 @criteria={:conditions=&amp;gt;{:name=&amp;gt;&amp;quot;Jeff&amp;quot;}, :limit=&amp;gt;5}&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='the_model'&gt;The model&lt;/h3&gt;

&lt;p&gt;There. We can collect query criteria now, but we&amp;#8217;ll need a model to actually query on. For this example, let&amp;#8217;s create a model named &lt;code&gt;User&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since we&amp;#8217;re building a library that can query a MongoDB database, I&amp;#8217;ve installed the &lt;a href='https://github.com/mongodb/mongo-ruby-driver'&gt;mongo-ruby-driver&lt;/a&gt; and added a &lt;code&gt;collection&lt;/code&gt; method to the &lt;code&gt;User&lt;/code&gt; model:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;mongo&amp;#39;&lt;/span&gt;

&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;User&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;collection&lt;/span&gt;
    &lt;span class='vi'&gt;@collection&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='no'&gt;Mongo&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Connection&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;criteria&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;][&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;users&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1397738/2b9bd004d7592e51bb698c2f7449771e711c0e35'&gt;https://gist.github.com/1397738/2b9bd0&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;The &lt;code&gt;collection&lt;/code&gt; method connects to the &amp;#8220;criteria&amp;#8221; database, looks up the &amp;#8220;users&amp;#8221; collection and returns an instance of &lt;code&gt;Mongo::Collection&lt;/code&gt;, which we&amp;#8217;ll use to query on later.&lt;/p&gt;

&lt;p&gt;Remember when I said I wanted to be able to do something like &lt;code&gt;User.where(:name =&amp;gt; &amp;#39;Jeff&amp;#39;).limit(5)&lt;/code&gt;? Well, right now our model doesn&amp;#8217;t implement &lt;code&gt;where&lt;/code&gt; or &lt;code&gt;limit&lt;/code&gt;, since we put them in the &lt;code&gt;Criteria&lt;/code&gt; class. Let&amp;#8217;s fix that by creating two methods on &lt;code&gt;User&lt;/code&gt; that delegate to &lt;code&gt;Criteria&lt;/code&gt;.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;mongo&amp;#39;&lt;/span&gt;
&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;criteria&amp;#39;&lt;/span&gt;

&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;User&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;collection&lt;/span&gt;
    &lt;span class='vi'&gt;@collection&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='no'&gt;Mongo&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Connection&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;mongo_chain&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;][&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;users&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='no'&gt;Criteria&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='no'&gt;Criteria&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1397738/6035babd3ed2439026c992abc7a12230718a77d1'&gt;https://gist.github.com/1397738/6035ba&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;This allows us to call our criteria methods directly on our model:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='irb'&gt;&lt;span class='go'&gt;ruby-1.9.3-p0 :001 &amp;gt; require File.expand_path &amp;#39;user&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; true&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :002 &amp;gt; User.where(:name =&amp;gt; &amp;#39;Jeff&amp;#39;).limit(5)&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; #&amp;lt;Criteria:0x007fca1c8b0bd0 @criteria={:conditions=&amp;gt;{:name=&amp;gt;&amp;quot;Jeff&amp;quot;}, :limit=&amp;gt;5}&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Great. Calling criteria on the &lt;code&gt;User&lt;/code&gt; model returns a &lt;code&gt;Criteria&lt;/code&gt; object now. But, maybe you already noticed it, the returned object has no idea what to query on. We need to let it know we want to search the users collection. To do that, we need to overwrite the &lt;code&gt;Criteria&lt;/code&gt;&amp;#8217;s &lt;code&gt;initialize&lt;/code&gt; method:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Criteria&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;klass&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='vi'&gt;@klass&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;klass&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;criteria&lt;/span&gt;
    &lt;span class='vi'&gt;@criteria&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:conditions&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{}}&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;criteria&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:conditions&lt;/span&gt;&lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;merge!&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nb'&gt;self&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;criteria&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:limit&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;limit&lt;/span&gt;
    &lt;span class='nb'&gt;self&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1397738/4e2e0b506dfb7ed171368b7b05b9ae560146c582'&gt;https://gist.github.com/1397738/4e2e0b&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;With a slight change to our model &amp;#8211; passing &lt;code&gt;self&lt;/code&gt; to &lt;code&gt;Criteria.new&lt;/code&gt; &amp;#8211;, we can let the &lt;code&gt;Criteria&lt;/code&gt; class know what we&amp;#8217;re looking for:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;mongo&amp;#39;&lt;/span&gt;
&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;criteria&amp;#39;&lt;/span&gt;

&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;User&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;collection&lt;/span&gt;
    &lt;span class='vi'&gt;@collection&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='no'&gt;Mongo&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Connection&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;criteria&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;][&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;users&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='no'&gt;Criteria&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='no'&gt;Criteria&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1397738/97652e1572efbcc3fe354c45c8905b0fdd975036'&gt;https://gist.github.com/1397738/97652e&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;After a quick test, we can see that the &lt;code&gt;Criteria&lt;/code&gt; instance successfully remembers our model class:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='irb'&gt;&lt;span class='go'&gt;ruby-1.9.3-p0 :001 &amp;gt; require File.expand_path &amp;#39;user&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; true&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :002 &amp;gt; User.where(:name =&amp;gt; &amp;#39;Jeff&amp;#39;)&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; #&amp;lt;Criteria:0x007ffdd30d4d68 @klass=User, @criteria={:conditions=&amp;gt;{:name=&amp;gt;&amp;quot;Jeff&amp;quot;}}&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='getting_some_results'&gt;Getting some results&lt;/h3&gt;

&lt;p&gt;The last thing we need to do is lazily querying our database and getting some results. To make sure our library doesn&amp;#8217;t query before collecting all of the criteria, we&amp;#8217;ll wait until &lt;code&gt;each&lt;/code&gt; gets called &amp;#8211; to loop over the query&amp;#8217;s results &amp;#8211; on the &lt;code&gt;Criteria&lt;/code&gt; instance. Let&amp;#8217;s see how our library handles that right now:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='irb'&gt;&lt;span class='go'&gt;ruby-1.9.3-p0 :001 &amp;gt; require File.expand_path &amp;#39;user&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; true&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :002 &amp;gt; User.where(:name =&amp;gt; &amp;#39;Jeff&amp;#39;).each { |u| puts u.inspect }&lt;/span&gt;
&lt;span class='go'&gt;NoMethodError: undefined method `each&amp;#39; for #&amp;lt;Criteria:0x007fd0540cfea0&amp;gt;&lt;/span&gt;
&lt;span class='go'&gt;	from (irb):2&lt;/span&gt;
&lt;span class='go'&gt;	from /Users/jeff/.rvm/rubies/ruby-1.9.3-p0/bin/irb:16:in `&amp;lt;main&amp;gt;&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Of course, there&amp;#8217;s no method named &lt;code&gt;each&lt;/code&gt; on &lt;code&gt;Criteria&lt;/code&gt;, because we don&amp;#8217;t have anything to loop over yet. We&amp;#8217;ll create &lt;code&gt;Criteria#each&lt;/code&gt;, which will execute the query, giving us an array of results. We use that array&amp;#8217;s &lt;code&gt;each&lt;/code&gt; method to pass our block to:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Criteria&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;klass&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='vi'&gt;@klass&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;klass&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;criteria&lt;/span&gt;
    &lt;span class='vi'&gt;@criteria&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:conditions&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{}}&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;criteria&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:conditions&lt;/span&gt;&lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;merge!&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nb'&gt;self&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;limit&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;criteria&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:limit&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;limit&lt;/span&gt;
    &lt;span class='nb'&gt;self&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;each&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='vi'&gt;@klass&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;collection&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;
      &lt;span class='n'&gt;criteria&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:conditions&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:limit&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;criteria&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:limit&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;each&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1397738/a1a25404469dcce8c1b5de36b0ab48349ca77d84'&gt;https://gist.github.com/1397738/a1a254&#x2026;&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;And now, finally, our query works (don&amp;#8217;t forget to add some user documents to your database):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='irb'&gt;&lt;span class='go'&gt;ruby-1.9.3-p0 :001 &amp;gt; require File.expand_path &amp;#39;user&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; true&lt;/span&gt;
&lt;span class='go'&gt;ruby-1.9.3-p0 :002 &amp;gt; User.where(:name =&amp;gt; &amp;#39;Jeff&amp;#39;).limit(2).each { |u| puts u.inspect }&lt;/span&gt;
&lt;span class='go'&gt;{&amp;quot;_id&amp;quot;=&amp;gt;BSON::ObjectId(&amp;#39;4ed2603b368ff6d6bc000001&amp;#39;), &amp;quot;name&amp;quot;=&amp;gt;&amp;quot;Jeff&amp;quot;}&lt;/span&gt;
&lt;span class='go'&gt;{&amp;quot;_id&amp;quot;=&amp;gt;BSON::ObjectId(&amp;#39;4ed2603b368ff6d6bc000002&amp;#39;), &amp;quot;name&amp;quot;=&amp;gt;&amp;quot;Jeff&amp;quot;}&lt;/span&gt;
&lt;span class='go'&gt; =&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='awesome_now_what'&gt;Awesome! Now what?&lt;/h3&gt;

&lt;p&gt;Now you have a library that can do chained and lazy-evaluated queries on a MongoDB database. Of course, there&amp;#8217;s a lot of stuff you could still add &amp;#8211; for example, you could mix in &lt;a href='http://www.ruby-doc.org/core-1.9.3/Enumerable.html'&gt;Enumerable&lt;/a&gt; and do some metaprogramming magic to remove some of the duplication &amp;#8211; but that&amp;#8217;s beyond the scope of this article.&lt;/p&gt;

&lt;p&gt;If you have any questions, ideas, suggestions or comments, or you just want more articles like this one be sure to let me know in the comments.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/kbgVgT0zE3g" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/method-chaining-and-lazy-evaluation-in-ruby</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Spec helpers, Bundler.setup and faster Rails test suites</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/V-g9pVl_nlw/spec-helpers-bundler-setup-faster-rails-test-suites" />
    
    <id>http://jeffkreeftmeijer.com/2011/spec-helpers-bundler-setup-faster-rails-test-suites</id>
    <updated>2011-10-17T00:00:00+02:00</updated>
    <summary>Loading dependencies in your Rails models, controllers, and initializers instead of letting Bundler pre-require everything all the time.</summary>
    <content type="html">
      
        &lt;p&gt;The &lt;code&gt;spec_helper.rb&lt;/code&gt; file rspec-rails generates is convenient, as it sets up everything you need to get your tests running. In your actual test, all you have to do is &lt;code&gt;require &amp;quot;spec_helper&amp;quot;&lt;/code&gt; and off you go. After waiting for a while.&lt;/p&gt;

&lt;p&gt;&lt;img alt='Bundler.setup instead of Bundler.require' src='http://jeffkreeftmeijer.com/images/bundler_setup.png' /&gt;&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;spec_helper&lt;/code&gt; is probably slowing your tests down quite a bit while it doesn&amp;#8217;t have to. To get an idea of how long it&amp;#8217;s taking, let&amp;#8217;s just time it (if you&amp;#8217;re following along, I&amp;#8217;d love to know how long yours takes to load in the comments):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; &lt;span class='nb'&gt;time &lt;/span&gt;ruby spec/spec_helper.rb
&lt;span class='go'&gt;ruby spec/spec_helper.rb  16.38s user 1.83s system 92% cpu 19.691 total&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For &lt;a href='http://codebrawl.com'&gt;Codebrawl&lt;/a&gt;, that&amp;#8217;s almost twenty seconds. Not to run the suite, but just to require everything needed. Wow. Also remember that this spec helper is being required even when running model specs.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s try to speed up a fairly simple spec for the &lt;code&gt;User&lt;/code&gt; model. The first thing I do is rip out the &lt;code&gt;require &amp;quot;spec_helper&amp;quot;&lt;/code&gt; line and run the spec. That ends me up with a couple of failures due to the model not being loaded and some missing requirements, so I add them to the top of my spec file:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;mongoid&amp;#39;&lt;/span&gt;
&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;gravtastic&amp;#39;&lt;/span&gt;
&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;app/models/user&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;shoulda-matchers&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There. The spec&amp;#8217;s running again and not requiring the spec helper gave me a six second speed boost, cutting off about 30% of my setup time. You might argue that that&amp;#8217;s still too long, but let&amp;#8217;s keep that for another blogpost.&lt;/p&gt;

&lt;h3 id='taking_it_one_step_further'&gt;Taking it one step further&lt;/h3&gt;

&lt;p&gt;There&amp;#8217;s a line like this in the spec helper rspec-rails generates:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;../../config/environment&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='bp'&gt;__FILE__&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;That line loads up the Rails environment file which, in turn, will require &lt;code&gt;config/application.rb&lt;/code&gt;. In there, you can find a line like one of these:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Bundler&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;require&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;groups&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:assets&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='sx'&gt;%w(development test)&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='c1'&gt;# or&lt;/span&gt;
&lt;span class='no'&gt;Bundler&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;require&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:default&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:assets&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;env&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;That line takes care of requiring the gems you added to your &lt;code&gt;:default&lt;/code&gt; and &lt;code&gt;:test&lt;/code&gt; groups in your Gemfile (in test mode) so you don&amp;#8217;t have to require anything in your models, for instance.&lt;/p&gt;

&lt;p&gt;By not loading up a spec helper, not loading Rails and not running &lt;code&gt;Bundler.require&lt;/code&gt;, like we just did in our spec, we need to take care of loading gems ourselves. In the example above, I just required the gems I needed in the &lt;code&gt;User&lt;/code&gt; model spec. That&amp;#8217;s probably not the best place to put them, since the test should not handle the model&amp;#8217;s requirements. The model should require the libraries it needs itself, right?&lt;/p&gt;

&lt;h3 id='bundlersetup_instead_of_bundlerrequire'&gt;Bundler.setup instead of Bundler.require&lt;/h3&gt;

&lt;p&gt;If we change the &lt;code&gt;Bundler.require&lt;/code&gt; call to a &lt;code&gt;Bundler.setup&lt;/code&gt; one, Bundler won&amp;#8217;t require any gems in &lt;code&gt;config/application.rb&lt;/code&gt; and will leave that all up to you:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Bundler&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;setup&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;groups&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:assets&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='sx'&gt;%w(development test)&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='c1'&gt;# or&lt;/span&gt;
&lt;span class='no'&gt;Bundler&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;setup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:default&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:assets&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;env&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now, run your tests. It might be a good idea to use &lt;a href='http://jeffkreeftmeijer.com/2010/making-rspec-stop-operation-immediately-after-failing/'&gt;RSpec&amp;#8217;s fail fast&lt;/a&gt; option here, because stuff is going to break. Simply add a &lt;code&gt;require&lt;/code&gt; wherever needed to get your tests running properly again. If you get stuck, just take a look at &lt;a href='https://github.com/codebrawl/codebrawl/commit/9df324809cc6e39495af9c78407a6fefed02b7e0'&gt;my changes in Codebrawl&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After doing that, you can remove the model&amp;#8217;s requirements from your &lt;code&gt;User&lt;/code&gt; spec file and only leave the spec&amp;#8217;s requirements:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;app/models/user&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;shoulda-matchers&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;From now on, your application&amp;#8217;s parts handle their requirements themselves instead of relying on Bundler to have them preloaded. This will allow you to load up one of your models without having to worry about its requirements. So, if your model needs Mongoid, it&amp;#8217;ll require it itself.&lt;/p&gt;

&lt;p&gt;Have any comments, questions or great ideas? Did this approach work for you? Or not? Be sure to let me know in the comments!&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/V-g9pVl_nlw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/spec-helpers-bundler-setup-faster-rails-test-suites</feedburner:origLink></entry>
  
  <entry>
    <title type="html">MicroGems: five minute RubyGems</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/I6l6DySTBeM/microgems-five-minute-rubygems" />
    
    <id>http://jeffkreeftmeijer.com/2011/microgems-five-minute-rubygems</id>
    <updated>2011-10-03T00:00:00+02:00</updated>
    <summary>Creating RubyGems out of libraries you want to use in multiple projects, without the open source.</summary>
    <content type="html">
      
        &lt;p class='small'&gt;
&lt;strong&gt;Note:&lt;/strong&gt; This is just something I've been playing around with. I'm not saying you should do this and I'm not implying this is better than what you're doing right now. I'm just sharing some experiences from a quick experiment.
&lt;/p&gt;
&lt;p&gt;For &lt;a href='http://codebrawl.com'&gt;Codebrawl&lt;/a&gt;, I created a library that could take a method from a model &amp;#8211; like &lt;code&gt;calculate_score&lt;/code&gt; for example &amp;#8211; and give it a corresponding bang method &amp;#8211; &lt;code&gt;calculate_score!&lt;/code&gt; in this case &amp;#8211; that saves the results using a simple &lt;code&gt;update_attribute&lt;/code&gt; call. In your model, I can just do this:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;User&lt;/span&gt;

 &lt;span class='n'&gt;bang&lt;/span&gt; &lt;span class='ss'&gt;:calculate_score&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:score&lt;/span&gt;

 &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;calculate_score&lt;/span&gt;
   &lt;span class='mi'&gt;100&lt;/span&gt; &lt;span class='c1'&gt;# Removed fancy logic for presentational purposes&lt;/span&gt;
 &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now, calling &lt;code&gt;calculate_score!&lt;/code&gt; will save the results of &lt;code&gt;calculate_score&lt;/code&gt; to the score attribute. It&amp;#8217;s one 4-line method in one file and it has a 25-line spec. Simple.&lt;/p&gt;

&lt;p&gt;&lt;a href='https://gist.github.com/1232884'&gt;&lt;img alt='MicroGem' src='http://jeffkreeftmeijer.com/images/microgem.png' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a while, I ran into the same &amp;#8220;issue&amp;#8221; in another project. I wanted to use my new Bang library, but I didn&amp;#8217;t want to copy it over to the new project and I didn&amp;#8217;t feel like starting a new repository on &lt;a href='https://github.com'&gt;Github&lt;/a&gt; because that would mean I&amp;#8217;d have to write a &lt;code&gt;README&lt;/code&gt;, squat a name on &lt;a href='http://rubygems.org'&gt;RubyGems&lt;/a&gt;, accept issues and support it forever. It just felt too small for that.&lt;/p&gt;

&lt;p&gt;Instead, &lt;a href='https://gist.github.com/1232884'&gt;I put the library into a Gist&lt;/a&gt;. The great thing about Gists is that they&amp;#8217;re full Git repositories in disguise, so I can just clone my library, work on it and push a new &amp;#8220;release&amp;#8221; without having to fiddle around in Gist&amp;#8217;s web interface.&lt;/p&gt;

&lt;p&gt;To be able to use it as a RubyGem, the only thing I needed to do was add a very, &lt;em&gt;very&lt;/em&gt; simple &lt;a href='https://gist.github.com/1232884#file_bang.gemspec'&gt;gemspec&lt;/a&gt;. The thing you have to keep in mind is that you can&amp;#8217;t have any directories in Gists (you could, but these files are hidden in the web interface). This means your gemspec will be a bit different from what you normally do. Instead of doing crazy git command magic, you&amp;#8217;ll set your &lt;code&gt;files&lt;/code&gt;, &lt;code&gt;test_file&lt;/code&gt; and &lt;code&gt;require_path&lt;/code&gt; like this:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;s&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;files&lt;/span&gt;         &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;bang.rb&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
&lt;span class='n'&gt;s&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;test_file&lt;/span&gt;     &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;bang_spec.rb&amp;#39;&lt;/span&gt;
&lt;span class='n'&gt;s&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;require_path&lt;/span&gt;  &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;.&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Using the power of Bundler, I can now require my library in my projects, without even having to push it to RubyGems:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;bang&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:git&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;git://gist.github.com/1232884.git&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='trying_this_at_home'&gt;Trying this at home&lt;/h3&gt;

&lt;p&gt;See how easy it is to create your own RubyGem? The only thing you need is an implementation and a gemspec. Since Bang isn&amp;#8217;t much more than that right now, I think a Gist is sufficient. At least for now.&lt;/p&gt;

&lt;p&gt;If your project is bigger than this or has any users besides yourself, you&amp;#8217;re probably going to have to create a proper Github repository and push it to RubyGems. Gists don&amp;#8217;t support Github&amp;#8217;s issues, you can&amp;#8217;t accept pull requests and you can&amp;#8217;t use directories. As long as your library doesn&amp;#8217;t need any of that, I think you&amp;#8217;ll be fine with something as simple as this.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve been using my &amp;#8220;MicroGem&amp;#8221; for a while now and I haven&amp;#8217;t run into any issues yet. Do you foresee problems with this approach or do you have any ideas to make it even better? Please let me know in the comments!&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/I6l6DySTBeM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/microgems-five-minute-rubygems</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Isolated testing for custom validators in Rails 3</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/oOIEMWH-pMo/isolated-testing-for-custom-validators-in-rails-3" />
    
    <id>http://jeffkreeftmeijer.com/2011/isolated-testing-for-custom-validators-in-rails-3</id>
    <updated>2011-09-19T00:00:00+02:00</updated>
    <summary>Blazingly fast specs by testing Rails 3 custom validators without requiring the model</summary>
    <content type="html">
      
        &lt;p&gt;It&amp;#8217;s &lt;a href='http://www.talklikeapirate.com/'&gt;International Talk Like A Pirate Day&lt;/a&gt; today, so you might want to add a custom validation to check if comments submitted in your application actually sound like they were written by a pirate. Right? Right. I thought so. Anyway, let&amp;#8217;s create a validator with specs that don&amp;#8217;t need to require the model every time they run, allowing them to be blazingly fast. Or, at least faster than what you did before.&lt;/p&gt;

&lt;p&gt;&lt;img alt='Pirate' src='http://jeffkreeftmeijer.com/images/pirate.jpg' /&gt;&lt;/p&gt;

&lt;p&gt;Since we care about keeping our test suite nice and fast, we&amp;#8217;ll try not to load the &lt;code&gt;Comment&lt;/code&gt; model and anything else we don&amp;#8217;t really need. Instead of throwing the tests for our validator in the &lt;code&gt;Comment&lt;/code&gt;&amp;#8217;s model spec, we&amp;#8217;ll create a new one in &lt;code&gt;spec/validators/pirate_validator_spec.rb&lt;/code&gt; and put a mock model named &lt;code&gt;Validatable&lt;/code&gt; in there to test with:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Validatable&lt;/span&gt;
  &lt;span class='kp'&gt;include&lt;/span&gt; &lt;span class='no'&gt;ActiveModel&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Validations&lt;/span&gt;
  &lt;span class='n'&gt;validates_with&lt;/span&gt; &lt;span class='no'&gt;PirateValidator&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1226439/8d730b568c5ad7440e008439d85ccdb98c0b9ea6'&gt;https://gist.github.com/1226439/8d730b...&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;Running it right now (yes, without any actual tests) would end us up with a &lt;code&gt;NameError&lt;/code&gt;, telling us &lt;code&gt;ActiveModel&lt;/code&gt; is uninitialized. We&amp;#8217;ll need to require it:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;active_model&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1226439/66dc63860e02aee4ea2f4fa9afcf0f94d59737e0'&gt;https://gist.github.com/1226439/66dc63...&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;When running it again, we quickly find out the &lt;code&gt;PirateValidator&lt;/code&gt; is uninitialized, since we didn&amp;#8217;t create and require it yet. Let&amp;#8217;s put an empty validator in &lt;code&gt;app/validators/pirate_validator.rb&lt;/code&gt; (and don&amp;#8217;t forget to require it in the spec):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;PirateValidator&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveModel&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Validator&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1226439/b5a45ce614cf49b8d0f6a6fc8c50b85d5b739290'&gt;https://gist.github.com/1226439/b5a45c...&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;Now the spec actually runs without stumbling on any errors, so we can start writing our first test:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;PirateValidator&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;

  &lt;span class='n'&gt;subject&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='no'&gt;Validatable&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;

  &lt;span class='n'&gt;context&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;with a comment that sounds like a pirate&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;

    &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;subject&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;stub&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:comment&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;Ahoy, matey!&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;

    &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;be_valid&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;

  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1226439/be72a980ae6026b4ac7e0d260c416c0a10b66bc9'&gt;https://gist.github.com/1226439/be72a9...&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;Running the spec again, we get a &lt;code&gt;NotImplementedError&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;NotImplementedError:
  Subclasses must implement a validate(record) method.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ah, our &lt;code&gt;PirateValidator&lt;/code&gt; doesn&amp;#8217;t have a &lt;code&gt;validate&lt;/code&gt; method yet, so we&amp;#8217;ll just add an empty one:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;PirateValidator&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveModel&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Validator&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;validate&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;document&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1226439/a1c73c4106977410e54fe10e4c09c5f9a26bebd4'&gt;https://gist.github.com/1226439/a1c73c...&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;Wait, what? Our first spec passes, since it asserts the &lt;code&gt;Validatable&lt;/code&gt; object to be valid and our validator doesn&amp;#8217;t do anything yet. Let&amp;#8217;s add another test to give it some actual functionality:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;context&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;with a comment that sounds like a dinosaur&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;

  &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;subject&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;stub&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:comment&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;ROOOAAAR!&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;

  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;have&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;error_on&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:comment&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1226439/d29923c4a42530e4dc669e0849e1715481954141'&gt;https://gist.github.com/1226439/d29923...&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;Which causes another &lt;code&gt;NoMethodError&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;NoMethodError:
  undefined method `error_on&amp;#39; for #&amp;lt;Validatable:0x007faa43462ec8&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That&amp;#8217;s because we use &lt;code&gt;should have(1).error_on(:comment)&lt;/code&gt; in our spec, and &lt;code&gt;error_on&lt;/code&gt; comes with &lt;a href='https://github.com/rspec/rspec-rails'&gt;rspec-rails&lt;/a&gt; and we haven&amp;#8217;t included that yet. &lt;code&gt;error_on&lt;/code&gt; is in &lt;a href='https://github.com/rspec/rspec-rails/blob/master/lib/rspec/rails/extensions/active_record/base.rb'&gt;&lt;code&gt;RSpec::Rails::Extensions&lt;/code&gt;&lt;/a&gt;, so let&amp;#8217;s just require that:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rspec/rails/extensions&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1226439/3c5f4b1e539e30ef8b1d423a273ef952c9a70843'&gt;https://gist.github.com/1226439/3c5f4b...&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;If we run our tests again, we notice that they&amp;#8217;re quite a bit slower now. We could solve that by not using the &lt;code&gt;error_on&lt;/code&gt; method and not requiring &lt;code&gt;RSpec::Rails::Extensions&lt;/code&gt;, but I prefer using &lt;code&gt;error_on&lt;/code&gt; instead of having to do assertions on the &lt;code&gt;subject.errors&lt;/code&gt; array, but that&amp;#8217;s completely up to you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;: If you don&amp;#8217;t want to load up &lt;code&gt;RSpec::Rails::Extensions&lt;/code&gt;, but do want to use &lt;code&gt;error_on&lt;/code&gt;, just put &lt;a href='https://gist.github.com/1239170'&gt;this validations support file&lt;/a&gt; in &lt;code&gt;spec/support/validations.rb&lt;/code&gt; and &lt;code&gt;require &amp;#39;support/validations&amp;#39;&lt;/code&gt; instead of &lt;code&gt;rspec/rails/extensions&lt;/code&gt;. This is saving me about 2 seconds.&lt;/p&gt;

&lt;p&gt;After requiring &lt;code&gt;RSpec::Rails::Extensions&lt;/code&gt;, our spec starts running again and fails, because we haven&amp;#8217;t implemented the actual validation yet. So let&amp;#8217;s do that now:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;PirateValidator&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveModel&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Validator&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;validate&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;document&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='n'&gt;document&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;comment&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;include?&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;matey&amp;#39;&lt;/span&gt;
      &lt;span class='n'&gt;document&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;errors&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:comment&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;does not sound like a pirate&amp;#39;&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;span class='small'&gt;&lt;a href='https://gist.github.com/1226439/7a79aa385ae18b0418e2319383c3481bd9452caa'&gt;https://gist.github.com/1226439/7a79aa...&lt;/a&gt;&lt;/span&gt;
&lt;p&gt;And our test passes! We successfully implemented a model validator without actually loading the model in the specs. Now, getting it running in your model is up to you, but that shouldn&amp;#8217;t be more difficult than getting it to run in &lt;code&gt;Validatable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions or suggestions about this approach to test validators, be sure to let me know in the comments.. &lt;em&gt;Matey&lt;/em&gt;.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/oOIEMWH-pMo" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/isolated-testing-for-custom-validators-in-rails-3</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Vim is hard, I just want to click around</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/JZkpqsTeFkM/vim-is-hard-i-just-want-to-click-around" />
    
    <id>http://jeffkreeftmeijer.com/2011/vim-is-hard-i-just-want-to-click-around</id>
    <updated>2011-09-05T00:00:00+02:00</updated>
    <summary>Setting up MacVim with Janus and a sexy file drawer</summary>
    <content type="html">
      
        &lt;p&gt;More than a year ago, I wrote about &lt;a href='http://jeffkreeftmeijer.com/2010/stumbling-into-vim/'&gt;switching to Vim&lt;/a&gt;. I was quite happy with how it all worked, but I found myself switching back to Textmate after a while. I read through a lot of dotfiles to &amp;#8220;steal&amp;#8221; some configuration options from others, took some time to go through &lt;code&gt;vimtutor&lt;/code&gt;, read some blogposts and after a while, I was quite happy with how it all worked.&lt;/p&gt;

&lt;p&gt;&lt;img alt='Alloys MacVim fork with Janus' src='http://jeffkreeftmeijer.com/images/vim2.png' /&gt; &lt;span class='small'&gt;My new, full screen Janus-powered MacVim editor, with sexy drawer&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;But after some time, I started switching back to Textmate. At first, I only opened it up to do non-code writing, but after a while, I started using Textmate for bigger projects too since it felt easier to navigate around project files. After about half a year, I was back to Textmate completely and I only used Vim on my Ubuntu-powered netbook sometimes.&lt;/p&gt;

&lt;p&gt;Sometimes, I just want to click around, especially when looking for and opening files. I missed Textmate&amp;#8217;s drawer and never really got the hang of the NERDTree plugin I was using. Besides that, my Vim config felt incomplete, but I didn&amp;#8217;t know exactly what I was missing or what I needed to install to make everything easier. While I loved editing code in Vim, I couldn&amp;#8217;t really get used to the rest of the editor.&lt;/p&gt;

&lt;h3 id='macvim'&gt;MacVim&lt;/h3&gt;

&lt;p&gt;It turns out I wasn&amp;#8217;t the only one running into problems like this. Less than five kilometers from where I was switching back, &lt;a href='http://twitter.com/alloy' title='Eloy Durán'&gt;@alloy&lt;/a&gt; decided to fix the problem instead of running back to Textmate. He created &lt;a href='https://github.com/alloy/macvim'&gt;his own fork of MacVim&lt;/a&gt; and added a Textmate-like file drawer.&lt;/p&gt;

&lt;p&gt;I got a new hard drive, which was a great excuse to do a clean install and set up a better work environment. I started out by installing Eloy&amp;#8217;s MacVim branch, and quickly found out the drawer works as good &amp;#8211; or even better &amp;#8211; as Textmate&amp;#8217;s.&lt;/p&gt;

&lt;p&gt;The one thing I didn&amp;#8217;t really like was the new native Lion fullscreen mode, which newer versions of MacVim use by default. Its animations annoyed me and when using multiple screens, it cleared out the second screen when I put something in fullscreen mode on the first one. In my earlier attempt at using MacVim, they rolled their own fullscreen mode that worked perfectly and I wanted that back.&lt;/p&gt;

&lt;p&gt;Luckily, that&amp;#8217;s pretty easy to achieve. It&amp;#8217;s a matter of throwing this into your terminal:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; defaults write org.vim.MacVim MMNativeFullScreen 0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If that doesn&amp;#8217;t work for you, update MacVim. I had some problems getting it to work, having a slightly older version.&lt;/p&gt;

&lt;h3 id='janus'&gt;Janus&lt;/h3&gt;

&lt;p&gt;Instead of going around stealing configurations from other people&amp;#8217;s dotfiles again, I installed &lt;a href='https://github.com/carlhuda/janus'&gt;Janus&lt;/a&gt;. Janus is an awesome Vim config by &lt;a href='http://twitter.com/wycats' title='Yehuda Katz'&gt;@wycats&lt;/a&gt; and &lt;a href='http://twitter.com/carllerche' title='Carl Lerche'&gt;@carllerche&lt;/a&gt;, which comes with a bunch of great plugins and color schemes and it probably has everything you need.&lt;/p&gt;

&lt;p&gt;It installs NERDTree by default, which I don&amp;#8217;t need because I already have a file drawer. It&amp;#8217;s really easy to skip it, though. Before running &lt;code&gt;rake&lt;/code&gt; in your &lt;code&gt;~/.vim&lt;/code&gt; directory, create a file named &lt;code&gt;~/.janus.rake&lt;/code&gt; and put this in:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;skip_vim_plugin&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;nerdtree&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you already installed before (with NERDTree enabled), be sure to delete the &lt;code&gt;~/.vim/nerdtree&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Without NERDTree, I started getting some error messages when changing directories inside MacVim:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Error detected while processing function ChangeDirectory:
line    4:
E492: Not an editor command:   NERDTree&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So, after looking around for a bit, I found the problem in &lt;code&gt;~/.vim/gvimrc&lt;/code&gt;. In the &lt;code&gt;ChangeDirectory&lt;/code&gt; function, I removed &lt;a href='https://github.com/carlhuda/janus/blob/master/gvimrc#L172'&gt;line 172&lt;/a&gt; and the problem disappeared. I haven&amp;#8217;t had time to turn this into a patch, sorry.&lt;/p&gt;

&lt;p&gt;Oh, and while Janus comes with a plugin that highlights trailing whitespace, it doesn&amp;#8217;t automatically clear it when you save. I put this in my &lt;code&gt;~/.vimrc.local&lt;/code&gt; file to never have to worry about that again:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;autocmd BufWritePre * :%s/\s\+$//e&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id='click_click'&gt;Click, click?&lt;/h3&gt;

&lt;p&gt;What I found amazing is how fast I started to get used to switching files without clicking and using the drawer. Janus installed Command-T and I started to use that most of the time. It can even open files in new tabs by pressing &lt;code&gt;^+T&lt;/code&gt; instead of Enter after selecting the file you need. Right now, I&amp;#8217;m keeping the drawer closed by default and I&amp;#8217;m using&lt;code&gt; &#x2318;+&#x21e7;+D&lt;/code&gt; to pop it out whenever I need it.&lt;/p&gt;

&lt;p&gt;Like last time, I still have a lot to learn &amp;#8211; both about Vim itself and the plugins I have installed &amp;#8211; but I think my setup is pretty good now. Have any great tips to make this even better? Let me know in the comments!&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/JZkpqsTeFkM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/vim-is-hard-i-just-want-to-click-around</feedburner:origLink></entry>
  
  <entry>
    <title type="html">The pain of JSON API testing</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/yVKJBxB43lw/the-pain-of-json-api-testing" />
    
    <id>http://jeffkreeftmeijer.com/2011/the-pain-of-json-api-testing</id>
    <updated>2011-07-25T00:00:00+02:00</updated>
    <summary>Is JSON API testing really that painful or are we just using the wrong tools for the job?</summary>
    <content type="html">
      
        &lt;p&gt;I&amp;#8217;m a little behind on my blog feeds, so I didn&amp;#8217;t read &lt;a href='http://collectiveidea.com/'&gt;Collective Idea&lt;/a&gt;&amp;#8217;s &lt;a href='http://collectiveidea.com/blog/archives/2011/07/12/test-your-api-with-cucumber-and-json_spec/'&gt;article&lt;/a&gt; about &lt;a href='https://github.com/collectiveidea/json_spec'&gt;json_spec&lt;/a&gt; until yesterday. They created a gem which provides some RSpec matchers and Cucumber steps to do JSON API testing, since &amp;#8220;They can be a joy to build but a pain to test&amp;#8221;. In this article, I want to take a step back and see exactly how painful it is to test a JSON API.&lt;/p&gt;

&lt;p&gt;Since I don&amp;#8217;t agree that testing an API with Cucumber is a good idea, we&amp;#8217;ll do it in plain RSpec. I&amp;#8217;ll get back on that at the end of this article.&lt;/p&gt;

&lt;p&gt;So, let&amp;#8217;s say we have a Rails application and our client &amp;#8211; or our users &amp;#8211; want an API to fetch and create data. We&amp;#8217;ll need an API with a user index and a create action for that, so let&amp;#8217;s get started with the index. Please note that I&amp;#8217;m &lt;em&gt;not&lt;/em&gt; checking response codes and not doing anything with authentication. You&amp;#8217;ll probably have to, so your spec will turn out a bit longer than the example below, but stuff like that is beyond the scope of this article.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='vi'&gt;@users&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;
    &lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;create!&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Alice&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:login&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;alice&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt;
    &lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;create!&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Bob&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:login&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;bob&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='o'&gt;]&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;context&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;fetching the list of users&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;

  &lt;span class='n'&gt;subject&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='n'&gt;get&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;/api/users.json&amp;#39;&lt;/span&gt;
    &lt;span class='no'&gt;JSON&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;parse&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;response&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;body&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;should return a list of users&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;
      &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Alice&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:login&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;alice&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;
      &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Bob&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:login&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;bob&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='o'&gt;]&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;First, in the &lt;code&gt;before&lt;/code&gt; block, we create two users, since we need something to fetch from our user index API. After that, we use the &lt;code&gt;get&lt;/code&gt; method to do a GET request to &lt;code&gt;/api/users.json&lt;/code&gt;. The response body it returns is a string and we won&amp;#8217;t hurt ourselves by trying to do comparisons on that, so we convert it into a Ruby hash using &lt;code&gt;JSON.parse&lt;/code&gt;. Since Ruby 1.9 has ordered hashes now, we can simply compare the JSON response hash to our desired result which we put in another hash.&lt;/p&gt;

&lt;p&gt;That&amp;#8217;s quite simple right? Let&amp;#8217;s create a quick API action that allows us to create a user.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='n'&gt;context&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;creating a new user&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;

      &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;should add one user&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
        &lt;span class='nb'&gt;lambda&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;/api/users.json&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:user&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Charlie&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:login&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;charlie&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='p'&gt;}&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;change&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:count&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;by&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;

      &lt;span class='n'&gt;context&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;after creating, the new user&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;

        &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
          &lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;/api/users.json&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:user&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Charlie&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:login&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;charlie&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
          &lt;span class='vi'&gt;@user&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;last&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;

        &lt;span class='n'&gt;subject&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='no'&gt;JSON&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;parse&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;response&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;body&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;

        &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;should have the correct name and login&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
          &lt;span class='vi'&gt;@user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;name&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Charlie&amp;#39;&lt;/span&gt;
          &lt;span class='vi'&gt;@user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;login&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;charlie&amp;#39;&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;

        &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;should be returned&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
          &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;login&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;charlie&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Charlie&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;

      &lt;span class='k'&gt;end&lt;/span&gt;

    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In our first spec, we use &lt;code&gt;post&lt;/code&gt; to POST some data to the same URL we used above, which will create a new user. We test if that really happens by asking the &lt;code&gt;User&lt;/code&gt; model if its &lt;code&gt;count&lt;/code&gt; changed by 1.&lt;/p&gt;

&lt;p&gt;Making sure a user is created is not enough, we need to test if our new user has the correct name and login values. In the second spec, we do just that by fetching the last user &amp;#8211; since we already know a user was created &amp;#8211; and checking its &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;login&lt;/code&gt; methods.&lt;/p&gt;

&lt;p&gt;The last thing we want our user creation API to do is to return our new user. Like the user index spec above, we parse the response body and compare is to another hash.&lt;/p&gt;

&lt;p&gt;See how easy that was? JSON might be a difficult format to do tests on, but hashes aren&amp;#8217;t. If you convert that JSON string to something we can actually work with, the pain goes away quickly and we&amp;#8217;re off testing our API.&lt;/p&gt;

&lt;h3 id='cucumber_why'&gt;Cucumber? Why?&lt;/h3&gt;

&lt;p&gt;If you&amp;#8217;ve been here before, you probably know I&amp;#8217;m not Cucumber&amp;#8217;s greatest fan, but let&amp;#8217;s put that aside for a bit. If you ask me, Cucumber&amp;#8217;s greatest feature is what it calls its &amp;#8220;business readable DSL&amp;#8221;, which allows you to write your tests in English. These tests are understandable for non-technical stakeholders, so they can read what the code is doing. A test written with json_spec&amp;#8217;s Cucumber steps doesn&amp;#8217;t really do that, since it&amp;#8217;s full of domain specific stuff:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='cucumber'&gt;&lt;span class='nf'&gt;    &lt;/span&gt;&lt;span class='k'&gt;Scenario:&lt;/span&gt;&lt;span class='nf'&gt; User list&lt;/span&gt;
&lt;span class='k'&gt;      Given &lt;/span&gt;&lt;span class='nf'&gt;I post to &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;/users.json&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; with:&lt;/span&gt;
&lt;span class='nf'&gt;        &lt;/span&gt;&lt;span class='k'&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class='s' /&gt;
&lt;span class='s'&gt;        {&lt;/span&gt;
&lt;span class='s'&gt;          &amp;quot;first_name&amp;quot;: &amp;quot;Steve&amp;quot;,&lt;/span&gt;
&lt;span class='s'&gt;          &amp;quot;last_name&amp;quot;: &amp;quot;Richert&amp;quot;&lt;/span&gt;
&lt;span class='s'&gt;        }&lt;/span&gt;
&lt;span class='s'&gt;        &lt;/span&gt;&lt;span class='k'&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class='nf' /&gt;
&lt;span class='nf'&gt;      &lt;/span&gt;&lt;span class='k'&gt;And &lt;/span&gt;&lt;span class='nf'&gt;I keep the JSON response at &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;id&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; as &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;USER_ID&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot;&lt;/span&gt;
&lt;span class='nf'&gt;      &lt;/span&gt;&lt;span class='k'&gt;When &lt;/span&gt;&lt;span class='nf'&gt;I get &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;/users.json&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot;&lt;/span&gt;
&lt;span class='nf'&gt;      &lt;/span&gt;&lt;span class='k'&gt;Then &lt;/span&gt;&lt;span class='nf'&gt;the JSON response should have &lt;/span&gt;&lt;span class='s'&gt;1&lt;/span&gt;&lt;span class='nf'&gt; user&lt;/span&gt;
&lt;span class='nf'&gt;      &lt;/span&gt;&lt;span class='k'&gt;And &lt;/span&gt;&lt;span class='nf'&gt;the JSON response at &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;0&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; should be:&lt;/span&gt;
&lt;span class='nf'&gt;        &lt;/span&gt;&lt;span class='k'&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class='s' /&gt;
&lt;span class='s'&gt;        {&lt;/span&gt;
&lt;span class='s'&gt;          &amp;quot;id&amp;quot;: %{USER_ID},&lt;/span&gt;
&lt;span class='s'&gt;          &amp;quot;first_name&amp;quot;: &amp;quot;Steve&amp;quot;,&lt;/span&gt;
&lt;span class='s'&gt;          &amp;quot;last_name&amp;quot;: &amp;quot;Richert&amp;quot;&lt;/span&gt;
&lt;span class='s'&gt;        }&lt;/span&gt;
&lt;span class='s'&gt;        &lt;/span&gt;&lt;span class='k'&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class='nf' /&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&amp;#8220;A step description should never contain regexen, CSS or XPath selectors, any kind of code or data structure. It should be easily understood just by reading the description.&amp;#8221; &amp;#8211; &lt;a href='http://elabs.se/blog/15-you-re-cuking-it-wrong'&gt;You&amp;#8217;re Cuking it wrong&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you &lt;em&gt;have&lt;/em&gt; to use data structures in your tests, maybe it&amp;#8217;s a good idea to put Cucumber aside for a bit. No matter how much you like using it.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/yVKJBxB43lw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/the-pain-of-json-api-testing</feedburner:origLink></entry>
  
  <entry>
    <title type="html">&amp;ldquo;Let's settle this in a Ruby programming contest&amp;rdquo;</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/qHn5NEbMebc/lets-settle-this-in-a-ruby-programming-contest" />
    
    <id>http://jeffkreeftmeijer.com/2011/lets-settle-this-in-a-ruby-programming-contest</id>
    <updated>2011-06-27T00:00:00+02:00</updated>
    <summary>Introducing Codebrawl, a Ruby and open source programming contest website</summary>
    <content type="html">
      
        &lt;p&gt;Last week, I launched &lt;a href="http://codebrawl.com"&gt;Codebrawl&lt;/a&gt;, a code contest website focussed on the Ruby programming language and open source software.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://codebrawl.com"&gt;&lt;img src="http://jeffkreeftmeijer.com/images/codebrawl.png" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A typical contest will introduce the contestants to an open source project to solve a specific problem. For example, the first challenge was to &lt;a href="http://codebrawl.com/contests/rspec-formatters"&gt;write an RSpec formatter&lt;/a&gt;. We got some great entries that scrambled the whole test output to annoy your colleagues, give you a special prize after all your specs passed, or run RCov in the background to make sure your test coverage stays great.&lt;/p&gt;
&lt;p&gt;The contestants are always encouraged to keep working on their projects after the contest, to try to make sure others in the Ruby community benefit from what&amp;#8217;s created at Codebrawl.&lt;/p&gt;
&lt;p&gt;Of course, we&amp;#8217;ll also do the occasional puzzle or give the contestants a specific task. This week, we&amp;#8217;re &lt;a href="http://codebrawl.com/contests/pixelizing-images-with-chunkypng"&gt;introducing the contestants to ChunkyPNG&lt;/a&gt;, by letting them pixelate an image of a Tapir. I&amp;#8217;m excited to see how this turns out and maybe we can even find something to contribute back to ChunkyPNG.&lt;/p&gt;
&lt;h3&gt;How it works&lt;/h3&gt;
&lt;p&gt;The contests are open to everyone with a Github account. If you want to enter, just put your solution in a &lt;a href="https://gist.github.com"&gt;Gist&lt;/a&gt; and submit it. Codebrawl will make sure your entry stays hidden until the voting period starts.&lt;/p&gt;
&lt;p&gt;After everyone submitted their entries, we&amp;#8217;ll show everything on the contest page and allow the other users to vote for the entry they like best. They won&amp;#8217;t see the contestants&amp;#8217; names just yet, because we don&amp;#8217;t want it to become a popularity contest.&lt;/p&gt;
&lt;p&gt;After you&amp;#8217;re done voting, the contestants&amp;#8217; names are revealed and we&amp;#8217;ll show the entry&amp;#8217;s comments (we take the Gist&amp;#8217;s comments for that), to allow the contestants and users to discuss the entries. After the voting period is over, we&amp;#8217;ll announce a winner, give out points and put your face in the hall of fame we&amp;#8217;re still working on.&lt;/p&gt;
&lt;h3&gt;Codebrawl needs your help&lt;/h3&gt;
&lt;p&gt;We&amp;#8217;re always looking for good contest ideas, so if you know something or have some open source project you&amp;#8217;d like us to have a play with, please &lt;a href="http://codebrawl.com/submissions/new"&gt;submit it&lt;/a&gt;. Also, we&amp;#8217;re looking for prize sponsors, so if you have some app credit you want to give out or some swag you want to send across the world, be sure to &lt;a href="jeffkreeftmeijer.com/contact"&gt;let me know&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have any comments, suggestions or ideas, please leave a comment below or create &lt;a href="https://github.com/codebrawl/codebrawl/issues"&gt;an issue on Github&lt;/a&gt; and be sure to participate in &lt;a href="http://codebrawl.com/contests/pixelizing-images-with-chunkypng"&gt;this week&amp;#8217;s contest&lt;/a&gt;. Good luck!&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/qHn5NEbMebc" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/lets-settle-this-in-a-ruby-programming-contest</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Euruko: The small and informal European Ruby conference</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/Nlv03ZOqNcQ/euruko-the-small-and-informal-european-ruby-conference" />
    
    <id>http://jeffkreeftmeijer.com/2011/euruko-the-small-and-informal-european-ruby-conference</id>
    <updated>2011-06-07T00:00:00+02:00</updated>
    <summary>About Euruko 2011 in Berlin and some thoughts about the Amsterdam edition next year</summary>
    <content type="html">
      
        &lt;p&gt;I know I&amp;#8217;m a bit late to the party and I wasn&amp;#8217;t really planning on writing about &lt;a href="http://euruko2011.org"&gt;Euruko&lt;/a&gt; this time, but after reading &lt;a href="http://twitter.com/#!/nusco" title="Paolo Perrotta"&gt;@nusco&lt;/a&gt;&amp;#8217;s &lt;a href="http://ducktypo.blogspot.com/2011/06/euruko-2011-thoughts.html"&gt;thoughts&lt;/a&gt;, I decided to throw my two cents in the mix.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/der_robert/5773702901/in/photostream/"&gt;&lt;img src="http://jeffkreeftmeijer.com/images/euruko2011.jpg" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We &amp;#8212; that&amp;#8217;s &lt;a href="http://80beans.com"&gt;80beans&lt;/a&gt; &amp;#8212; wanted to take three people to go to Berlin for Euruko. After completely missing the first ticket run and failing in the second, we put our hopes on the last one. Since that one was sold out in a split second too, we managed to get only one ticket. We were a bit bummed out, but we decided to go to Berlin anyway, since it seemed like it was going to be a great trip.&lt;/p&gt;
&lt;p&gt;After tweeting about not having a ticket, it didn&amp;#8217;t take long before &lt;a href="http://twitter.com/rymai" title="Rémy Coutable"&gt;@rymai&lt;/a&gt;, replied saying that &lt;a href="http://twitter.com/rymai/status/45531844854292480"&gt;he had one left&lt;/a&gt;. I sent him my name and address and he bought the ticket for me. Just one week before we left, &lt;a href="http://twitter.com/matsimitsu" title="Robert Beekman"&gt;@matsimitsu&lt;/a&gt; somehow got his ticket from somebody too. This meant we were able to get all of us into the conference after all.&lt;/p&gt;
&lt;p&gt;The conference was amazing, although we missed quite a few talks because waking up got harder every morning. We didn&amp;#8217;t end up at &lt;a href="http://eurucamp.org/"&gt;Eurucamp&lt;/a&gt; at all, but I&amp;#8217;ve been told that was great too. I believe we would have been just fine if we didn&amp;#8217;t all get tickets to Euruko because of that.&lt;/p&gt;
&lt;h3&gt;Should Euruko &amp;#8220;scale&amp;#8221;?&lt;/h3&gt;
&lt;p&gt;Personally, I don&amp;#8217;t agree &lt;a href="http://ducktypo.blogspot.com/2011/06/euruko-2011-thoughts.html"&gt;Euruko should get bigger&lt;/a&gt; next year, I loved the atmosphere and the relaxed feel of the one-track setup. The venue was amazing, the screen was &lt;a href="http://www.flickr.com/photos/mfilej/5768540968"&gt;huge&lt;/a&gt; and even the &lt;a href="http://euruko2011.org/2011/05/26/such-great-heights.html"&gt;wifi&lt;/a&gt; was great, too.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/der_robert/5773703421/in/photostream/"&gt;&lt;img src="http://jeffkreeftmeijer.com/images/euruko2011_amsterdam.jpg" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="small"&gt;&lt;a href="http://amsterdam-rb.org"&gt;Amsterdam.rb&lt;/a&gt; is getting to organize Euruko next year.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Of course, doing it like that next year &amp;#8212; &lt;a href="http://amsterdam-rb.org"&gt;Amsterdam.rb&lt;/a&gt; will organize it &amp;#8212; will mean a lot of people won&amp;#8217;t be able to attend the conference, but that&amp;#8217;s a result of trying to keep it small and informal. To make this a big Ruby event, why don&amp;#8217;t we focus on organizing great things &lt;em&gt;around&lt;/em&gt; the conference?&lt;/p&gt;
&lt;p&gt;This year&amp;#8217;s Eurucamp was an amazing idea, and I believe it got a lot of people to Berlin even though they didn&amp;#8217;t have a ticket to the main conference.  Having all these people together, combined with well-organized parties and a Github drinkup on Sunday, made sure everyone &amp;#8212; even people without tickets to the main conference &amp;#8212; had a great time and a chance to meet up with each other. I think that&amp;#8217;s &lt;em&gt;exactly&lt;/em&gt; what going to a conference is about.&lt;/p&gt;
&lt;p&gt;For next year, let&amp;#8217;s focus on having a great time in Amsterdam, rather than just the conference. Let&amp;#8217;s have places and events to hack, great parties, an open-space event like Eurucamp and a lot of fun.&lt;/p&gt;
&lt;h3&gt;Tickets&lt;/h3&gt;
&lt;p&gt;The three ticket runs for this year&amp;#8217;s Euruko were gone in seconds, so we need to rethink how we&amp;#8217;re going to do this for next year. We&amp;#8217;re planning on having a pre-registration to have an idea of how many people want to come and maybe we&amp;#8217;ll make some tickets available for European user groups so we&amp;#8217;re sure to have a diverse audience from all over Europe.&lt;/p&gt;
&lt;p&gt;If you have a great idea for Euruko 2012, or have an idea to smoothen out the ticket selling process a bit, be sure to let me know in the comments. To the Euruko 2011 organizers: I had an amazing time, let&amp;#8217;s hope next year will be as awesome in Amsterdam. I hope to see you all then!&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/Nlv03ZOqNcQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/euruko-the-small-and-informal-european-ruby-conference</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Managing Heroku's workers with HireFire</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/IqKKb8aUSgI/managing-herokus-workers-with-hirefire" />
    
    <id>http://jeffkreeftmeijer.com/2011/managing-herokus-workers-with-hirefire</id>
    <updated>2011-05-25T00:00:00+02:00</updated>
    <summary>HireFire fires idle workers, and hires them when needed, saving you money and enables faster job processing.</summary>
    <content type="html">
      
        
          &lt;p&gt;This is a guest post written by &lt;a href="http://twitter.com/meskyanichi" title="Michael van Rooijen"&gt;@meskyanichi&lt;/a&gt;. Michael is a Ruby programmer and &lt;a href="https://github.com/meskyanichi"&gt;open source contributor&lt;/a&gt;, who's turning his open source HireFire gem into a hosted service.
        &lt;p&gt;As you are most certainly already aware, &lt;a href="http://heroku.com/"&gt;Heroku&lt;/a&gt; is a platform dedicated to hosting Ruby (or Rack) applications. One of the reasons you might consider Heroku is when you:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;don&amp;#8217;t know how, or don&amp;#8217;t want to provision and manage servers&lt;/li&gt;
	&lt;li&gt;don&amp;#8217;t want to spend time on maintaining or fine-tuning servers (and software)&lt;/li&gt;
	&lt;li&gt;want to be up and running in minutes, rather than hours or days&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Heroku also provides add-ons that hook up internal, and 3rd party services with your application automatically!&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/hirefire.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;As appealing as it sounds, in the short run you&amp;#8217;ll find yourself spending a lot of money on resources which you would otherwise get a lot cheaper elsewhere, like with &lt;span class="caps"&gt;VPS&lt;/span&gt; or dedicated hosting.&lt;/p&gt;
&lt;p&gt;One of those resources, is the &lt;a href="http://www.heroku.com/pricing#1-24"&gt;Heroku Worker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When you run a large business, generating plenty of income, such things matter less. However, when you have a bunch of small-medium web applications, be it startups, clients or personal sites, it becomes quite expensive early on for applications that don&amp;#8217;t generate (enough) monthly revenue to pay such fees.&lt;/p&gt;
&lt;h3&gt;Enter: HireFire &amp;#8211; The Heroku Worker Manager&lt;/h3&gt;
&lt;p&gt;A while back I released &lt;a href="https://github.com/meskyanichi/hirefire"&gt;HireFire&lt;/a&gt;, an open source RubyGem to address this issue. As the title suggests, HireFire monitors and manages your applications on Heroku. It will shut down workers when there aren&amp;#8217;t any jobs, and will then spin up new ones again when jobs get enqueued. This can save you a significant amount of money since workers will no longer run full-time. Not only that, but it&amp;#8217;ll also enable you to process jobs at a higher concurrency because of the ability to automatically scale up and down.&lt;/p&gt;
&lt;p&gt;It has been working great, but since the performance and reliability of my open source solution didn&amp;#8217;t quite cut it, due to Heroku&amp;#8217;s platform limitations, I decided to release it as a hosted service. I gave it a nice interface for easily defining how you want to scale your workers. For example, you could configure it to spin up two workers when the queue size hits 15, and four workers when the queue size hits 30, and so forth. This is nice because you can configure it based on how long the average job of your application takes.&lt;/p&gt;
&lt;p&gt;If you want to give it a try, head over to &lt;a href="http://hirefireapp.com"&gt;HireFire&lt;/a&gt; and set up your worker manager. If you have any questions, check out the &lt;a href="http://hirefireapp.tenderapp.com/kb"&gt;knowledge base&lt;/a&gt; or post a comment below.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/IqKKb8aUSgI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/managing-herokus-workers-with-hirefire</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Introducing Tapir: Simple search for static sites</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/TA1k9nu_fv4/introducing-tapir-simple-search-for-static-sites" />
    
    <id>http://jeffkreeftmeijer.com/2011/introducing-tapir-simple-search-for-static-sites</id>
    <updated>2011-05-09T00:00:00+02:00</updated>
    <summary>Wouldn't it be nice to have a service that indexes your RSS feed and allows you to search it using a JSON API?</summary>
    <content type="html">
      
        &lt;p class="small"&gt;
&lt;strong&gt;Note:&lt;/strong&gt; I stopped working at &lt;a href="http://80beans.com"&gt;80beans&lt;/a&gt;, so I can&amp;#8217;t help you with any issues anymore. You can always send an e-mail to &lt;a href="mailto:help@tapirgo.com"&gt;Tapir&amp;#8217;s support e-mail address&lt;/a&gt; if you need help, though.
&lt;/p&gt;
&lt;p&gt;Static site generators like &lt;a href="https://github.com/mojombo/jekyll"&gt;Jekyll&lt;/a&gt; allow you to generate your whole website or blog as static &lt;span class="caps"&gt;HTML&lt;/span&gt; files and put it online without having to use a database or even building an actual application.&lt;/p&gt;
&lt;p&gt;While being static has a lot of advantages, it can be quite a pain in some situations. Since you don&amp;#8217;t have a database, you can&amp;#8217;t build a commenting system so your readers can comment on what you write, for example. Some other things are analytics and contact forms. Luckily, there are really nice solutions &amp;#8212; like &lt;a href="http://discus.com"&gt;Disqus&lt;/a&gt;, &lt;a href="http://google.com/analytics"&gt;Google analytics&lt;/a&gt; and &lt;a href="http://wufoo.com"&gt;Wufoo&lt;/a&gt; &amp;#8212; that allow you to implement these features using a simple javascript snippet.&lt;/p&gt;
&lt;p&gt;A feature I really wanted in my website was search, since the &lt;a href="/archive"&gt;archive&lt;/a&gt; grew quite a bit and it became impossible to find an article without knowing its title, so I started looking for a solution I could use.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://tapirgo.com"&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapirgo.png" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After &lt;a href="http://twitter.com/#!/jkreeftmeijer/status/62928349399490560"&gt;asking around&lt;/a&gt;, it turned out &lt;a href="http://www.google.com/cse/"&gt;Google&amp;#8217;s custom search&lt;/a&gt; is still the most used out there. The problem I ran into was that it takes you off the website you were searching on and takes you to a Google results page (complete with slightly irrelevant ads).&lt;/p&gt;
&lt;p&gt;I wanted a more elegant solution, with a nice &lt;span class="caps"&gt;JSON&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt; that would allow me to build a search feature on my website using only javascript, but I found out that most of the existing search engines didn&amp;#8217;t have an &lt;span class="caps"&gt;API&lt;/span&gt;, made me put their logo on my website or gave me a request limit.&lt;/p&gt;
&lt;p&gt;After discussing this at &lt;a href="http://80beans.com"&gt;80beans&lt;/a&gt;, we decided to get some beers, order dinner and throw something together ourselves. After a couple of hours, we had a working prototype and we asked &lt;a href="http://twitter.com/ivanasetiawan" title="Ivana Setiawan"&gt;@ivanasetiawan&lt;/a&gt; to draw us an awesome Tapir and create a nice design for the website. We took a week to tweak and test the application before we released it into the wild.&lt;/p&gt;
&lt;h3&gt;Tapir, go!&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://tapirgo.com"&gt;Tapir&lt;/a&gt; is a simple application that indexes your &lt;span class="caps"&gt;RSS&lt;/span&gt; feed and uses &lt;a href="https://github.com/karmi/tire"&gt;Tire&lt;/a&gt; (which is powered by &lt;a href="http://www.elasticsearch.org"&gt;Elasticsearch&lt;/a&gt;, which is powered by &lt;a href="http://lucene.apache.org"&gt;Lucene&lt;/a&gt;) to index and search it. It gives you a straightforward &lt;span class="caps"&gt;JSON&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt; that returns the results.&lt;/p&gt;
&lt;p&gt;After signing up and getting your token, you&amp;#8217;ll be able to use this &lt;span class="caps"&gt;API&lt;/span&gt; to search your site. For example, here&amp;#8217;s one of my results when &lt;a href="http://tapirgo.com/api/1/search.json?token=4dbfc79e3f61b05b53000011&amp;amp;query=capybara"&gt;searching for &amp;ldquo;Capybara&amp;rdquo;&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[
  {
      "title":"Capybara ate Swinger",
      "published_on":"2011-02-07T05:00:00Z",
      "content": [the full article content],
      "link":"http://jeffkreeftmeijer.com/2011/capybara-ate-swinger",
      "summary":"Remember Swinger, the Capybara RSpec driver swapper? Capybara can now swap drivers out of the box.",
      "_score":61.15513
    }
  ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tapir will check your feed every fifteen minutes to see if there&amp;#8217;s anything new to index, but of course you can also ping it to update your search results immediately.&lt;/p&gt;
&lt;p&gt;Using that, you can build your own search feature, which requests its results from Tapir&amp;#8217;s &lt;span class="caps"&gt;API&lt;/span&gt;. If you want to give it a go, check out &lt;a href="http://jeffkreeftmeijer.com/search/?query=capybara"&gt;the one I implemented&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;What&amp;#8217;s next?&lt;/h3&gt;
&lt;p&gt;Over the last week we&amp;#8217;ve been busy tweaking the search results and making the application more usable. We have a couple of plans to make Tapir more awesome, but we definitely need your help. If you start using Tapir, have a great idea or if you run into any issues, &lt;a href="http://twitter.com/tapirgo" title="Tapir"&gt;@tapirgo&lt;/a&gt; would be glad to hear about it or try help you out.&lt;/p&gt;
&lt;p&gt;While we built Tapir to search static sites, you can use it for anything that has an &lt;span class="caps"&gt;RSS&lt;/span&gt; feed, of course. &lt;a href="http://twitter.com/thedjinn" title="Emil Loer"&gt;@thedjinn&lt;/a&gt; created a &lt;a href="http://emilloer.com/2011/05/04/searching-the-news-with-tapir/"&gt;news search&lt;/a&gt; that searches multiple feeds, like &lt;span class="caps"&gt;CNN&lt;/span&gt; and the Dutch &lt;span class="caps"&gt;NOS&lt;/span&gt;. If you end up doing something awesome we hadn&amp;#8217;t thought about before, be sure to let us know.&lt;/p&gt;
&lt;p&gt;Now, what do &lt;em&gt;you&lt;/em&gt; think of Tapir?&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/TA1k9nu_fv4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/introducing-tapir-simple-search-for-static-sites</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Comparing images and creating image diffs</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/HzqKpxtFvWk/comparing-images-and-creating-image-diffs" />
    
    <id>http://jeffkreeftmeijer.com/2011/comparing-images-and-creating-image-diffs</id>
    <updated>2011-04-18T00:00:00+02:00</updated>
    <summary>Using pure Ruby to find the differences in two versions of an image.</summary>
    <content type="html">
      
        &lt;p&gt;I&amp;#8217;m sure you&amp;#8217;ve seen the &lt;a href="https://github.com/blog/817-behold-image-view-modes"&gt;image view modes&lt;/a&gt; Github released last month. It&amp;#8217;s a really nice way to see the differences between two versions of an image. In this article, I&amp;#8217;ll try to explain how a simple image diff could be built using pure Ruby and &lt;a href="https://github.com/wvanbergen/chunky_png"&gt;ChunkyPNG&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you need a more basic introduction to working with pixel data in ChunkyPNG, check out &lt;a href="http://jeffkreeftmeijer.com/2011/pure-ruby-colored-blob-detection"&gt;last week&amp;#8217;s article&lt;/a&gt;, which I did some simple blob detection.&lt;/p&gt;
&lt;p&gt;In its simplest form, finding differences in images works by looping over each pixel in the first image and checking if it&amp;#8217;s the same as the pixel in the same spot in the second image. An implementation might look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;chunky_png&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;

&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each_with_index&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;pixel&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;pixels (total):     &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;pixels changed:     &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;pixels changed (%): &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_f&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;diff.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;span class="small"&gt;Want the code? Here&amp;#8217;s a &lt;a href="https://gist.github.com/923894"&gt;Gist&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After loading in the two images, we&amp;#8217;ll loop over the pixels of the first one. If the pixel is the same as the one in the second image, we&amp;#8217;ll add it to the &lt;code&gt;diff&lt;/code&gt; array. When we&amp;#8217;re done, we&amp;#8217;ll draw a bounding box around the area that contains the changes:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir.png" style="width:130px; margin:0 5px;"/&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir_hat.png" style="width:130px; margin:0 5px;"/&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir_diff_box.png" style="width:130px; margin:0 5px;"/&gt;&lt;/p&gt;
&lt;p&gt;It worked! The result image has a bounding box around the hat we added to the image and the output tells us that almost 9% of the pixels in the image changed, which seems about right.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pixels (total):     16900
pixels changed:     1502
pixels changed (%): 8.887573964497042%&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A problem with this approach is that it only &lt;em&gt;detects&lt;/em&gt; change, without &lt;em&gt;measuring&lt;/em&gt; it. It doesn&amp;#8217;t care if the pixel it&amp;#8217;s looking at is just a bit darker or a completely different color. If we use this code to compare one image to a slightly darker version of itself, the result will look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir_hat.png" style="width:130px; margin:0 5px;"/&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir_hat_dark.png" style="width:130px; margin:0 5px;"/&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir_diff_dark_box.png" style="width:130px; margin:0 5px;"/&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pixels (total):     16900
pixels changed:     16900
pixels changed (%): 100.0%&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would mean that the two images are completely different, while (from a human eye&amp;#8217;s perspective) they&amp;#8217;re almost the same. To get a more accurate result, we&amp;#8217;ll need to measure the difference in the pixels&amp;#8217; colors.&lt;/p&gt;
&lt;h3&gt;Calculating color difference&lt;/h3&gt;
&lt;p&gt;To calculate the color difference, we&amp;#8217;ll use the the &lt;a href="http://en.wikipedia.org/wiki/Color_difference"&gt;&#x394;E*&lt;/a&gt; (&amp;ldquo;Delta E&amp;rdquo;) distance metric. There are a couple of different versions of this metric, but we&amp;#8217;ll take the first one (&lt;a href="http://en.wikipedia.org/wiki/Color_difference#CIE76"&gt;CIE76&lt;/a&gt;), since it&amp;#8217;s the simplest and we don&amp;#8217;t need anything too fancy. The &#x394;E* metric was created for the &lt;a href="http://en.wikipedia.org/wiki/Lab_color_space"&gt;&lt;span class="caps"&gt;LAB&lt;/span&gt; color space&lt;/a&gt;, which was designed to approximate human vision. In this example, we&amp;#8217;re not going to worry about converting to &lt;span class="caps"&gt;LAB&lt;/span&gt;, so we&amp;#8217;ll just use the &lt;span class="caps"&gt;RGB&lt;/span&gt; color space (note that this will mean our results will be less accurate). If you want to know more about the difference, check out &lt;a href="http://stevehanov.ca/blog/index.php?id=116"&gt;this demo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Again, we loop over every pixel in the images. If they&amp;#8217;re different, we calculate how different they are using the &#x394;E* metric and store that in the &lt;code&gt;diff&lt;/code&gt; array. We also use that score to calculate a grayscale color value we use on the result image:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;chunky_png&amp;#39;&lt;/span&gt;
&lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Color&lt;/span&gt;


&lt;span class="n"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;WHITE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;

&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each_with_index&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;pixel&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="no"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MAX&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grayscale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MAX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;pixels (total):     &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;pixels changed:     &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;image changed (%): &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; / images.first.pixels.length) * 100}%&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;diff.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;span class="small"&gt;Want the code? Here&amp;#8217;s a &lt;a href="https://gist.github.com/925244"&gt;Gist&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now we have a more accurate difference score. If we look at the output, we can see that less than 3% of the image was changed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pixels (total):    16900
pixels changed:    1502
image changed (%): 2.882157784948056%&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, a diff image is saved. This time, it shows the differences using shades of gray. Bigger changes are darker:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir.png" style="width:130px; margin:0 5px;"/&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir_hat.png" style="width:130px; margin:0 5px;"/&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir_diff_delta_e.png" style="width:130px; margin:0 5px;"/&gt;&lt;/p&gt;
&lt;p&gt;Now, let&amp;#8217;s try the two images where the second one is slightly darker:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pixels (total):    16900
pixels changed:    16900
image changed (%): 5.4418255392228945%&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir_hat.png" style="width:130px; margin:0 5px;"/&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir_hat_dark.png" style="width:130px; margin:0 5px;"/&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir_diff_delta_e_dark.png" style="width:130px; margin:0 5px;"/&gt;&lt;/p&gt;
&lt;p&gt;Great. Now our code knows that the images are only darker, not completely different. If you look closely, you can see the difference in the result image.&lt;/p&gt;
&lt;h3&gt;What about Github?&lt;/h3&gt;
&lt;p&gt;Github uses a &lt;a href="http://en.wikipedia.org/wiki/Blend_modes#Difference"&gt;difference blend&lt;/a&gt;, which might be familiar if you&amp;#8217;ve worked with image-editing software like Photoshop before. Doing something like that is quite simple. We loop over every pixel in the two images and calculate their difference per &lt;span class="caps"&gt;RGB&lt;/span&gt; channel:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;chunky_png&amp;#39;&lt;/span&gt;
&lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Color&lt;/span&gt;

&lt;span class="n"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each_with_index&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

    &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;diff.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;span class="small"&gt;Want the code? Here&amp;#8217;s a &lt;a href="https://gist.github.com/924996"&gt;Gist&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Using that, comparing the two images to the left would result in the diff-image on the right, nicely showing what changed:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir.png" style="width:130px; margin:0 5px;"/&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir_hat.png" style="width:130px; margin:0 5px;"/&gt;&lt;img src="http://jeffkreeftmeijer.com/images/tapir_diff_blend.png" style="width:130px; margin:0 5px;"/&gt;&lt;/p&gt;
&lt;p&gt;Because the colors are compared by channel (R,G and B) instead of as one color, three scores are returned. This means the output image is in color, but comparing the channels separately can make the result less accurate.&lt;/p&gt;
&lt;p&gt;As always, if you used this idea to build something yourself, know of a way to improve the code or have some questions or tips, be sure to let me know. If you want to know more about something I talked about, be sure to suggest it as a next article.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/HzqKpxtFvWk" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/comparing-images-and-creating-image-diffs</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Pure Ruby colored blob detection</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/cnEneThuOXo/pure-ruby-colored-blob-detection" />
    
    <id>http://jeffkreeftmeijer.com/2011/pure-ruby-colored-blob-detection</id>
    <updated>2011-04-11T00:00:00+02:00</updated>
    <summary>Detecting blobs from an image in pure Ruby, with a bit of ChunkyPNG</summary>
    <content type="html">
      
        &lt;p&gt;Over the last few weeks, I&amp;#8217;ve become increasingly interested in computer vision. After finally getting &lt;a href="http://opencv.willowgarage.com/wiki"&gt;OpenCV&lt;/a&gt; running, playing around with a little face detection and searching for solutions to weird problems that occurred, I decided to dive a bit deeper into computer vision to figure out how it actually works.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/nose_detector.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I started out with blob detection, which simply means detecting, locating and measuring an object in an image. A really simple way of doing something like that (without having to worry about object textures and creepy stuff like that), is to use color to identify blobs. In this article, we&amp;#8217;re going to build a clown&amp;#8217;s nose detector by finding the biggest red blob in an image. We won&amp;#8217;t use any computer vision libraries, the only thing we&amp;#8217;ll need is &lt;a href="https://github.com/wvanbergen/chunky_png"&gt;ChunkyPNG&lt;/a&gt; by &lt;a href="http://twitter.com/wvanbergen" title="Willem van Bergen"&gt;@wvanbergen&lt;/a&gt; (but you can use any library that allows you to loop over a image&amp;#8217;s pixels).&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/bassie.png" style="width:100px; height:100px;"/&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the image we&amp;#8217;ll be working with. If you&amp;#8217;re interested, it&amp;#8217;s Bassie from the dutch circus duo &lt;a href="http://en.wikipedia.org/wiki/Bassie_%26_Adriaan"&gt;Bassie &amp;amp; Adriaan&lt;/a&gt;. The important thing about this image is that the nose is the biggest red object, since we won&amp;#8217;t really be &lt;em&gt;recognizing&lt;/em&gt; things as clowns&amp;#8217; noses, we&amp;#8217;re just &lt;em&gt;detecting&lt;/em&gt; red objects.&lt;/p&gt;
&lt;h3&gt;Binary images&lt;/h3&gt;
&lt;p&gt;First, we load in the image and simplify it to make it easier to work with. Since we&amp;#8217;re only interested in the red pixels, we can turn the original image into a binary one. We go over each of the original image&amp;#8217;s pixels and figure out it&amp;#8217;s redness. If the pixel is red enough (we&amp;#8217;ll use a threshold of 100/255), we give it a value of -1, meaning the pixel is interesting but we still have to process it. We don&amp;#8217;t need to do anything with the other pixels, so we&amp;#8217;ll give those a value of 0, these are the background pixels.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;chunky_png&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bassie.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;working_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dup&lt;/span&gt;

&lt;span class="n"&gt;working_image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map!&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;redness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;redness&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here&amp;#8217;s the working image with the blobs in white and the background pixels in black:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/bassie_blobs.png" style="width:100px; height:100px;"/&gt;&lt;/p&gt;
&lt;p&gt;Now we have an image we can work with, let&amp;#8217;s divide the separate blobs so we can measure them individually to figure out which one the nose is. To do this, we&amp;#8217;ll loop over every pixel in the image and see if it has a -1 value because it still needs to be processed. If it does, we&amp;#8217;ll give it a label and add it to an &lt;code&gt;areas&lt;/code&gt; array.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;areas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="n"&gt;working_image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;working_image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each_with_index&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;areas&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pixel&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you probably figured out already, we&amp;#8217;re assigning each pixel with a -1 value a separate label right now. This means pixels in the same blob get different labels. We don&amp;#8217;t want that, since we want to label &lt;em&gt;groups&lt;/em&gt; of pixels that belong to the same blob. To do that, we need to check the pixel&amp;#8217;s neighbors too.&lt;/p&gt;
&lt;h3&gt;Pixel neighborhoods&lt;/h3&gt;
&lt;p&gt;To be able to find connected pixels, we need to know each pixel&amp;#8217;s neighborhood. For this example we&amp;#8217;ll use a 4-neighborhood, which means we use the pixels right above (north), to the right (east), right below (south) and to the left (west) of the current one. The 8-neighborhood also uses the northeast, southeast, southwest and northwest pixels, but we won&amp;#8217;t really need that for something simple like this.&lt;/p&gt;
&lt;p&gt;We need to keep in mind that some pixels don&amp;#8217;t have four neighbors, like the one in the top left, which doesn&amp;#8217;t have any north or west neighbors because they would fall out of the image. Luckily, ChunkyPNG has some nice methods to help us figure out if pixels actually exist. We&amp;#8217;ll implement a &lt;code&gt;neighbors&lt;/code&gt; method directly into &lt;code&gt;ChunkyPNG::Image&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Image&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;neighbors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]].&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;include_xy?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Using our new neighborhood method, we can improve the blob separator we wrote earlier. We implement a method named &lt;code&gt;label_recursively&lt;/code&gt;, which assigns a label to the current pixel, adds the current pixel to the &lt;code&gt;areas&lt;/code&gt; array, checks its neighbors and calls itself (without changing the label) on the neighbors if their values are -1 too. When none of the neighbors&amp;#8217; values are -1, the loop will stop until the main loop finds another -1-pixel. If that happens, the label gets increased by 1 and the &lt;code&gt;label_recursively&lt;/code&gt; method is called again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;label_recursively&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;areas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;areas&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;neighbors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;[*&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="n"&gt;areas&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;xy&lt;/span&gt;
      &lt;span class="n"&gt;label_recursively&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;areas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;areas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="n"&gt;working_image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;working_image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each_with_index&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;label_recursively&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;working_image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;areas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pixel&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;areas&lt;/code&gt; array holds the blobs as labeled groups of pixels now. If we would color the detected blobs, it would look somewhat like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/bassie_areas.png" style="width:100px; height:100px;"/&gt;&lt;/p&gt;
&lt;p&gt;As you can see, there were some detected areas that overlap and should have been counted as one. This shouldn&amp;#8217;t be a problem for this example, but using an 8-neighborhood can solve some of these issues and make the result more precise, if you end up needing it.&lt;/p&gt;
&lt;p&gt;All we have to do to detect the nose is find out which area is the biggest (which one has the most pixels). Since we stored the areas in the &lt;code&gt;areas&lt;/code&gt; array, this should be straightforward:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;area&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;areas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now, &lt;code&gt;area&lt;/code&gt; holds an array of the pixels in the blob we&amp;#8217;re interested in. Since we want to put a bounding box around the blob we found, we get the highest and lowest x and y values, create a rectangle on the image using ChunkyPNG&amp;#8217;s drawing tools and save the image:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bassie_detected.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And there you go! We successfully detected, located and measured the biggest red blob in the image:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/bassie_detected.png" style="width:100px; height:100px;"/&gt;&lt;/p&gt;
&lt;p&gt;Of course, this method isn&amp;#8217;t fool proof, but it&amp;#8217;s a really simple first step into computer vision. As always, if you used this idea to build something yourself, know of a way to improve the code or have some questions or tips, be sure to let me know. You can find the complete source in &lt;a href="https://gist.github.com/913400"&gt;this Gist&lt;/a&gt;. Have fun!&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/cnEneThuOXo" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/pure-ruby-colored-blob-detection</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Custom ActiveRecord Attribute Serialization</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/3mgkwwx_mkM/index.html" />
    
    <id>http://jeffkreeftmeijer.com/2011/custom-activerecord-attribute-serialization</id>
    <updated>2011-03-09T00:00:00+01:00</updated>
    <summary>Serializing attributes in ActiveRecord has been possible for a while, but in Rails 3.1 you'll be able to use arbitrary objects.</summary>
    <content type="html">
      
        Serializing attributes in ActiveRecord has been possible for a while, but in Rails 3.1 you'll be able to use arbitrary objects.
        
          Published as a guest article for EdgeRails.
        
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/3mgkwwx_mkM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://edgerails.info/articles/what-s-new-in-edge-rails/2011/03/09/custom-activerecord-attribute-serialization/index.html</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Keeping up with the Rails Lighthouse</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/fQsjV_uZY_0/keeping-up-with-the-rails-lighthouse" />
    
    <id>http://jeffkreeftmeijer.com/2011/keeping-up-with-the-rails-lighthouse</id>
    <updated>2011-02-28T00:00:00+01:00</updated>
    <summary>Keeping up with the Rails Lighthouse can be quite a task. Here's what I did to make it a bit more manageable.</summary>
    <content type="html">
      
        &lt;p&gt;&lt;a href="http://lighthouseapp.com"&gt;Lighthouse&lt;/a&gt; is the issue tracker that&#x2019;s used for &lt;a href="http://rails.lighthouseapp.com"&gt;Ruby on Rails&lt;/a&gt;&amp;#8217; ticket management, for everything from bugs to patches to improvements. While it works great most of the time, sometimes it seems like it&amp;#8217;s not built for projects this big and the amount of tickets and updates can seem overwhelming. In this article I&amp;#8217;ll try to explain my way of keeping up, which has been working for me over the last few weeks.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/lighthouse.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Lighthouse provides an &lt;a href="https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/events.atom"&gt;&lt;span class="caps"&gt;RSS&lt;/span&gt; feed for project events&lt;/a&gt; so you immediately know when tickets get created, updated and closed, as well as all sorts of other stuff. While that might sound like the perfect solution, this feed is practically useless for a project as big and active as Rails because there&amp;#8217;s too much going on to keep up with.&lt;/p&gt;
&lt;p&gt;It didn&amp;#8217;t take me long to unsubscribe from the fire hose and decide to change my strategy. I ended up watching tickets that seemed interesting and setting up email notifications. This way, Lighthouse sends me an email whenever one of my watched tickets get updated. I also have a filter in Gmail to catch everything coming from Lighthouse, label it with &amp;ldquo;rails&amp;rdquo; and skip my inbox. Now and again, I&amp;#8217;ll go through the messages with this label and see if I can do anything to help out.&lt;/p&gt;
&lt;p&gt;The watched tickets don&amp;#8217;t automatically get un-watched when a they&amp;#8217;re closed. Most of the time this is not a problem because closed tickets don&amp;#8217;t get commented on anymore. Spammers sometimes do, so I usually mark that comment as spam and un-watch the ticket manually after receiving a spammy email.&lt;/p&gt;
&lt;h3&gt;Where do I find tickets to watch?&lt;/h3&gt;
&lt;p&gt;It&amp;#8217;s nice to be able to follow tickets you&amp;#8217;re already involved with, but finding the ones you&amp;#8217;re interested in &amp;mdash;because you don&amp;#8217;t want to watch everything that gets created&amp;mdash; can be quite difficult too.&lt;/p&gt;
&lt;p&gt;I went through &lt;a href="https://rails.Lighthouseapp.com/projects/8994-ruby-on-rails/tickets/bins/31228"&gt;&amp;ldquo;this week&amp;rdquo;-bin&lt;/a&gt; every once in a while to find tickets to watch, comment on or write a patch for. Because there&amp;#8217;s no good way to see if you&amp;#8217;ve already read a specific ticket, this turned out to be quite a task.&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://help.lighthouseapp.com/kb/getting-started/how-do-i-search-for-tickets"&gt;ticket filtering&lt;/a&gt; helped me a lot here. You can filter on queries like `&lt;code&gt;created:"this week" state:open&lt;/code&gt;` or even order tickets, like `&lt;code&gt;order:number-&lt;/code&gt;`.&lt;/p&gt;
&lt;p&gt;Diving into Lighthouse every once in a while to find tickets to watch became quite tedious, so I decided to try and solve it myself. It turned out that the filtering you can do in the Lighthouse interface works in &lt;a href="http://Lighthouseapp.com/api"&gt;their &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt; too, so I built a &lt;a href="https://gist.github.com/825847"&gt;stupid-simple Sinatra app&lt;/a&gt; to fetch tickets from it and turn them into an &lt;span class="caps"&gt;RSS&lt;/span&gt; feed.&lt;/p&gt;
&lt;p&gt;Using &lt;a href="http://keeper.jeffkreeftmeijer.com"&gt;Lighthouse Keeper&lt;/a&gt;, I&amp;#8217;m now subscribed to a feed that shows me tickets ordered by ticket number (`&lt;code&gt;order:number-&lt;/code&gt;`). This means new tickets automatically end up in my feed reader. Whenever something interesting comes along, I&amp;#8217;ll comment on the ticket, watch it, or assign it to myself if I feel confident enough to write a patch and help push it through.&lt;/p&gt;
&lt;p&gt;Needless to say, I would &lt;em&gt;love&lt;/em&gt; Lighthouse to provide &lt;span class="caps"&gt;RSS&lt;/span&gt; feeds like this. (hint, hint)&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m curious how you&amp;#8217;re keeping up with the Rails Lighthouse too, so be sure to tell me in the comments. Of course you can use Keeper too and if you find something that can be improved, let me know or fork the &lt;a href="https://gist.github.com/825847"&gt;gist&lt;/a&gt;.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/fQsjV_uZY_0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/keeping-up-with-the-rails-lighthouse</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Acceptance testing using Capybara's new RSpec DSL</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/bgT-FFc3TN8/acceptance-testing-using-capybaras-new-rspec-dsl" />
    
    <id>http://jeffkreeftmeijer.com/2011/acceptance-testing-using-capybaras-new-rspec-dsl</id>
    <updated>2011-02-21T00:00:00+01:00</updated>
    <summary>Capybara turned carnivore and devoured Steak.</summary>
    <content type="html">
      
        &lt;p&gt;I&amp;#8217;ve been using &lt;a href="https://github.com/cavalle/steak"&gt;Steak&lt;/a&gt; for almost a year now. It&amp;#8217;s an &lt;a href="https://github.com/rspec"&gt;RSpec&lt;/a&gt; extension by &lt;a href="http://twitter.com/cavalle" title="Luismi Cavallé"&gt;@cavalle&lt;/a&gt; to allow you to get up and running doing acceptance testing. Since then I &lt;a href="http://jeffkreeftmeijer.com/2010/steak-because-cucumber-is-for-vegetarians"&gt;wrote&lt;/a&gt; and &lt;a href="http://jeffkreeftmeijer.com/2011/herbivore-carnivore"&gt;talked&lt;/a&gt; about it a lot and it definitely helped me to get into acceptance testing without having to get out of RSpec and writing tests in English.&lt;/p&gt;
&lt;p&gt;What a lot of people don&amp;#8217;t know is that Steak is nothing more than a bunch of aliases and generators. This doesn&amp;#8217;t mean that it doesn&amp;#8217;t make your work easier or that it&amp;#8217;s a &lt;a href="http://twitter.com/#!/wycats/status/26769271290"&gt;scam&lt;/a&gt;, but it&amp;#8217;s not a completely separate testing library. It&amp;#8217;s essentially just a tool to make it easier to get &lt;a href="https://github.com/jnicklas/capybara"&gt;Capybara&lt;/a&gt; running on RSpec.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/carnivore_capybara.png" alt="" /&gt;&lt;br /&gt;
&lt;span class="small"&gt;Carnivore Capybara!!!1 Roaargh!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Back when Steak was first released, Capybara didn&amp;#8217;t have any of the nice RSpec helpers it does now. A lot has changed since. Besides the helpers, &lt;a href="https://github.com/jnicklas/capybara/commit/f4897f890d8dd33215fef238902988e8823a6539"&gt;it got its own RSpec acceptance testing &lt;span class="caps"&gt;DSL&lt;/span&gt;&lt;/a&gt; recently, essentially eating Steak&amp;#8217;s functionality and turning it into a complete acceptance testing solution (on top of RSpec).&lt;/p&gt;
&lt;h3&gt;Acceptance testing with Capybara&lt;/h3&gt;
&lt;p&gt;The &lt;span class="caps"&gt;DSL&lt;/span&gt; is&amp;#8217;nt officially released yet, but &lt;a href="http://twitter.com/#!/jncoward/status/36102623924850688"&gt;it will be available in Capybara 1.0&lt;/a&gt;. Right now, you&amp;#8217;ll have to get it from git if you want to use it.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s say you&amp;#8217;re working on a Rails project using Bundler. Simply add RSpec and Capybara to your &lt;code&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rspec-rails&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;capybara&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:git&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;git://github.com/jnicklas/capybara.git&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After running &lt;code&gt;bundle install&lt;/code&gt;, set up RSpec like you&amp;#8217;re used to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; rails g rspec:install
&lt;span class="go"&gt;      create  .rspec&lt;/span&gt;
&lt;span class="go"&gt;      create  spec&lt;/span&gt;
&lt;span class="go"&gt;      create  spec/spec_helper.rb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Capybara doesn&amp;#8217;t have any generators for its new &lt;span class="caps"&gt;DSL&lt;/span&gt;, but that shouldn&amp;#8217;t be a problem. Setting it up is quite easy, just add a file named &lt;code&gt;spec/acceptance/acceptance_helper.rb&lt;/code&gt;. In this file, we&amp;#8217;ll require &lt;code&gt;spec/spec_helper.rb&lt;/code&gt; to make RSpec available. We&amp;#8217;ll also require Capybara&amp;#8217;s RSpec helper file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rspec&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;capybara/rspec&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Capybara will now automatically hook into RSpec and allow you to write your first test. See? I told you we weren&amp;#8217;t going to need any generators. Let&amp;#8217;s write a spec named &lt;code&gt;spec/acceptance/articles_spec.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;acceptance/acceptance_helper&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;feature&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Articles&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%q{&lt;/span&gt;
&lt;span class="sx"&gt;  In order to have an awesome blog&lt;/span&gt;
&lt;span class="sx"&gt;  As an author&lt;/span&gt;
&lt;span class="sx"&gt;  I want to create and manage articles&lt;/span&gt;
&lt;span class="sx"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;One&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Two&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;scenario&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Article index&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/articles&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;One&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Two&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, we&amp;#8217;re using methods like &lt;code&gt;feature&lt;/code&gt;, &lt;code&gt;background&lt;/code&gt; and &lt;code&gt;scenario&lt;/code&gt; instead of &lt;code&gt;context&lt;/code&gt;, &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;example&lt;/code&gt;, just like we did in Steak. Now, you can run your new spec like any other RSpec test:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; rspec spec/acceptance/articles_spec.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There you go. A complete acceptance testing solution including the acceptance testing slang, without generators and just a couple of lines of code. What do you think? Do you have any ideas to make this more awesome? Definitely let me know!&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/bgT-FFc3TN8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/acceptance-testing-using-capybaras-new-rspec-dsl</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Capybara ate Swinger</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/xiUz1zoq17A/capybara-ate-swinger" />
    
    <id>http://jeffkreeftmeijer.com/2011/capybara-ate-swinger</id>
    <updated>2011-02-07T00:00:00+01:00</updated>
    <summary>Remember Swinger, the Capybara RSpec driver swapper? Capybara can now swap drivers out of the box.</summary>
    <content type="html">
      
        &lt;p&gt;About three months ago, I &lt;a href="http://jeffkreeftmeijer.com/2010/capybara-driver-swapping-on-rspec-with-swinger"&gt;released Swinger&lt;/a&gt; to add &lt;a href="http://github.com/rspec"&gt;RSpec&lt;/a&gt; driver swapping to &lt;a href="http://github.com/jnicklas/capybara"&gt;Capybara&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/capybara.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;This idea wasn&amp;#8217;t new, Capybara had doing it &lt;a href="https://github.com/jnicklas/capybara/blob/master/lib/capybara/cucumber.rb"&gt;for Cucumber&lt;/a&gt; for quite some time. Cucumber has this nice tagging thing that allows you to hook in and do cool stuff like that.&lt;/p&gt;
&lt;p&gt;RSpec didn&amp;#8217;t have a feature like that until 2.0, when it released the &lt;a href="http://blog.davidchelimsky.net/2010/06/14/filtering-examples-in-rspec-2"&gt;metadata feature&lt;/a&gt;. That made it possible to pass arbitrary metadata to examples or example groups, allowing you to tag examples. Like in Cucumber. But better.&lt;/p&gt;
&lt;p&gt;Using that, I built Swinger for a big project we were working on at &lt;a href="http://80beans.com"&gt;80beans&lt;/a&gt;. It was still a bit of a monkey patch, since RSpec didn&amp;#8217;t really provide a way to access an example&amp;#8217;s metadata in the around hook, but it worked pretty well.&lt;/p&gt;
&lt;p&gt;Not long after relasing it as a separate gem, I turned Swinger into a &lt;a href="https://github.com/jnicklas/capybara/pull/187"&gt;Capybara pull request&lt;/a&gt;. It was still a monkey patch, so &lt;a href="http://twitter.com/dchelimsky" title="David Chelimsky"&gt;@dchelimsky&lt;/a&gt; did &lt;a href="https://github.com/rspec/rspec-core/issues/issue/221"&gt;a patch for RSpec&lt;/a&gt; to expose the metadata in around hooks, which made it possible for &lt;a href="http://twitter.com/jncoward" title="Jonas Nicklas"&gt;@jncoward&lt;/a&gt; to eventually built a cleaner version of the driver swapping idea into Capybara.&lt;/p&gt;
&lt;h3&gt;Driver Swapping in Capybara, without the crap&lt;/h3&gt;
&lt;p&gt;Now, since Capybara version &lt;del&gt;&lt;a href="http://rubygems.org/gems/capybara/versions/0.4.1"&gt;0.4.1&lt;/a&gt;&lt;/del&gt; &lt;a href="http://rubygems.org/gems/capybara/versions/0.4.1.1"&gt;0.4.1.1&lt;/a&gt;, it supports driver swapping out of the box. Here&amp;#8217;s how to get it working.&lt;/p&gt;
&lt;p&gt;First, in the file where you set up Capybara (that&amp;#8217;s &lt;code&gt;spec/acceptance/acceptance_helper.rb&lt;/code&gt; if you&amp;#8217;re using &lt;a href="https://github.com/cavalle/steak"&gt;Steak&lt;/a&gt;). Include Capybara&amp;#8217;s RSpec helper file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;capybara/rspec&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now, just like with Swinger, you can tag your examples with a specific &lt;code&gt;:driver&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;does fancy stuff&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:driver&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:selenium&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# test fancy stuff&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Of course you can also use this on example groups:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;fancy stuff&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:driver&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:rack_test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# fancy examples&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you set &lt;code&gt;Capybara.javascript_driver&lt;/code&gt;, you can even tag examples that need a javascript capable driver like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;javascript_driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:selenium&lt;/span&gt;

&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;does fancy stuff&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:js&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# test fancy stuff&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This officially means I stopped maintaining Swinger and I urge you to update Capybara to 0.4.1.1. Thanks to everyone who helped out building Swinger and getting it into Capybara, it certainly made my test suites a lot faster.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/xiUz1zoq17A" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/capybara-ate-swinger</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Herbivore / Carnivore</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/KLBFlVOAoHI/herbivore-carnivore" />
    
    <id>http://jeffkreeftmeijer.com/2011/herbivore-carnivore</id>
    <updated>2011-01-31T00:00:00+01:00</updated>
    <summary>Dinosaurs with lasers &amp; acceptance testing. About my Amsterdam.rb presentation comparing Cucumber to Steak.</summary>
    <content type="html">
      
        &lt;p&gt;At the &lt;a href="http://80beans.com"&gt;80beans&lt;/a&gt; / &lt;a href="http://amsterdam-rb.org"&gt;Amsterdam.rb&lt;/a&gt; new years event, I gave a short presentation about acceptance testing named &amp;ldquo;Herbivore / Carnivore&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jeffkreeftmeijer.github.com/herbivore_carnivore"&gt;&lt;img src="http://jeffkreeftmeijer.com/images/herbivore_carnivore.png" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As a happy &lt;a href="https://github.com/cavalle/steak"&gt;Steak&lt;/a&gt; user, I wanted to talk about how it could free you from your horrible problems with &lt;a href="http://cukes.info/"&gt;Cucumber&lt;/a&gt; and make you a better and more efficient programmer. But during my research, finding bad things to say about Cucumber proved more difficult than I thought, especially after talking to a bunch of Cucumber users that explained why it works the way it does and cleared some of my misconceptions.&lt;/p&gt;
&lt;p&gt;Using my new Cucumber knowledge, I turned my presentation around from explaining &lt;em&gt;why&lt;/em&gt; I think you should use a tool like Steak to &lt;em&gt;when&lt;/em&gt; I think you should use a tool like Steak. I did this because I think a lot of people just choose between testing in English or Ruby and stick to that, no matter what the next project or situation is.&lt;/p&gt;
&lt;p&gt;Instead of trying to convince everyone that writing tests in English is overkill, I tried to explain what a great tool Cucumber is when you&amp;#8217;re actually using it as a communication tool. Because, let&amp;#8217;s be honest, You can&amp;#8217;t let your clients read code, no matter how readable you think it is. You&amp;#8217;re a programmer. If you want to write stories together with your client and keep them involved in the process you &lt;em&gt;need&lt;/em&gt; some kind of &lt;a href="http://www.martinfowler.com/bliki/BusinessReadableDSL.html"&gt;business readable &lt;span class="caps"&gt;DSL&lt;/span&gt;&lt;/a&gt; to allow you to write stories in a language everybody involved can understand. That language is &lt;em&gt;not&lt;/em&gt; Ruby (in most cases).&lt;/p&gt;
&lt;p&gt;In situations where you can&amp;#8217;t get the client involved and you find yourself being the only one writing and reading the English stories, Steak might be a great solution. It&amp;#8217;s a simple RSpec extension that adds just enough code to allow you to do acceptance testing in Ruby, without the extra complexity or overhead. I&amp;#8217;m not going to explain Steak here, I wrote &lt;a href="http://jeffkreeftmeijer.com/2010/steak-because-cucumber-is-for-vegetarians/"&gt;an article about it&lt;/a&gt; a couple of moths back.&lt;/p&gt;
&lt;p&gt;So, why is Cucumber the most used acceptance testing library while most projects, clients and teams don&amp;#8217;t really &lt;em&gt;need&lt;/em&gt; English tests? Shouldn&amp;#8217;t we add less complexity when we don&amp;#8217;t need it?&lt;/p&gt;
&lt;p&gt;I ended my presentation with a question I think all Cucumber users should ask themselves every once in a while: &amp;ldquo;Do you benefit from writing tests in English?&amp;rdquo;. Do you &lt;em&gt;use&lt;/em&gt; the tests as executable documentation and as a communication tool? If your answer is yes, keep doing what you&amp;#8217;re doing. Otherwise, Steak might be a good tool for you.&lt;/p&gt;
&lt;p&gt;I would love to hear your thoughts on this. Here are &lt;a href="http://jeffkreeftmeijer.github.com/herbivore_carnivore"&gt;my slides&lt;/a&gt; if you want to give them another look. Also, here&amp;#8217;s &lt;a href="http://iain.nl/2011/01/cucumber-vs-steak/"&gt;a great article&lt;/a&gt; by &lt;a href="http://twitter.com/iain_nl" title="Iain Hecker"&gt;@iain_nl&lt;/a&gt; about Steak and Cucumber.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/KLBFlVOAoHI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/herbivore-carnivore</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Testing code that's testing itself</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/eQIc4ljhXsM/testing-code-thats-testing-itself" />
    
    <id>http://jeffkreeftmeijer.com/2011/testing-code-thats-testing-itself</id>
    <updated>2011-01-24T00:00:00+01:00</updated>
    <summary>Trying to solve the problem of broken code being unable to test itself because it's broken</summary>
    <content type="html">
      
        &lt;p&gt;It&amp;#8217;s quite common for testing libraries (like &lt;a href="https://github.com/rspec"&gt;RSpec&lt;/a&gt; and &lt;a href="https://github.com/chneukirchen/bacon"&gt;Bacon&lt;/a&gt;) to test themselves. That&amp;#8217;s pretty cool, but it might get you into some problems. In this article I&amp;#8217;ll try to explain one of those problems and give a suggestion that might help.&lt;/p&gt;
&lt;p&gt;Instead of showing you a red dot and a failing test, RSpec won&amp;#8217;t be able to run its own tests &lt;em&gt;at all&lt;/em&gt; if you break &lt;code&gt;RSpec::Core::Example.run&lt;/code&gt;. That shouldn&amp;#8217;t be anything shocking, because that method is very important to be able to actually run an example.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/recursion.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;The problem we have here is that code you&amp;#8217;re working on is unstable and can&amp;#8217;t be trusted, which means you can&amp;#8217;t really use it to test anything either. That&amp;#8217;s testing broken code with broken code and it can get very confusing very fast.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a more simple example. Let&amp;#8217;s say we added a method called &lt;code&gt;#one&lt;/code&gt; to &lt;code&gt;Object&lt;/code&gt;, allowing us to check the numerical value of &amp;#8216;one&amp;#8217; by just calling &lt;code&gt;#one&lt;/code&gt; on any object:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;one&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We also have a method to check if something&amp;#8217;s value is 1, and we put it conveniently in the same library:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;one&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_one?&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now, we can write a test that makes our library test its own method:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_one?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Nice. Now the library is using its &lt;code&gt;#is_one?&lt;/code&gt; method to test its &lt;code&gt;#one&lt;/code&gt; method. There&amp;#8217;s only one problem though. What if we dive in again and start hacking on our library? We could break &lt;code&gt;#is_one?&lt;/code&gt; and end up with something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;one&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_one?&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="c1"&gt;# oops!&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This would mean that our test for &lt;code&gt;#one&lt;/code&gt; will fail, while it&amp;#8217;s not broken at all. Instead, our test is broken (which happens to be in the library we&amp;#8217;re currently testing).&lt;/p&gt;
&lt;p&gt;A solution would be to let a stable version of our library test the current version. Let&amp;#8217;s try that.&lt;/p&gt;
&lt;p&gt;In our test, we &lt;code&gt;require&lt;/code&gt; the library to be tested first and clone it into a &lt;code&gt;Unstable&lt;/code&gt; namespace. After that, we &lt;code&gt;require&lt;/code&gt; a stable version of our library:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# library_spec.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;library&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Unstable&lt;/span&gt;
  &lt;span class="no"&gt;Object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;stable_library&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The stable library has overwritten the &lt;code&gt;Object&lt;/code&gt; in the global namespace, but not the &lt;code&gt;Unstable::Object&lt;/code&gt;. Now we should call &lt;code&gt;#one&lt;/code&gt; from the &lt;code&gt;Unstable&lt;/code&gt; namespace instead of the global one (because &lt;code&gt;Unstable::Object&lt;/code&gt; is the class we&amp;#8217;re currently testing):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;Unstable&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_one?&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Because &lt;code&gt;Unstable::Object#one&lt;/code&gt; returns a stable &lt;code&gt;Fixnum&lt;/code&gt;, our stable version of &lt;code&gt;#is_one?&lt;/code&gt; is used. This makes sure our test actually runs, even if the current (unstable) version of our library is broken.&lt;/p&gt;
&lt;p&gt;Now we have a setup that uses a stable version of a library that can find bugs in an unstable version of itself. Awesome.&lt;/p&gt;
&lt;p&gt;What do you think? Be sure to let me know if you have any ideas to improve this approach.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/eQIc4ljhXsM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/testing-code-thats-testing-itself</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Stop releasing prototypes</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/heBEeuuX6Mk/stop-releasing-prototypes" />
    
    <id>http://jeffkreeftmeijer.com/2011/stop-releasing-prototypes</id>
    <updated>2011-01-17T00:00:00+01:00</updated>
    <summary>Explaining why I think writing prototypes is good, as long as you throw them out.</summary>
    <content type="html">
      
        &lt;p&gt;I talked about this with a bunch of people at &lt;a href="http://jeffkreeftmeijer.com/2010/rubyandrails-2010/"&gt;RubyAndRails 2010&lt;/a&gt; a couple of months back, but never found the time to write it down.&lt;/p&gt;
&lt;p&gt;In &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" title="Test Driven Development"&gt;&lt;span class="caps"&gt;TDD&lt;/span&gt;&lt;/a&gt;, we use a very short development cycle to get something to work. We write a test, watch it fail, implement the feature and watch it pass. Then we move on to a new test. Simple.&lt;/p&gt;
&lt;p&gt;But, to be able to write a test, we need some information to start out with. Most of the time, we probably have that information already. You know your stuff good enough to be able to write tests &lt;em&gt;before&lt;/em&gt; doing anything else.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/trex.jpg" alt="" /&gt;&lt;br /&gt;
&lt;span class="small"&gt;Seriously, stop it. Or &lt;span class="caps"&gt;TDD&lt;/span&gt; rex will eat you.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;But sometimes you&amp;#8217;re working on something you don&amp;#8217;t have any experience with in a library you never worked on before. And you don&amp;#8217;t know what code you need to write, meaning you can&amp;#8217;t write any tests for it. What now?&lt;/p&gt;
&lt;p&gt;You dive in and write a prototype. You don&amp;#8217;t worry about tests or clean code, you just hack until the thing works exactly how you like it. It probably won&amp;#8217;t take you long before you have something that does what you need. You show your friends and co-workers and they tell you you did an awesome job.&lt;/p&gt;
&lt;p&gt;You&amp;#8217;re eager to get your library out there, but you remember everybody screaming you should write tests &amp;#8220;all the fucking time&amp;#8221;. So, for that reason, you dive in once again and slap some tests on your new library. Now it&amp;#8217;s tested and your work is done. The last thing you need to do is think of some fun, &lt;a href="http://twitter.com/alloy/status/26996675729948672"&gt;mildly sexual&lt;/a&gt; name before releasing it.&lt;/p&gt;
&lt;h3&gt;No, no, no, no, no, no, no!&lt;/h3&gt;
&lt;p&gt;Building that prototype helped you to figure out what needed to happen and what the nicest implementation of your feature was. You didn&amp;#8217;t worry about writing tests or clean code, you just wrote the feature.&lt;/p&gt;
&lt;p&gt;After that you&amp;#8217;re quite proud of your work and there doesn&amp;#8217;t seem to be anything wrong with it. So, you decided to add some tests. More often than not, this leads to poorly tested code.&lt;/p&gt;
&lt;p&gt;Doing &lt;span class="caps"&gt;TDD&lt;/span&gt;, you would have written as little code as you needed to get your test to pass. When writing your implementation first, chances are you wrote more code than you actually needed. Even if you completely test this extra code afterwards, it still adds bloat to your library. Besides that, unless you&amp;#8217;ve seen your test fail, how do you know if it works?&lt;/p&gt;
&lt;p&gt;I believe you should&amp;#8217;ve thrown the prototype out and started over using the new knowledge you gained from creating it. You probably learned enough about the existing code and your implementation to be able to write proper tests.&lt;/p&gt;
&lt;p&gt;So, instead of throwing tests on prototypes, let&amp;#8217;s try to throw our prototypes out and start over. Yes, this takes way longer, but I know your clean, stable and maintainable implementation will make up for that.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/heBEeuuX6Mk" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2011/stop-releasing-prototypes</feedburner:origLink></entry>
  
  <entry>
    <title type="html">StillMaintained status buttons in Github search results</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/iGgIsNLtNTI/stillmaintained-status-buttons-in-github-search-results" />
    
    <id>http://jeffkreeftmeijer.com/2010/stillmaintained-status-buttons-in-github-search-results</id>
    <updated>2010-12-20T00:00:00+01:00</updated>
    <summary>Releasing a simple userscript that adds the StillMaintained status buttons to Github search results.</summary>
    <content type="html">
      
        &lt;p&gt;Ever since &lt;a href="http://stillmaintained.com"&gt;StillMaintained&lt;/a&gt; launched a couple of weeks ago, we&amp;#8217;ve been working on ideas to get the gathered data back into &lt;a href="http://github.com"&gt;Github&lt;/a&gt;&amp;#8217;s interface. The first step was the &lt;a href="https://gist.github.com/731135"&gt;embeddable status buttons&lt;/a&gt; that got &lt;a href="http://jeffkreeftmeijer.com/2010/stillmaintained-is-still-being-maintained/"&gt;released last week&lt;/a&gt; and have been popping up in a bunch of project &lt;code&gt;README&lt;/code&gt; files already.&lt;/p&gt;
&lt;p&gt;To take this even further, I&amp;#8217;ve been working on a little userscript that shows these status buttons right in Github&amp;#8217;s search results so you can quickly see which project is still being maintained and which ones you probably shouldn&amp;#8217;t start using.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/stillmaintained_userscript.jpg" alt="" /&gt;&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;Chrome and Firefox users can &lt;a href="http://stillmaintained.com/javascripts/stillmaintained.user.js"&gt;install the userscript&lt;/a&gt; (you need &lt;a href="http://www.greasespot.net"&gt;Greasemonkey&lt;/a&gt; or &lt;a href="http://8-p.info/greasekit"&gt;GreaseKit&lt;/a&gt; first if you&amp;#8217;re on Firefox). Safari users can simply &lt;a href="http://stillmaintained.com/stillmaintained.safariextz"&gt;get the Safari plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please let me know what you think in the comments if you start using it. If you&amp;#8217;re running into problems, have any cool ideas or want to help out: the userscript (together with the rest of StillMaintained) is &lt;a href="https://github.com/jeffkreeftmeijer/stillmaintained"&gt;on Github&lt;/a&gt;.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/iGgIsNLtNTI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/stillmaintained-status-buttons-in-github-search-results</feedburner:origLink></entry>
  
  <entry>
    <title type="html">StillMaintained is still being maintained</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/30jpnmaleoQ/stillmaintained-is-still-being-maintained" />
    
    <id>http://jeffkreeftmeijer.com/2010/stillmaintained-is-still-being-maintained</id>
    <updated>2010-12-13T00:00:00+01:00</updated>
    <summary>Highlighting some of the new features added to StillMaintained and giving you a sneak peak of the new design.</summary>
    <content type="html">
      
        &lt;p&gt;&lt;span class="small"&gt;&lt;br /&gt;
  &lt;strong&gt;tl;dr&lt;/strong&gt; &lt;a href="http://stillmaintained.com" title="Still Maintained?"&gt;StillMaintained&lt;/a&gt; got a bunch of awesome new features. Be sure to have a look and add your projects!&lt;br /&gt;
&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Two weeks ago, I &lt;a href="http://jeffkreeftmeijer.com/2010/finally-a-way-to-mark-your-github-project-as-abandoned"&gt;launched&lt;/a&gt; &lt;a href="http://stillmaintained.com"&gt;StillMaintained&lt;/a&gt;: a simple application that makes it easy to give your &lt;a href="http://github.com"&gt;Github&lt;/a&gt; projects a state and create nice status pages for them.&lt;/p&gt;
&lt;p&gt;Since then, a bunch of &lt;a href="https://github.com/jeffkreeftmeijer/stillmaintained/issues"&gt;issues&lt;/a&gt; were created and a lot of work has been done. In this article I&amp;#8217;ll go over some of the coolest new stuff.&lt;/p&gt;
&lt;h3&gt;Soon: New design&lt;/h3&gt;
&lt;p&gt;Okay, this isn&amp;#8217;t really done yet, but we are too excited about it to keep it a secret. Over the last two weeks, &lt;a href="http://twitter.com/ivanasetiawan" title="Ivana Setiawan"&gt;@ivanasetiawan&lt;/a&gt; has been doing some awesome work creating a proper design for StillMaintained. Here&amp;#8217;s a quick preview, we&amp;#8217;re doing our best to get it up soon.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://stillmaintained.com"&gt;&lt;img src="http://jeffkreeftmeijer.com/images/stillmaintained_design.jpg" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Organization support&lt;/h3&gt;
&lt;p&gt;Since you can&amp;#8217;t log in as an organization on Github, it was impossible to add any projects that weren&amp;#8217;t started by an actual user when StillMaintained launched.&lt;/p&gt;
&lt;p&gt;Using Github&amp;#8217;s &lt;a href="http://develop.github.com/p/orgs.html"&gt;Organizations &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt;, StillMaintained got organization support last week.&lt;/p&gt;
&lt;p&gt;If you log in via Github now, the projects of the organizations you&amp;#8217;re a member of are editable by you. If you add them to the list, the organization they belong to gets its own page and the project gets a status page like any other.&lt;/p&gt;
&lt;p&gt;Did you add your projects already? You can just log in again to update your projects and add projects from organizations you&amp;#8217;re a member of.&lt;/p&gt;
&lt;h3&gt;&lt;span class="caps"&gt;JSON&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;Thanks to &lt;a href="http://twitter.com/matsimitsu" title="Robert Beekman"&gt;@matsimitsu&lt;/a&gt;, StillMaintained got a very simple &lt;span class="caps"&gt;API&lt;/span&gt; allowing you to fetch data in &lt;span class="caps"&gt;JSON&lt;/span&gt; format:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; curl http://stillmaintained.com/jeffkreeftmeijer/fuubar.json
&lt;span class="go"&gt;{&amp;quot;name&amp;quot;:&amp;quot;fuubar&amp;quot;,&amp;quot;created_at&amp;quot;:&amp;quot;2010/11/30 07:09:55 +0000&amp;quot;,&amp;quot;watchers&amp;quot;:92,&amp;quot;updated_at&amp;quot;:&amp;quot;2010/12/09 21:21:16 +0000&amp;quot;,&amp;quot;_id&amp;quot;:&amp;quot;4cf3c2a6f9d9006a5d00000b&amp;quot;,&amp;quot;description&amp;quot;:&amp;quot;The instafailing RSpec progress bar formatter&amp;quot;,&amp;quot;visible&amp;quot;:true,&amp;quot;user&amp;quot;:&amp;quot;jeffkreeftmeijer&amp;quot;,&amp;quot;state&amp;quot;:&amp;quot;maintained&amp;quot;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can get any page in &lt;span class="caps"&gt;JSON&lt;/span&gt; by adding &lt;code&gt;.json&lt;/code&gt; after the &lt;span class="caps"&gt;URL&lt;/span&gt;. Be sure to let me know if you create anything awesome with it.&lt;/p&gt;
&lt;h3&gt;Status buttons&lt;/h3&gt;
&lt;p&gt;To be able to tell your users the state of your project while not on StillMaintained, Robert and I added nice self-updating status buttons you can embed in your project&amp;#8217;s &lt;code&gt;README&lt;/code&gt; on Github (or anywhere else you&amp;#8217;d like).&lt;/p&gt;
&lt;p&gt;&lt;a href="http://stillmaintained.com"&gt;&lt;img src="http://jeffkreeftmeijer.com/images/stillmaintained_status_buttons.jpg" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If your project is listed on StillMaintained, you can simply embed the image like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://stillmaintained.com/you/yourproject&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://stillmaintained.com/you/yourproject.png&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Oh, and be sure to check out &lt;a href="https://gist.github.com/731135"&gt;this Gist&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h3&gt;And more&lt;/h3&gt;
&lt;p&gt;StillMaintained also has search and state filtering, project descriptions and the lists are ordered by project popularity to name a few more. Be sure to &lt;a href="http://stillmaintained.com"&gt;check it out&lt;/a&gt; and if you have any great ideas: it&amp;#8217;s &lt;a href="http://github.com/jeffkreeftmeijer/stillmaintained"&gt;on Github&lt;/a&gt;, so be sure to create &lt;a href="http://github.com/jeffkreeftmeijer/stillmaintained/issues"&gt;issues&lt;/a&gt; or even fork the project and get your hands dirty yourself.&lt;/p&gt;
&lt;p&gt;So, what do you think? Do you like the new design and the new features?&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/30jpnmaleoQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/stillmaintained-is-still-being-maintained</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Finally a way to mark your Github project as abandoned</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/CgCVjMUjT0g/finally-a-way-to-mark-your-github-project-as-abandoned" />
    
    <id>http://jeffkreeftmeijer.com/2010/finally-a-way-to-mark-your-github-project-as-abandoned</id>
    <updated>2010-11-29T00:00:00+01:00</updated>
    <summary>Announcing stillmaintained.com, a simple web application that keeps track of open source maintenance states.</summary>
    <content type="html">
      
        &lt;p&gt;&lt;span class="small"&gt;&lt;br /&gt;
  &lt;strong&gt;tl;dr&lt;/strong&gt; Check out &lt;a href="http://stillmaintained.com" title="Still Maintained?"&gt;stillmaintained.com&lt;/a&gt; to create some nice maintenance status pages for your Github projects.&lt;br /&gt;
&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Back when it was still nice and sunny outside, I wrote an article about &lt;a href="http://jeffkreeftmeijer.com/2010/abandoned-open-source-projects"&gt;abandoned open source projects&lt;/a&gt;, in which I proposed a way to mark Github projects as abandoned. There was &lt;a href="http://news.ycombinator.com/item?id=1528523"&gt;some discussion&lt;/a&gt; and a &lt;a href="http://support.github.com/discussions/feature-requests/987-abandoned-projects"&gt;feature request&lt;/a&gt;, but nobody seemed to have nice and clean enough solution that solved the problem.&lt;/p&gt;
&lt;p&gt;I almost forgot about the whole thing, until I started getting a bunch of questions and pull requests for old projects again.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://stillmaintained.com"&gt;&lt;img src="http://jeffkreeftmeijer.com/images/stillmaintained.jpg" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I decided to spend a Monday to build a simple application called &lt;a href="http://stillmaintained.com" title="Still Maintained?"&gt;stillmaintained.com&lt;/a&gt; that keeps a list of open source projects and makes it easy to give your own projects a state (maintained, looking for a new maintainer or abandoned). This way, each project gets a nice status page. Here&amp;#8217;s &lt;a href="http://stillmaintained.com/jeffkreeftmeijer/fuubar"&gt;Fuubar&amp;#8217;s&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Help!&lt;/h3&gt;
&lt;p&gt;The current version isn&amp;#8217;t much yet, but you can at least start adding your projects. There&amp;#8217;s a lot of stuff to be done and a lot of ideas to consider (like doing something about that long &lt;a href="http://stillmaintained.com/projects"&gt;projects page&lt;/a&gt; or adding an &lt;span class="caps"&gt;API&lt;/span&gt;), but hey, it&amp;#8217;s a start.&lt;/p&gt;
&lt;p&gt;If you want to help out, the project is &lt;a href="https://github.com/jeffkreeftmeijer/stillmaintained"&gt;on Github&lt;/a&gt;, so be sure to create an &lt;a href="https://github.com/jeffkreeftmeijer/stillmaintained/issues"&gt;issue&lt;/a&gt; if you have a great idea or run into something weird.&lt;/p&gt;
&lt;p&gt;Also, it would be awesome if you took a moment and added your projects to the list. It&amp;#8217;ll only take you about a minute.&lt;/p&gt;
&lt;p&gt;What do you think? Is this a way to help solve the abandoned project issue? Have any awesome ideas already? Be sure to let me know.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/CgCVjMUjT0g" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/finally-a-way-to-mark-your-github-project-as-abandoned</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Fuubar on RSpec 1.3.x, Cucumber &amp; MiniTest</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/dYts9nKGInc/fuubar-on-spec-13x-cucumber-minitest" />
    
    <id>http://jeffkreeftmeijer.com/2010/fuubar-on-spec-13x-cucumber-minitest</id>
    <updated>2010-11-22T00:00:00+01:00</updated>
    <summary>Last week I released an RSpec progress bar formatter called Fuubar. Now it's running on RSpec 1.3.x, MiniTest and Cucumber.</summary>
    <content type="html">
      
        &lt;p&gt;Last week, I released an &lt;a href="http://jeffkreeftmeijer.com/2010/fuubar-the-instafailing-rspec-progress-bar-formatter"&gt;RSpec progress bar formatter called Fuubar&lt;/a&gt; and some cool stuff has happened since.&lt;/p&gt;
&lt;p&gt;Version 0.0.2 was released, which uses &lt;a href="http://rubygems.org/gems/ruby-progressbar"&gt;ruby-progessbar&lt;/a&gt; instead of &lt;a href="http://rubygems.org/gems/progressbar"&gt;progressbar&lt;/a&gt; and prints the bar using &lt;code&gt;====&lt;/code&gt; instead of &lt;code&gt;oooo&lt;/code&gt;. &lt;a href="http://twitter.com/iain_nl" title="Iain Hecker"&gt;@iain_nl&lt;/a&gt; moved the progress bar &lt;a href="https://github.com/jeffkreeftmeijer/fuubar/issues/closed#issue/2"&gt;two spaces to the right&lt;/a&gt;, a couple of bugs were squashed and Fuubar is eating its own dog food by using itself as its default formatter now.&lt;/p&gt;
&lt;h3&gt;RSpec 1.3.x&lt;/h3&gt;
&lt;p&gt;With &lt;a href="https://github.com/jeffkreeftmeijer/fuubar/issues/closed#issue/5"&gt;a lot of help&lt;/a&gt; from &lt;a href="http://twitter.com/#!/__NeX__" title="Roberto Decurnex"&gt;@__NeX__&lt;/a&gt;, Fuubar got RSpec 1.3.x support today. It&amp;#8217;s in a separate &lt;a href="https://github.com/jeffkreeftmeijer/fuubar/tree/legacy"&gt;legacy branch&lt;/a&gt; and it&amp;#8217;s &lt;em&gt;not&lt;/em&gt; released as a separate gem right now.&lt;/p&gt;
&lt;p&gt;Installing it for RSpec 1.3.x should still be quite easy. Since you need to pull it in from git, you need to specify the repository url and branch name in your &lt;code&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fuubar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="ss"&gt;:git&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;git://github.com/jeffkreeftmeijer/fuubar.git&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="ss"&gt;:branch&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;legacy&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you want to use Fuubar as your default formatter, put this in your &lt;code&gt;spec/spec.opts&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--format Fuubar
--color&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Cucumber and MiniTest&lt;/h3&gt;
&lt;p&gt;A bunch of work has been done to build a &lt;a href="http://gotmayonase.tumblr.com/post/1616925126/minitest-progressbar"&gt;Fuubar-like formatter for MiniTest&lt;/a&gt; by &lt;a href="http://twitter.com/mike_mayo" title="Mike Mayo"&gt;@mike_mayo&lt;/a&gt;, who has been building it into &lt;a href="https://github.com/gotmayonase/minitest"&gt;his MiniTest fork&lt;/a&gt;. Keep an eye on &lt;a href="http://gotmayonase.tumblr.com"&gt;his blog&lt;/a&gt; for more information about the project.&lt;/p&gt;
&lt;p&gt;Also, there&amp;#8217;s a ready-to-use gem called &lt;a href="https://github.com/martinciu/fuubar-cucumber"&gt;fuubar-cucumber&lt;/a&gt;, released by &lt;a href="http://twitter.com/martinciu" title="Marcin Ciunelis"&gt;@martinciu&lt;/a&gt;, so you can even use Fuubar when you&amp;#8217;re on &lt;a href="http://cukes.info"&gt;Cucumber&lt;/a&gt; for acceptance testing.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m excited to see where this is going, Fuubar&amp;#8217;s being used in a couple of &lt;a href="https://github.com/search?type=Code&amp;amp;language=ruby&amp;amp;q=fuubar&amp;amp;repo=&amp;amp;langOverride=&amp;amp;x=0&amp;amp;y=0&amp;amp;start_value=1"&gt;projects&lt;/a&gt; already (most notably &lt;a href="https://github.com/jnicklas/capybara"&gt;Capybara&lt;/a&gt; and &lt;a href="https://github.com/tomas-stefano/infinity_test"&gt;infinity_test&lt;/a&gt;) and nice ideas are popping up. Thanks for helping out, using it and spreading the word, everyone!&lt;/p&gt;
&lt;p&gt;Now, what would you like to see in Fuubar in the near future?&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/dYts9nKGInc" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/fuubar-on-spec-13x-cucumber-minitest</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Ever heard of Capybara's save_and_open_page method?</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/WRHXNUgneLo/ever-heard-of-capybaras-save_and_open_page-method" />
    
    <id>http://jeffkreeftmeijer.com/2010/ever-heard-of-capybaras-save_and_open_page-method</id>
    <updated>2010-11-18T00:00:00+01:00</updated>
    <summary>For when you really need to see what's going on in your acceptance test.</summary>
    <content type="html">
      
        &lt;p&gt;Every once in a while, when running acceptance tests, you get this weird failure and &lt;a href="http://github.com/jnicklas/capybara"&gt;Capybara&lt;/a&gt; can&amp;#8217;t really tell you what&amp;#8217;s wrong either:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="go"&gt;1) User registration should register successfully&lt;/span&gt;
&lt;span class="go"&gt;   Failure/Error: fill_in &amp;#39;username&amp;#39;, :with =&amp;gt; &amp;#39;jkreeftmeijer&amp;#39;&lt;/span&gt;
&lt;span class="go"&gt;   cannot fill in, no text field, text area or password field with id, name, or label &amp;#39;username&amp;#39; found&lt;/span&gt;
&lt;span class="go"&gt;   # ./spec/acceptance/user_registration_spec.rb:46&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;All we know now is that there&amp;#8217;s no &amp;#8220;username&amp;#8221; field on the page, which doesn&amp;#8217;t really tell us anything. We could start our server and browse to the page manually or &amp;#8212; when using &lt;a href="http://seleniumhq.org"&gt;Selenium&lt;/a&gt; &amp;#8212; add a &lt;code&gt;sleep&lt;/code&gt; right before the spec fails to get a quick glance at what&amp;#8217;s going wrong. Nasty. Don&amp;#8217;t do that.&lt;/p&gt;
&lt;h3&gt;save_and_open_page&lt;/h3&gt;
&lt;p&gt;When going through Capybara&amp;#8217;s source last week, I found the &lt;code&gt;save_and_open_page&lt;/code&gt; method which does a great job at fixing this issue. As the name implies: it saves the page &amp;#8212; complete with styling and images &amp;#8212; and opens it in your favorite browser:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;should register successfully&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;registration_page&lt;/span&gt;
  &lt;span class="n"&gt;save_and_open_page&lt;/span&gt;
  &lt;span class="n"&gt;fill_in&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:with&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;jkreeftmeijer&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I hope this helps someone, it would have saved me a &lt;em&gt;lot&lt;/em&gt; of time.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/WRHXNUgneLo" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/ever-heard-of-capybaras-save_and_open_page-method</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Fuubar: the instafailing RSpec progress bar formatter</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/b6w8aQ6j97U/fuubar-the-instafailing-rspec-progress-bar-formatter" />
    
    <id>http://jeffkreeftmeijer.com/2010/fuubar-the-instafailing-rspec-progress-bar-formatter</id>
    <updated>2010-11-15T00:00:00+01:00</updated>
    <summary>Combining the work of Nick Evans and Michael Grosser in an attempt to create a better RSpec formatter</summary>
    <content type="html">
      
        &lt;p&gt;As you might have noticed, I&amp;#8217;ve been spending some time trying to get running test suites with &lt;a href="https://github.com/rspec"&gt;RSpec&lt;/a&gt; a bit better and faster over the last weeks. This week I looked into RSpec&amp;#8217;s formatters.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/fuubar.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Aside from the red &amp;#8220;F&amp;#8221; RSpec will output when a spec fails, there&amp;#8217;s no direct feedback that allows you to go fix things immediately. You&amp;#8217;ll simply have to wait until your whole suite is done running, or use ^C to interrupt the run. Another thing you can do is use &lt;a href="http://jeffkreeftmeijer.com/2010/making-rspec-stop-operation-immediately-after-failing"&gt;the &lt;code&gt;--fail-fast&lt;/code&gt; option I wrote about last week&lt;/a&gt; or check out &lt;a href="https://github.com/grosser/rspec-instafail"&gt;rspec-instafail&lt;/a&gt;, which will output the failure details while continuing to run the rest of your specs. Nice.&lt;/p&gt;
&lt;p&gt;Besides that, I realized there were more things that could be improved on, like knowing the total number of specs, the number of specs that have run already and maybe even an &lt;span class="caps"&gt;ETA&lt;/span&gt; or something like that. Also, the big string of dots and &amp;quot;F&amp;quot;&amp;#8217;s started to seem unnecessary, there should be a nicer way to display this information.&lt;/p&gt;
&lt;p&gt;I checked out RSpec&amp;#8217;s formatters and realized it was extremely easy to write one, so I got distracted and wrote &lt;a href="http://gist.github.com/676219"&gt;FffuuuFormatter&lt;/a&gt;, which makes RSpec print &lt;span class="caps"&gt;FFFUUU&lt;/span&gt; instead of &lt;span class="caps"&gt;FFFFF&lt;/span&gt;. Heh.&lt;/p&gt;
&lt;h3&gt;Fuubar&lt;/h3&gt;
&lt;p&gt;After looking around for a bit, I found an article by &lt;a href="http://twitter.com/nick_evans" title="Nicholas Evans"&gt;@nick_evans&lt;/a&gt; posted in 2008, in which he tried to fix some of the issues I&amp;#8217;m having with a &lt;a href="http://ekenosen.net/nick/devblog/2008/12/better-progress-bar-for-rspec"&gt;progress bar formatter&lt;/a&gt;. Nick wrote a really nice solution for it using the &lt;a href="http://rubygems.org/gems/progressbar"&gt;progressbar gem&lt;/a&gt; by &lt;a href="http://twitter.com/peleteiro" title="Jose Peleteiro"&gt;@peleteiro&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I decided to continue Nick&amp;#8217;s work and got the formatter up to speed with RSpec 2, using rspec-instafail to handle the direct feedback after every failure.  I put the whole thing in a gem and &lt;a href="http://twitter.com/josevalim" title="José Valim"&gt;@josevalim&lt;/a&gt; came up with the name: &lt;a href="http://github.com/jeffkreeftmeijer/fuubar"&gt;Fuubar&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a &lt;a href="http://vimeo.com/16845253"&gt;short video of Fuubar in action&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;object width="440" height="350"&gt;&lt;br /&gt;
  &lt;param name="allowfullscreen" value="true" /&gt;&lt;br /&gt;
  &lt;param name="allowscriptaccess" value="always" /&gt;&lt;br /&gt;
  &lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=16845253&amp;amp;server=vimeo.com&amp;amp;show_title=0&amp;amp;show_byline=0&amp;amp;show_portrait=0&amp;amp;color=ff9933&amp;amp;fullscreen=1" /&gt;&lt;br /&gt;
  &lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=16845253&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=ff9933&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always"  width="440" height="350"&gt;&lt;/embed&gt;&lt;br /&gt;
&lt;/object&gt;&lt;/p&gt;
&lt;p&gt;Installing Fuubar is easy. Just put it in your &lt;code&gt;Gemfile&lt;/code&gt; and run your specs like this from now on:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; rspec --format Fuubar --color spec
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you want to use Fuubar as your default formatter, simply put the options in your &lt;code&gt;.rspec&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--format Fuubar
--color&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After doing that you can simply run your specs like you&amp;#8217;re used to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; rspec spec
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Fuuture&lt;/h3&gt;
&lt;p&gt;As always: Let me know how you like it and be sure to create an &lt;a href="http://github.com/jeffkreeftmeijer/fuubar/issues"&gt;issue on Github&lt;/a&gt;, send me a &lt;a href="https://github.com/inbox/new/jeffkreeftmeijer"&gt;Github message&lt;/a&gt; or an &lt;a href="http://jeffkreeftmeijer.com/contact/"&gt;email&lt;/a&gt; if you have any ideas or run into any issues. Of course you can always fork the project and send me a pull request or a patch via email.&lt;/p&gt;
&lt;p&gt;Fuubar only works on RSpec 2 right now, but there&amp;#8217;s no reason why it would be impossible to make it compatible with earlier versions. Also, Nick&amp;#8217;s original code had some functionality to find slow specs, but I omitted it because I couldn&amp;#8217;t really find a nice implementation.&lt;/p&gt;
&lt;p&gt;So, there&amp;#8217;s still a lot to do but I think this is quite an improvement from the current formatters. Maybe we can even turn this into a patch for RSpec to make Fuubar one of the default formatters. What do you think?&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/b6w8aQ6j97U" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/fuubar-the-instafailing-rspec-progress-bar-formatter</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Making RSpec stop operation immediately after failing</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/BjJ20Qkc6Sg/making-rspec-stop-operation-immediately-after-failing" />
    
    <id>http://jeffkreeftmeijer.com/2010/making-rspec-stop-operation-immediately-after-failing</id>
    <updated>2010-11-08T00:00:00+01:00</updated>
    <summary>Discovering RSpec's fail_fast configuration option and command line argument</summary>
    <content type="html">
      
        &lt;p&gt;Imagine you&amp;#8217;re running your whole &lt;a href="https://github.com/rspec/rspec"&gt;RSpec&lt;/a&gt; spec suite and the first or second spec fails. You have about two hundred specs to go, but you want to dive in and fix that first failure right away. You&amp;#8217;d probably interrupt the run to get the failure details to go fix them, right?&lt;/p&gt;
&lt;p&gt;While this works most of the time (you might get some problems interrupting the run when using &lt;a href="http://seleniumhq.org/"&gt;Selenium&lt;/a&gt;) wouldn&amp;#8217;t it be awesome if RSpec could automatically stop and output when running into the first failure?&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/fusebox.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Aside from a &lt;a href="http://rubyforge.org/pipermail/rspec-users/2007-July/002376.html"&gt;three year old thread in the rspec-users group&lt;/a&gt;, I couldn&amp;#8217;t find a lot of useful data. So I set out to write something myself.&lt;/p&gt;
&lt;h3&gt;Fail-fast&lt;/h3&gt;
&lt;p&gt;I started poking around in the RSpec source and accidentally stumbled on &lt;a href="http://relishapp.com/rspec/rspec-core/"&gt;RSpec&amp;#8217;s generated documentation on Relish&lt;/a&gt;, where I found &lt;a href="http://relishapp.com/rspec/rspec-core/v/2-0/dir/configuration/fail-fast"&gt;the &lt;code&gt;fail_fast&lt;/code&gt; configuration option&lt;/a&gt; that got introduced in &lt;a href="http://rubygems.org/gems/rspec-core/versions/2.0.0.rc"&gt;rspec-core 2.0.0.rc&lt;/a&gt;. It actually does &lt;em&gt;exactly&lt;/em&gt; what I wanted to achieve.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;fail_fast&lt;/code&gt; option will cause RSpec to immediately stop running your specs and output the failure details whenever it fails for the first time, giving you a nice speed boost when trying to fix some spec fails. Usage is pretty straightforward:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# spec/spec_helper.rb&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fail_fast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Command line support&lt;/h3&gt;
&lt;p&gt;Since you probably still want to run your whole spec suite without using the &lt;code&gt;fail_fast&lt;/code&gt; option from time to time, setting it in &lt;code&gt;spec/spec_helper.rb&lt;/code&gt; isn&amp;#8217;t really a great option. So I added a &lt;a href="https://github.com/rspec/rspec-core/issues/issue/219"&gt;command line option&lt;/a&gt; so you can use it whenever you want:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rspec spec/ --fail-fast
&lt;span class="go"&gt;.F&lt;/span&gt;

&lt;span class="go"&gt;Failures:&lt;/span&gt;
&lt;span class="go"&gt;  1) Swinger should set the Capybara driver&lt;/span&gt;
&lt;span class="go"&gt;     Failure/Error: Capybara.current_driver.should_not == :rack_test&lt;/span&gt;

&lt;span class="go"&gt;Finished in 0.00479 seconds&lt;/span&gt;
&lt;span class="go"&gt;2 examples, 1 failure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;--fail-fast&lt;/code&gt; option was only &lt;a href="http://blog.davidchelimsky.net/2010/11/07/rspec-21-is-released/"&gt;released&lt;/a&gt; yesterday as a part of RSpec 2.1, so don&amp;#8217;t forget to update.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/BjJ20Qkc6Sg" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/making-rspec-stop-operation-immediately-after-failing</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Giving back to the community: the start of The Social Charity Collective</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/-dZOmVehdgQ/giving-back-to-the-community-the-start-of-the-social-charity-collective" />
    
    <id>http://jeffkreeftmeijer.com/2010/giving-back-to-the-community-the-start-of-the-social-charity-collective</id>
    <updated>2010-11-05T00:00:00+01:00</updated>
    <summary>Sometimes giving back to the community isn't just about open source contributions.</summary>
    <content type="html">
      
        
          &lt;p&gt;This is a guest post written by &lt;a href="http://twitter.com/joshkalderimis" title="Josh Kalderimis"&gt;@joshkalderimis&lt;/a&gt;. Josh is a Ruby programmer at &lt;a href="http://www.tty.nl/"&gt;TTY&lt;/a&gt; and an enthusiastic hacker, open source contributor and &lt;a href="http://amsterdam-rb.org"&gt;Amsterdam.rb&lt;/a&gt; organizer.
        &lt;div class="notice"&gt;
Please show your support for Movember by going to &lt;a href="http://thedailymo.com"&gt;The Daily Mo&lt;/a&gt; and becoming a MO Poser!
&lt;/div&gt;
&lt;p&gt;Just over a year ago &lt;a href="http://twitter.com/pat" title="Pat Allan"&gt;@pat&lt;/a&gt;, of &lt;a href="http://freelancing-god.github.com/ts/en/"&gt;Thinking Sphinx&lt;/a&gt; fame, came to Amsterdam and gave a talk at the Amsterdam.rb meeting about how we should all be giving back to the community.&lt;/p&gt;
&lt;p&gt;From &lt;a href="http://www.slideshare.net/freelancing_god/from-the-keyboard-to-the-community-rails-underground?from=ss_embed"&gt;Pat&amp;#8217;s presentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo; As (Ruby) developers, we&amp;#8217;re quite good at learning and improving ourselves. Sometimes, though, we should direct that energy to helping others learn and improve &amp;#8211; across different levels of community/society. &amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Apart from being a great developer, Pat is also a wise man with a heart of gold, and the presentation and what it was saying stuck with me. How could I give back to the community? What could I do? Which community? Starting with &lt;span class="caps"&gt;OSS&lt;/span&gt; contributions was good, but it is also self serving as the contributions are in my name. There must be a better greater cause which might need some love.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://movember.com"&gt;&lt;img src="http://jeffkreeftmeijer.com/images/movember.jpg" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you haven&amp;#8217;t heard of &lt;a href="http://movember.com"&gt;Movember&lt;/a&gt;, here&amp;#8217;s a quick description stolen directly from their website :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo; What started out in Australia back in 2003 with 30 guys for a bit of a laugh has evolved into a worldwide campaign raising millions of euros and involving thousands of Mo Bros and Sistas from all corners of the globe, all coming together with the aim of changing the face of men&#x2019;s health&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Having taken part in Movember three times, growing Mo&amp;#8217;s (mustaches) and raising money for the New Zealand Movember run charity, when the opportunity came by to help the Movember Netherlands charity with a little web page to sit in-between their site and the donation form I jumped at the chance. In the end it was 10-ish hours of work, which included creating the page and exporting the information daily. Knowing the organiser of the charity was a plus as that is how he thought to get in touch with me.&lt;/p&gt;
&lt;p&gt;Well, a year later Movember was kicking into gear again, this time Charin had grander plans. We sat down and planned out a full registration system which would use a third party for donations, along with Facebook and Twitter for letting your friends know about updates on your Mo progress. &lt;a href="http://tty.nl"&gt;&lt;span class="caps"&gt;TTY&lt;/span&gt;&lt;/a&gt; came to the party and donated some of my time and I convinced some others in the local Ruby community to join the cause. Sadly due to legal issues surrounding privacy we could not do a registration system as it needed to pass through Movember Foundation HQ, and with only three weeks to go they were not able to give the a-ok so quickly.&lt;/p&gt;
&lt;p&gt;It was a set back, but we weren&amp;#8217;t prepared to give up. &lt;a href="http://twitter.com/spif" title="Stefan Fountain"&gt;@spif&lt;/a&gt; from &lt;a href="http://soocial.com"&gt;Soocial&lt;/a&gt; had also shown interest in helping out and suggested we copy the fun viral aspects of &lt;a href="http://snoranje.nl/snorgeanten"&gt;Snoranje&lt;/a&gt;, a charity event setup during the Football World Cup, also mustache related, and allow supporters to add a Mo to their Facebook and Twitter profile pics, as well as providing a place for Mo growers to do a daily update. Although this wasn&amp;#8217;t a direct help to Movember, it was in support of Movember, and provided a space for Dutch Movember participants to update on their daily growth, and hopefully the fun viral element would allow others who might not have heard of the cause to get involved.&lt;/p&gt;
&lt;p&gt;So we jumped into action, &lt;span class="caps"&gt;TTY&lt;/span&gt; would provide food, beer, and cover hosting costs, while we would code code code. Initially we had five ruby devs take part, but this grew by another 4-5, and also shrunk after a bit of dev burnout and personal commitments.&lt;/p&gt;
&lt;p&gt;Thus &lt;a href="http://github.com/TheSocialCharityCollective"&gt;TheSocialCharityCollective&lt;/a&gt; on GitHub was born. Ruby, Rails 3.1, Monogid + Mongodb + MongoHQ, Heroku for deployment, Carrierwave + S3 for uploads, and Devise + Omniauth (fork) for social network integrations, oh, and &lt;a href="http://jupiler.be"&gt;Jupiler&lt;/a&gt; beer for sustenance. All the code is open source and can be forked, hacked, and played around with, if we are going to give some time to Movember, why not also give our code to the community?&lt;/p&gt;
&lt;p&gt;&lt;a href="http://thedailymo.com"&gt;&lt;img src="http://jeffkreeftmeijer.com/images/thedailymo.jpg" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After two weeks of 3-4 days of overtime and weekend work, &lt;a href="http://www.thedailymo.com"&gt;The Daily Mo&lt;/a&gt; was ready, as well as &lt;a href="http://museumofmoart.com"&gt;The Museum of Mo Art&lt;/a&gt; for a locally competition.&lt;/p&gt;
&lt;p&gt;And as I write this article we are still adding features, changing text, including and updating links, and trying to spread the word, which is the hardest part of all.&lt;/p&gt;
&lt;p&gt;Now, this story might seem like a 1-2-3 of events, its underlying point is giving back to a local community or charity might involve a lot of time, but it&amp;#8217;s fantastic! We created a fun site which aims to be a bit of fun, we got to work and pair program with other great developers, we all learnt a lot, and we got to create while also helping a good cause out.&lt;/p&gt;
&lt;p&gt;TheSocialCharityCollective will not be disappearing after Movember, if anything it has given us all a taste of helping out a local charity in a fun viral way. Just like the hackfests you see taking place online and in cities all around us, this was what The Daily Mo was for us, a hackfest for a good cause. Maybe it would be great to see more of these, or have charities and good causes take heed of this concept and organize a few themselves, in fact, maybe thats what TheSocialCharityCollective could do and could become?&lt;/p&gt;
&lt;p&gt;So what can we do as a community? We all talk about devoting some time to open source, if it be a patch here or a blog post there, but I think we need to go an extra step further and give/donate some of our time and expertise to organizations / charities / causes which could benefit from some tech advice, a viral site, or maybe some improvements to their backoffice. We also need to talk to companies which can help with costs, maybe some hosting fees, maybe some beer or thank you gifts to the developers who lent a hand. Every little bit goes a very long way.&lt;/p&gt;
&lt;p&gt;Hopefully after you read this you will take the plunge and email a local charity, church group, school, sports team, or us, showing your interest, hell, maybe you have an idea already! But at the very least, keep what I have said at the back of your mind, who knows when the opportunity to help out will present itself.&lt;/p&gt;
&lt;p&gt;It would be great to hear your comments and thoughts on this topic, so please don&amp;#8217;t hesitate to leave a message below. And if you have some time, show your support for Movember and for us by going to &lt;a href="http://www.thedailymo.com"&gt;The Daily Mo&lt;/a&gt; and becoming a MO Poser.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/-dZOmVehdgQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/giving-back-to-the-community-the-start-of-the-social-charity-collective</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Capybara driver swapping on RSpec with Swinger</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/sM1s15Q85Dk/capybara-driver-swapping-on-rspec-with-swinger" />
    
    <id>http://jeffkreeftmeijer.com/2010/capybara-driver-swapping-on-rspec-with-swinger</id>
    <updated>2010-11-01T00:00:00+01:00</updated>
    <summary>Introducing Swinger: a really simple extension that allows your Capybara to have multiple partners in one session.</summary>
    <content type="html">
      
        &lt;div class="notice"&gt;Swinger is not maintained anymore, since its functionality is built into Capybara. Check out &lt;a href="http://jeffkreeftmeijer.com/2011/capybara-ate-swinger"&gt;Capybara ate Swinger&lt;/a&gt; for more information.&lt;/div&gt;
&lt;p&gt;If you&amp;#8217;ve been doing the acceptance testing dance for a while, you probably know it can get slow pretty fast. Not necessarily in every driver, but since you wanted awesome javascript stuff in your application you&amp;#8217;ve equipped &lt;a href="http://github.com/jnicklas/capybara"&gt;Capybara&lt;/a&gt; with &lt;a href="http://seleniumhq.org/"&gt;Selenium&lt;/a&gt; to actually run your tests in an actual browser. Ouch.&lt;/p&gt;
&lt;p&gt;The javascript interaction in your application is minimal, but &lt;em&gt;can&amp;#8217;t&lt;/em&gt; be tested &lt;em&gt;without&lt;/em&gt; Selenium. The worst part is that it&amp;#8217;s only ten specs. Wouldn&amp;#8217;t it be awesome to only use Selenium for those instead of the whole suite?&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/capybaras.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re using &lt;a href="http://cukes.info/"&gt;Cucumber&lt;/a&gt;, &lt;a href="http://github.com/jnicklas/capybara/blob/master/lib/capybara/cucumber.rb"&gt;Capybara&amp;#8217;s got your back&lt;/a&gt;, making it possible to use &lt;a href="http://github.com/aslakhellesoy/cucumber/wiki/tags"&gt;Cucumber&amp;#8217;s tagging feature&lt;/a&gt; to simply specify which driver you want to use per example, effectively allowing your Capybara to have multiple partners in one session.&lt;/p&gt;
&lt;p&gt;But since you like to think you&amp;#8217;re a rebel and rather test your application in pure Ruby, you&amp;#8217;re using plain &lt;a href="http://github.com/rspec/rspec"&gt;RSpec&lt;/a&gt; (maybe even with &lt;a href="http://github.com/cavalle/steak"&gt;Steak&lt;/a&gt;). So you need to do stuff like this when you want to switch drivers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# `scenario` is an alias for `it`, provided by Steak&lt;/span&gt;

&lt;span class="n"&gt;scenario&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;get a nice greeting&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:selenium&lt;/span&gt;
  &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Welcome&amp;#39;&lt;/span&gt;
  &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reset_current_driver&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Swinger&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://github.com/jeffkreeftmeijer/swinger"&gt;Swinger&lt;/a&gt; is a really simple Capybara extension that aims to make this a bit less horrible to look at. Using &lt;a href="http://blog.davidchelimsky.net/2010/06/14/filtering-examples-in-rspec-2"&gt;the new metadata feature in RSpec 2&lt;/a&gt;, you can simply set your &lt;code&gt;:driver&lt;/code&gt; per scenario or context:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;scenario&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;get a nice greeting&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:driver&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:selenium&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Welcome&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;when logged in&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:driver&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:rack_test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;scenario&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;get a nice greeting&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Welcome back&amp;#39;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;scenario&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;see the logout link&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Logout&amp;#39;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It also adds the &lt;code&gt;using_driver&lt;/code&gt; method to Capybara, allowing you to execute a block using a specific driver. This is especially useful when you&amp;#8217;re (still) not on RSpec 2 and can&amp;#8217;t use the new metadata feature:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;scenario&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;get a nice greeting&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;using_driver&lt;/span&gt; &lt;span class="ss"&gt;:selenium&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Welcome&amp;#39;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Installing Swinger is easy. Simply add it to your &lt;code&gt;Gemfile&lt;/code&gt; and require it in &lt;code&gt;spec_helper.rb&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;A patch for Capybara?&lt;/h3&gt;
&lt;p&gt;Let me know how you like it and be sure to create an &lt;a href="http://github.com/jeffkreeftmeijer/swinger/issues"&gt;issue on Github&lt;/a&gt;, send me a &lt;a href="https://github.com/inbox/new/jeffkreeftmeijer"&gt;Github message&lt;/a&gt; or an &lt;a href="http://jeffkreeftmeijer.com/contact/"&gt;email&lt;/a&gt; if you have any ideas or run into any issues. Of course you can always fork the project and send me a pull request or a patch via email.&lt;/p&gt;
&lt;p&gt;Oh, one last thing: I know this is a pretty minimal gem, but I wanted to just get it out there to use it myself and see if it needs any improvement before maybe turning it into a patch for Capybara.&lt;/p&gt;
&lt;p&gt;Would you like this to be part of Capybara or do you prefer keeping this a separate gem? Please let me know!&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/sM1s15Q85Dk" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/capybara-driver-swapping-on-rspec-with-swinger</feedburner:origLink></entry>
  
  <entry>
    <title type="html">RubyAndRails 2010</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/zr0O7jozYNk/rubyandrails-2010" />
    
    <id>http://jeffkreeftmeijer.com/2010/rubyandrails-2010</id>
    <updated>2010-10-25T00:00:00+02:00</updated>
    <summary>An overview of what inspired me the most attending RubyAndRails 2010 in Amsterdam</summary>
    <content type="html">
      
        &lt;p&gt;If you follow any of us on Twitter, you probably know &lt;a href="http://80beans.com"&gt;80beans&lt;/a&gt; went to &lt;a href="http://rubyandrails.eu"&gt;RubyAndRails&lt;/a&gt; in Amsterdam last week. In this weeks article, I&amp;#8217;ll try to explain what inspired me the most. If you need a &lt;em&gt;proper&lt;/em&gt; talk overview, check out &lt;a href="http://rubyandrails.eu/blog"&gt;the RubyAndRails blog&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Rails 3 Mountable apps&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://twitter.com/drogus" title="Piotr Sarnacki"&gt;@drogus&lt;/a&gt; gave a talk about the work he did on mountable applications in Rails 3 during the &lt;a href="http://rubysoc.org/"&gt;Ruby Summer of Code&lt;/a&gt;. While the ultimate goal was to have multiple Rails applications running in a single process, development switched to boosting the (existing) Rails Engines to be &amp;ldquo;almost as powerful as applications&amp;rdquo;. In fact, the only difference is that engines have to be mounted and can&amp;#8217;t be run standalone.&lt;/p&gt;
&lt;p&gt;I had a brief but very inspiring chat with Piotr during the Geek dinner and I&amp;#8217;m sure we&amp;#8217;ll hear a lot from him in the future.&lt;/p&gt;
&lt;h3&gt;Live music-hacking-jam-session&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://twitter.com/rosejn" title="Jeff Rose"&gt;@rosejn&lt;/a&gt; and &lt;a href="http://twitter.com/samaaron" title="Sam Aaron"&gt;@samaaron&lt;/a&gt; (the &lt;a href="http://twitter.com/lambdatones" title="&#x3bb;-tones"&gt;@lambdatones&lt;/a&gt;) gave a presentation on building audio synthesizers with &lt;a href="http://project-overtone.org/"&gt;Overtone&lt;/a&gt; and playing music in Clojure. They even did an amazing &lt;a href="http://www.youtube.com/watch?v=WXovdAEV_mI&amp;amp;hd=1"&gt;live music-hacking-jam-session&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/lambdatones.jpg" alt="" /&gt;&lt;/p&gt;
&lt;h3&gt;Profiling and Frankenstein programming&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://twitter.com/chastell" title="Piotr Szotkowski"&gt;@chastell&lt;/a&gt; dove into &lt;a href="http://projecteuler.net/index.php?section=problems&amp;amp;id=10"&gt;Project Euler&amp;#8217;s problem #10&lt;/a&gt; and found out finding the sum of all the primes below two million got extremely slow in Ruby. In his talk, he went over a couple of profiling tools and eventually went with &lt;a href="http://github.com/tmm1/perftools.rb/"&gt;perftools.rb&lt;/a&gt; for having the least overhead. After that, he went on tweaking the calculation code and eventually using &lt;a href="http://www.zenspider.com/ZSS/Products/RubyInline/"&gt;RubyInline&lt;/a&gt; to be able to use inline C, creating a really fast Frankenstein of languages.&lt;/p&gt;
&lt;h3&gt;Fighter jet pilots&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://twitter.com/coreyhaines" title="Corey Haines"&gt;@coreyhaines&lt;/a&gt; did an amazingly inspiring talk about a lot of things we (should) already know &amp;#8212; like &lt;span class="caps"&gt;BDD&lt;/span&gt; and pair programming &amp;#8212; but also reminded us of how awesome our profession is, without even coming close to the awesomeness of being a fighter jet pilot. He urged everyone in the room to practice more to become happier programmers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo; How did you get so happy?, Because I practiced and now I&amp;#8217;m awesome at programming. &amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;#8217;m still not sure why I missed the &lt;a href="http://coderetreat.com/"&gt;Code Retreat&lt;/a&gt; on Saturday.&lt;/p&gt;
&lt;h3&gt;Fancy programming&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://twitter.com/bakkdoor" title="Christopher &amp;quot;Mr. Fancy&amp;quot; Bertels"&gt;@bakkdoor&lt;/a&gt; gave a talk about his programming language called &lt;a href="http://www.fancy-lang.org/"&gt;Fancy&lt;/a&gt;. Inspired by Smalltalk, Ruby and Erlang and running on Rubinius since a couple of weeks, Fancy aims to be a reasonably fast truly object oriented dynamic language that&amp;#8217;s easy to understand.&lt;/p&gt;
&lt;p&gt;Fancy was written for fun and it seems like an amazing way to learn about programming in general:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo; We&amp;#8217;re very open to discussion on anything related to the language, its features or semantics. It&amp;#8217;s about the fun after all. &amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Mining for RubyGems&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://fngtps.com"&gt;Fingertips&lt;/a&gt; won this years &lt;a href="http://rubyandrails.eu/rumble"&gt;Rumble&lt;/a&gt; with &lt;a href="http://github.com/Fingertips/miner"&gt;Miner&lt;/a&gt;: a simple MacRuby application that displays interesting new projects in an App Store for Rubygems, complete with one-click installation. This application and its &lt;a href="http://minerapp.com"&gt;online counterpart&lt;/a&gt; were built in under eight hours. How&amp;#8217;s that for inspiring?&lt;/p&gt;
&lt;h3&gt;See you all next year!&lt;/h3&gt;
&lt;p&gt;There were a lot more interesting talks, but these were some that inspired me to get my hands dirty again. I had a lot of fun and I talked to a lot of interesting people during the pre-party, the Github meetup and the Geek dinner. Thanks everyone, I hope to see you all again next year!&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/zr0O7jozYNk" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/rubyandrails-2010</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Be awesome: write your .gemspec yourself</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/4f18YiOQkAo/be-awesome-write-your-gemspec-yourself" />
    
    <id>http://jeffkreeftmeijer.com/2010/be-awesome-write-your-gemspec-yourself</id>
    <updated>2010-10-18T00:00:00+02:00</updated>
    <summary>Why I'm still having issues with gem builders and .gemspec files and why I think you should consider using Bundler to bootstrap your RubyGems.</summary>
    <content type="html">
      
        &lt;p&gt;&lt;a href="http://github.com/technicalpickles/jeweler"&gt;Jeweler&lt;/a&gt; is an awesome tool that gives you a generator to quickly set up the main skeleton for building a RubyGem. It has a bunch of rake tasks that make generating, creating, pushing and releasing your gem super easy without having to deal with the scary stuff. It&amp;#8217;s great for anyone that thinks they don&amp;#8217;t know or understand how to build a gem.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m taking Jeweler as an example here. There are a lot of gem builders out there that do the same thing, I just think Jeweler is the one most people know or use. This is &lt;em&gt;not&lt;/em&gt; a Jeweler hate campaign, it&amp;#8217;s about gem builders in general.&lt;/p&gt;
&lt;p&gt;Half a year ago, the gems I released were all built and maintained using Jeweler, so I put my gem&amp;#8217;s information in a &lt;code&gt;Rakefile&lt;/code&gt; and  generated my &lt;code&gt;.gemspec&lt;/code&gt; &amp;#8212; the file used to build the actual gem &amp;#8212; from that. I published &amp;ldquo;&lt;a href="http://jeffkreeftmeijer.com/2010/gitignore-your-gemspec"&gt;.gitignore your *.gemspec&lt;/a&gt;&amp;rdquo;, arguing that the &lt;code&gt;.gemspec&lt;/code&gt; shouldn&amp;#8217;t be checked into source control, since it was a generated file.&lt;/p&gt;
&lt;p&gt;After getting a lot of responses and reading &lt;a href="http://twitter.com/wycats"&gt;@wycats&lt;/a&gt;&amp;#8217; &lt;a href="http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended"&gt;article&lt;/a&gt;, I realized that generating the &lt;code&gt;.gemspec&lt;/code&gt; file was silly, I completely changed my mind about the whole issue and wrote a follow up titled &amp;ldquo;&lt;a href="http://jeffkreeftmeijer.com/2010/dont-put-your-gemspec-in-your-rakefile/"&gt;Don&amp;#8217;t put your *.gemspec in your Rakefile&lt;/a&gt;&amp;rdquo;. I&amp;#8217;ve been manually maintaining my &lt;code&gt;.gemspec&lt;/code&gt;s ever since.&lt;/p&gt;
&lt;p&gt;A lot of people are still using (tools like) Jeweler, which is fine. I would like to ask you to take a look at &lt;a href="http://github.com/jeffkreeftmeijer/navvy/blob/develop/navvy.gemspec"&gt;a simple &lt;code&gt;.gemspec&lt;/code&gt;&lt;/a&gt; file sometime and ask yourself if this would more difficult to write and maintain than a &lt;code&gt;Rakefile&lt;/code&gt; specifying the exact same thing. I feel like a lot of people are using Jeweler because everyone did it when they started and haven&amp;#8217;t looked back since.&lt;/p&gt;
&lt;p&gt;Do we &lt;em&gt;need&lt;/em&gt; this extra layer of abstraction? Doesn&amp;#8217;t generating &lt;code&gt;.gemspec&lt;/code&gt;s make everything more cumbersome? Is the &lt;code&gt;Rakefile&lt;/code&gt; the correct place for your gem specification?&lt;/p&gt;
&lt;h3&gt;The new kid in town&lt;/h3&gt;
&lt;p&gt;If you read &lt;a href="http://twitter.com/ryanbigg"&gt;@ryanbigg&lt;/a&gt;&amp;#8217;s great &amp;ldquo;&lt;a href="http://github.com/radar/guides/blob/master/gem-development.md"&gt;Developing a RubyGem using Bundler&lt;/a&gt;&amp;rdquo; guide, you probably know that &lt;a href="http://gembundler.com"&gt;Bundler&lt;/a&gt; can also bootstrap your RubyGem using its &lt;code&gt;gem&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; bundle gem my_gem
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It&amp;#8217;ll give you everything you need &amp;#8212; like the &lt;code&gt;.gitignore&lt;/code&gt;, &lt;code&gt;Rakefile&lt;/code&gt;, &lt;code&gt;Gemfile&lt;/code&gt; and &lt;code&gt;.gemspec&lt;/code&gt; &amp;#8212; but it puts your gem specification right where it belongs: directly in your &lt;code&gt;.gemspec&lt;/code&gt;. Also, it sets up a &lt;code&gt;Gemfile&lt;/code&gt; that automatically  gets its dependencies from the &lt;code&gt;.gemspec&lt;/code&gt; file, like I explained &lt;a href="http://jeffkreeftmeijer.com/2010/bundler-because-your-gems-depend-on-gems-too/"&gt;before&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For a more detailed explanation, check out &lt;a href="http://github.com/radar/guides/blob/master/gem-development.md"&gt;Ryan&amp;#8217;s guide&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Time to move on&lt;/h3&gt;
&lt;p&gt;While Jeweler and the like were wicked-cool back in the day, I think it&amp;#8217;s time to move on. Maintaining writing a &lt;code&gt;.gemspec&lt;/code&gt; yourself isn&amp;#8217;t difficult at all and you don&amp;#8217;t need a tool to generate one for you, look:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../lib/your_gem/version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;Gem&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Specification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;your_gem&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;YourGem&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;VERSION&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_s&lt;/span&gt;

  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;an awesome gem&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;extended description&amp;quot;&lt;/span&gt;

  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authors&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Me Myself&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;One Other&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;me@example.com&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://github.com/user/your_gem&amp;#39;&lt;/span&gt;

  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_dependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rake&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_development_dependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rspec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;gt;= 2.0.0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;# ensure the gem is built out of versioned files&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rakefile&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{bin,lib,man,test,spec}/**/*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;README*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;LICENSE*&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="sb"&gt;`git ls-files -z`&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you like to have a tool to set up a gem skeleton for you, I would suggest using Bundler. It&amp;#8217;s the cleanest gem builder I&amp;#8217;ve come across so far.&lt;/p&gt;
&lt;p&gt;What do you think? Do you prefer treating your &lt;code&gt;.gemspec&lt;/code&gt; as a generated file? Do you use a gem builder? Let me know in the comments.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/4f18YiOQkAo" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/be-awesome-write-your-gemspec-yourself</feedburner:origLink></entry>
  
  <entry>
    <title type="html">The magical (and not harmful) rebase</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/o7bPU2s380s/the-magical-and-not-harmful-rebase" />
    
    <id>http://jeffkreeftmeijer.com/2010/the-magical-and-not-harmful-rebase</id>
    <updated>2010-10-11T00:00:00+02:00</updated>
    <summary>Why Git's rebase isn't evil and why I think you should use it more often.</summary>
    <content type="html">
      
        &lt;p&gt;About a month ago, I published a series of articles on &lt;a href="http://git-scm.org"&gt;Git&lt;/a&gt;, starting with &lt;a href="http://twitter.com/nvie" title="Vincent Driessen"&gt;@nvie&lt;/a&gt;&amp;#8217;s awesome &lt;a href="http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow"&gt;git-flow&lt;/a&gt; and &lt;a href="http://jeffkreeftmeijer.com/2010/git-your-act-together"&gt;asking you to write better commit messages&lt;/a&gt;. The series ended with an article named &amp;ldquo;&lt;a href="http://jeffkreeftmeijer.com/2010/the-mighty-reflog-and-the-amazing-bisect"&gt;The mighty reflog and the amazing bisect&lt;/a&gt;&amp;rdquo;. Guess what? I forgot the magical &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html"&gt;rebase&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In general people seem to know rebase as &lt;a href="http://jeffkreeftmeijer.com/2010/git-your-act-together#rebase-and-amend-to-get-rid-of-oops-commits"&gt;a tool to squash commits&lt;/a&gt;, but that&amp;#8217;s not its general purpose. As the name implies, it can be used to rebase your changes. Oh, and forget about &lt;a href="http://changelog.complete.org/archives/586-rebase-considered-harmful"&gt;that article that made you scared of rebase&lt;/a&gt;, nothing bad will happen if you know what you&amp;#8217;re doing.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s say you&amp;#8217;re working on a feature branch called &lt;code&gt;feature/login&lt;/code&gt; and somebody else implemented something and pushed it to the &lt;code&gt;develop&lt;/code&gt; branch. You need that change. What do you do?&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/rebase01.jpg" title="You need that change. What do you do?" alt="You need that change. What do you do?" /&gt;&lt;/p&gt;
&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; simply &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-merge.html"&gt;merge&lt;/a&gt; that into your feature branch, but that would result in one of those nasty merge commits:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/rebase02.jpg" title="You could simply merge that into your feature branch, but that would result in one of those nasty merge commits" alt="You could simply merge that into your feature branch, but that would result in one of those nasty merge commits" /&gt;&lt;/p&gt;
&lt;p&gt;Another option is to &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-cherry-pick.html"&gt;cherry-pick&lt;/a&gt; the change you need into your branch. While &amp;#8212; history wise &amp;#8212; I don&amp;#8217;t see a real problem with this, it&amp;#8217;s a bit of a hassle because you need to find the commit you need to merge in first.&lt;/p&gt;
&lt;p&gt;Git&amp;#8217;s &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html"&gt;Rebase&lt;/a&gt; allows you to temporarily rewind the commits you did in this branch, pull in everything from another branch and apply your commits on top of that again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; git rebase develop
&lt;span class="go"&gt;First, rewinding head to replay your work on top of it...&lt;/span&gt;
&lt;span class="go"&gt;Fast-forwarded feature/login to develop.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/rebase03.jpg" title="Git&amp;#39;s rebase allows you to temporarily rewind the commits you did in this branch, pull in everything from another branch and apply your commits on top of that again" alt="Git&amp;#39;s rebase allows you to temporarily rewind the commits you did in this branch, pull in everything from another branch and apply your commits on top of that again" /&gt;&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s as if you didn&amp;#8217;t start working in the &lt;code&gt;feature/login&lt;/code&gt; branch before the commits you pulled in were made. Nice, huh? You can also &lt;a href="http://www.gitready.com/advanced/2009/02/11/pull-with-rebase.html"&gt;pull with rebase&lt;/a&gt; so you don&amp;#8217;t have to switch out of your current branch.&lt;/p&gt;
&lt;h3&gt;Conflicts served in smaller chunks&lt;/h3&gt;
&lt;p&gt;Besides keeping your history clean, rebase also has your back when you run into a merge conflict during the rebase:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; git rebase develop
&lt;span class="go"&gt;First, rewinding head to replay your work on top of it...&lt;/span&gt;
&lt;span class="go"&gt;Applying: feature/login&lt;/span&gt;
&lt;span class="go"&gt;Using index info to reconstruct a base tree...&lt;/span&gt;
&lt;span class="go"&gt;Falling back to patching base and 3-way merge...&lt;/span&gt;
&lt;span class="go"&gt;Auto-merging config/environment.rb&lt;/span&gt;
&lt;span class="go"&gt;CONFLICT (content): Merge conflict in config/environment.rb&lt;/span&gt;
&lt;span class="go"&gt;Failed to merge in the changes.&lt;/span&gt;
&lt;span class="go"&gt;Patch failed at 0001 feature/login&lt;/span&gt;

&lt;span class="go"&gt;When you have resolved this problem run &amp;quot;git rebase --continue&amp;quot;.&lt;/span&gt;
&lt;span class="go"&gt;If you would prefer to skip this patch, instead run &amp;quot;git rebase --skip&amp;quot;.&lt;/span&gt;
&lt;span class="go"&gt;To restore the original branch and stop rebasing run &amp;quot;git rebase --abort&amp;quot;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Because rebase merges every commit individually, conflicts will be served in smaller chunks making them easier to fix and understand. When you&amp;#8217;re done fixing a conflict, simply &lt;code&gt;git add&lt;/code&gt; the file and continue rebasing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; git rebase --continue
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Rebase vs Merge&lt;/h3&gt;
&lt;p&gt;When you&amp;#8217;re working on a feature branch and you need changes from the main development branch, I would suggest using rebase. Merge can be used when you want to merge a feature branch back into your development branch. That way, you&amp;#8217;ll be able to see when you merged in what in the future because you have that merge commit I called &amp;#8220;nasty&amp;#8221; before. It isn&amp;#8217;t, really.&lt;/p&gt;
&lt;p&gt;What I would like to ask you is to rebase your feature branch to the main development branch before merging it in. This way you make sure your branch applies cleanly to the branch you&amp;#8217;re merging into.&lt;/p&gt;
&lt;p&gt;Do you use rebase? When do you choose rebase over merge? Do you &lt;a href="http://darwinweb.net/articles/the-case-for-git-rebase"&gt;rebase exclusively&lt;/a&gt;? Are you still &lt;a href="http://changelog.complete.org/archives/586-rebase-considered-harmful"&gt;scared of rebase&lt;/a&gt;? Let me know in the comments.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/o7bPU2s380s" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/the-magical-and-not-harmful-rebase</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Let's not add_bundler_dependencies anymore</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/WvCT2_k4K9M/lets-not-add_bundler_dependencies-anymore" />
    
    <id>http://jeffkreeftmeijer.com/2010/lets-not-add_bundler_dependencies-anymore</id>
    <updated>2010-09-27T00:00:00+02:00</updated>
    <summary>Letting the Bundler Gemfile read your .gemspec instead of the other way around.</summary>
    <content type="html">
      
        &lt;p&gt;The article I published about &lt;a href="http://jeffkreeftmeijer.com/2010/bundler-because-your-gems-depend-on-gems-too/"&gt;using Bundler to manage your gem&amp;#8217;s gem dependencies&lt;/a&gt; turned out a bit different than I thought. I started writing it because I had a problem with &lt;a href="http://gembundler.com"&gt;Bundler&lt;/a&gt;&amp;#8217;s &lt;code&gt;add_bundler_dependencies&lt;/code&gt; method and I wanted to propose a better way to handle this.&lt;/p&gt;
&lt;p&gt;So I went to Bundler&amp;#8217;s amazing little website to read up the nasty feature, but it was &lt;a href="http://www.google.com/search?q=%22add_bundler_dependencies%22+site%3Agembundler.com"&gt;gone&lt;/a&gt;. After looking around for a second, I found the new &amp;ldquo;&lt;a href="http://gembundler.com/rubygems.html"&gt;Using Bundler with Rubygem gemspecs&lt;/a&gt;&amp;rdquo; page and realized a lot had changed since everybody got excited about &lt;code&gt;add_bundler_dependencies&lt;/code&gt; at Railsconf.&lt;/p&gt;
&lt;p&gt;I ended up writing a cheerful article about how awesome Bundler was, since my biggest annoyance had been destroyed. Now, a couple of weeks later, &lt;a href="http://andre.arko.net/2010/05/01/bundler-for-gem-development"&gt;this article about &lt;code&gt;add_bundler_dependencies&lt;/code&gt;&lt;/a&gt; started to get mentioned again. Please allow me to explain why I think you should stop using it.&lt;/p&gt;
&lt;h3&gt;Why?&lt;/h3&gt;
&lt;p&gt;If you don&amp;#8217;t know, &lt;code&gt;add_bundler_dependencies&lt;/code&gt; allows you to make your &lt;code&gt;.gemspec&lt;/code&gt; get its dependencies from your &lt;code&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# .gemspec&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bundler&amp;#39;&lt;/span&gt;
&lt;span class="no"&gt;Gem&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Specification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_bundler_dependencies&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;A very convenient way not to have to specify your dependencies twice, right? The problem with this is that you just added Bundler to your gem&amp;#8217;s dependencies.&lt;/p&gt;
&lt;p&gt;This feels totally backwards, since the &lt;code&gt;.gemspec&lt;/code&gt; file should be the one specifying Gem dependencies. If you ask me, getting them from the &lt;code&gt;Gemfile&lt;/code&gt; instead and adding a dependency is wrong. Bundler should only be needed when you &lt;em&gt;want&lt;/em&gt; to do something with the &lt;code&gt;Gemfile&lt;/code&gt;, the &lt;code&gt;.gemspec&lt;/code&gt; &amp;#8212; and the rest of the gem &amp;#8212; should work without it.&lt;/p&gt;
&lt;h3&gt;Gemfiles reading .gemspecs&lt;/h3&gt;
&lt;p&gt;The &lt;a href="http://jeffkreeftmeijer.com/2010/bundler-because-your-gems-depend-on-gems-too/"&gt;new implementation&lt;/a&gt; turns this all around and allows you to let your &lt;code&gt;Gemfile&lt;/code&gt; read dependencies from your &lt;code&gt;.gemspec&lt;/code&gt;, so you don&amp;#8217;t need Bundler to install it anymore. It&amp;#8217;s a matter of simply throwing &lt;code&gt;gemspec&lt;/code&gt; into your &lt;code&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# Gemfile&lt;/span&gt;
&lt;span class="n"&gt;gemspec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Please, let&amp;#8217;s start using that one now. It&amp;#8217;s prettier, it makes more sense and there&amp;#8217;s a reason &lt;code&gt;add_bundler_dependencies&lt;/code&gt; isn&amp;#8217;t mentioned on the Bundler website anymore.&lt;/p&gt;
&lt;p&gt;&lt;del&gt;What do you think about a deprecation warning when still using &lt;code&gt;add_bundler_dependencies&lt;/code&gt;? &lt;a href="http://twitter.com/wycats" title="Yehuda Katz"&gt;@wycats&lt;/a&gt;? &lt;a href="http://twitter.com/carllerche" title="Carl Lerche"&gt;@carllerche&lt;/a&gt;? Anyone?&lt;/del&gt; Oh, it&amp;#8217;ll &lt;a href="http://jeffkreeftmeijer.com/2010/lets-not-add_bundler_dependencies-anymore/#comment-81240290"&gt;throw a deprecation warning&lt;/a&gt; too.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/WvCT2_k4K9M" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/lets-not-add_bundler_dependencies-anymore</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Terminitor: the Terminal automator from the future</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/0eRXxejbrEA/terminitor-the-terminal-automator-from-the-future" />
    
    <id>http://jeffkreeftmeijer.com/2010/terminitor-the-terminal-automator-from-the-future</id>
    <updated>2010-09-20T00:00:00+02:00</updated>
    <summary>Automating your Terminal workflow to save you some keystrokes.</summary>
    <content type="html">
      
        &lt;p&gt;&lt;a href="http://github.com/achiu/terminitor"&gt;Terminitor&lt;/a&gt; &amp;#8212; that&amp;#8217;s &lt;em&gt;not&lt;/em&gt; Termin&lt;strong&gt;a&lt;/strong&gt;tor &amp;#8212; is a really simple Terminal workflow automation tool by &lt;a href="http://twitter.com/arthur_chiu" title="Arthur Chiu"&gt;@arthur_chiu&lt;/a&gt; and the rest of the &lt;a href="http://www.padrinorb.com/"&gt;Padrino&lt;/a&gt; &lt;a href="http://www.padrinorb.com/team"&gt;team&lt;/a&gt;. It aims to automate the terminal commands you do when you start working on a project so you don&amp;#8217;t have to start your database, web server and editor every time you want to continue your work.&lt;/p&gt;
&lt;p&gt;After installing, you create a &amp;ldquo;terminit&amp;rdquo; &lt;span class="caps"&gt;YAML&lt;/span&gt; file that defines one or more tabs with the commands you want to run. I created a little config for working on my website:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="yaml"&gt;&lt;span class="c1"&gt;# ~/.terminitor/jeffkreeftmeijer.yml&lt;/span&gt;

&lt;span class="p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;server&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;
  &lt;span class="p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;cd ~/opensource/jeffkreeftmeijer.com&lt;/span&gt;
  &lt;span class="p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;jekyll --server --auto&lt;/span&gt;
&lt;span class="p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;browser&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;
  &lt;span class="p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;sleep 5&lt;/span&gt;
  &lt;span class="p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;open --background http://localhost:4000&lt;/span&gt;
&lt;span class="p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;vim&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;
  &lt;span class="p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;mvim ~/opensource/jeffkreeftmeijer.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will open up three tabs in my terminal. The first one starts the &lt;a href="http://github.com/mojombo/jekyll"&gt;Jekyll&lt;/a&gt; server, with the &lt;code&gt;--auto&lt;/code&gt; flag to make it update after every change automatically. Another tab sleeps for five seconds &amp;#8212; giving the server a bit of time to start &amp;#8212; and opens localhost in a browser window behind the terminal. The last one opens the project in Vim, so I can start writing right away.&lt;/p&gt;
&lt;p&gt;To do all of the above, I simply run Terminitor&amp;#8217;s &lt;code&gt;start&lt;/code&gt; command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ terminitor start jeffkreeftmeijer&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pretty cool, right? Be sure to check out the &lt;a href="http://github.com/achiu/terminitor/blob/master/README.md"&gt;&lt;span class="caps"&gt;README&lt;/span&gt;&lt;/a&gt;, where all of this stuff gets explained more thoroughly.&lt;/p&gt;
&lt;h3&gt;Checking in Termfiles?&lt;/h3&gt;
&lt;p&gt;Besides storing the configuration files in the &lt;code&gt;~/.terminitor/&lt;/code&gt; directory, you can also store everyhing in a &lt;code&gt;Termfile&lt;/code&gt; right inside your project. Checking this file into &lt;a href="http://git-scm.org"&gt;Git&lt;/a&gt; would allow other developers that work on the same project to use your Terminitor workflow.&lt;/p&gt;
&lt;p&gt;This may sound like a great feature but I&amp;#8217;m not too excited about it. First of all, I think personal configuration should never come anywhere near a source code repository since it&amp;#8217;s simply not part of the code or the application.&lt;/p&gt;
&lt;p&gt;Also, I want to be able to set up my &lt;em&gt;own&lt;/em&gt; workflow (I know using the &lt;code&gt;Termfile&lt;/code&gt; doesn&amp;#8217;t keep me from doing that) so I probably don&amp;#8217;t care about yours. If I do, I&amp;#8217;ll ask you to put it in a &lt;a href="http://gist.github.com"&gt;Gist&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Lastly, I&amp;#8217;d love to keep using Terminitor an option, so people who choose not to use it shouldn&amp;#8217;t have to deal with extra, meaningless configuration files flying around.&lt;/p&gt;
&lt;p&gt;I would suggest &lt;em&gt;not&lt;/em&gt; checking it in and keeping it to yourself right now. However, Arthur himself &lt;a href="http://github.com/jeffkreeftmeijer/jeffkreeftmeijer.com/commit/1b7da3c7665c698972ad50f2cb2fcc8f8eb74a73#commitcomment-150393"&gt;commented&lt;/a&gt; and explained how the &lt;code&gt;Termfile&lt;/code&gt; feature could go one step furter in the future and give you the ability to do something like this on a remote repository that has a &lt;code&gt;Termfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ terminitor clone jeffkreeftmeijer navvy&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Making it possible to do a one-command setup so you can start hacking on the project immediately. Now &lt;em&gt;that&lt;/em&gt; sounds promising.&lt;/p&gt;
&lt;h3&gt;What do you think?&lt;/h3&gt;
&lt;p&gt;Overall, Terminitor is a really nice tool that saved me a bunch of keystrokes already. I&amp;#8217;m going to keep using it and I&amp;#8217;d love to see what will happen to it in the future. What do you think about Terminitor and the whole &lt;code&gt;Termfile&lt;/code&gt; thing? I&amp;#8217;d love to hear your opinion.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/0eRXxejbrEA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/terminitor-the-terminal-automator-from-the-future</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Disabling ActiveModel callbacks</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/as_iOvJfsz8/disabling-activemodel-callbacks" />
    
    <id>http://jeffkreeftmeijer.com/2010/disabling-activemodel-callbacks</id>
    <updated>2010-09-13T00:00:00+02:00</updated>
    <summary>Trying to figure out how to temporarily disable callbacks. How would you solve this?</summary>
    <content type="html">
      
        &lt;p&gt;Remember the last time you wanted to create, update, save or destroy a record or document and your &lt;code&gt;before_create&lt;/code&gt; or &lt;code&gt;after_update&lt;/code&gt; fired when you didn&amp;#8217;t want it to? Or are you using &lt;a href="http://mongoid.org"&gt;Mongoid&lt;/a&gt; and did you include &lt;code&gt;Mongoid::Timestamps&lt;/code&gt; but don&amp;#8217;t want your &lt;code&gt;updated_at&lt;/code&gt; attribute to change for a specific action?&lt;/p&gt;
&lt;p&gt;Think for a second. Adding callbacks to disable them later is nasty and will result in code that&amp;#8217;s more difficult to maintain. You probably made a wrong design decision here, so get back to the drawing board and rethink this part of your application if you can.&lt;/p&gt;
&lt;p&gt;If you can&amp;#8217;t or you&amp;#8217;re completely sure you have a valid reason to skip your callbacks, you probably tried something like &lt;a href="http://intridea.com/2009/3/12/temporarily-disable-activerecord-callbacks"&gt;removing the callback method&lt;/a&gt; but found out that didn&amp;#8217;t work anymore.&lt;/p&gt;
&lt;p&gt;The way callbacks are handled completely changed in Rails 3, breaking approaches like above. The new &lt;a href="http://apidock.com/rails/ActiveSupport/Callbacks/ClassMethods/skip_callback"&gt;&lt;code&gt;skip_callback&lt;/code&gt;&lt;/a&gt; and &lt;a href="http://apidock.com/rails/ActiveSupport/Callbacks/ClassMethods/set_callback"&gt;&lt;code&gt;set_callback&lt;/code&gt;&lt;/a&gt; can be of some help though:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="c1"&gt;# I&amp;#39;m using Mongoid, but this should work for anything based on&lt;/span&gt;
  &lt;span class="c1"&gt;# ActiveModel.&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Document&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Timestamps&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sneaky_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skip_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:save&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:set_updated_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:save&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:set_updated_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will keep &lt;code&gt;Mongoid::Timestamps&lt;/code&gt; from calling &lt;code&gt;set_updated_at&lt;/code&gt; before saving the Document.&lt;/p&gt;
&lt;p&gt;Because I didn&amp;#8217;t like how this looked, I added a method called &lt;code&gt;without_callback&lt;/code&gt; to &lt;code&gt;ActiveSupport::Callbacks&lt;/code&gt; to allow you to temporarily disable callbacks in a block. Just throw this in &lt;code&gt;config/initializers/without_callback.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ActiveSupport::Callbacks::ClassMethods&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;without_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;skip_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;
    &lt;span class="n"&gt;set_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And you&amp;#8217;ll be able to do stuff like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="c1"&gt;# I&amp;#39;m using Mongoid, but this should work for anything based on&lt;/span&gt;
  &lt;span class="c1"&gt;# ActiveModel.&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Document&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Timestamps&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sneaky_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;without_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:save&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:set_updated_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;update_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;At least, that&amp;#8217;s how I solved it. I&amp;#8217;m not completely sure this is the prettiest way to do something like this. Do you know of a better way? Be sure to let me know in the comments.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/as_iOvJfsz8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/disabling-activemodel-callbacks</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Clients writing Cucumber stories?</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/vpQ8VOVgxeo/clients-writing-cucumber-stories" />
    
    <id>http://jeffkreeftmeijer.com/2010/clients-writing-cucumber-stories</id>
    <updated>2010-09-07T00:00:00+02:00</updated>
    <summary>Trying to clear some misconceptions about Cucumber while telling you my reason for not using it.</summary>
    <content type="html">
      
        &lt;p&gt;Although I told you &lt;a href="http://jeffkreeftmeijer.com/2010/steak-because-cucumber-is-for-vegetarians"&gt;Cucumber is for vegetarians&lt;/a&gt; a while back, I certainly don&amp;#8217;t think &lt;a href="http://afewgoodlines.com/post/1077316188/integration-testing-retrospective-part-i"&gt;it&amp;#8217;s redundant&lt;/a&gt;. Here are my two cents.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/cucumber.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;From &lt;a href="http://twitter.com/ryandotsmith" title="Ryan Smith"&gt;@ryandotsmith&lt;/a&gt;&amp;#8217;s &lt;a href="http://afewgoodlines.com/post/1077316188/integration-testing-retrospective-part-i"&gt;article&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;How wonderful! I will have the client write out cucumber features and I will copy and paste them into a features directory, then I will write a bunch of regular expressions that couple the client generated stories to my code. Yeah right! Perhaps I have the only clients in the world who suck at writing cucumber features, but in my experience cucumber stories are best written by developers.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nobody ever said you should let your client write cucumber stories on his own, copy and paste them into your &lt;code&gt;features&lt;/code&gt; directory and write regular expressions to translate this mess into clean Ruby code.&lt;/p&gt;
&lt;p&gt;Cucumber&amp;#8217;s &lt;a href="http://www.martinfowler.com/bliki/BusinessReadableDSL.html"&gt;Business Readable &lt;span class="caps"&gt;DSL&lt;/span&gt;&lt;/a&gt; allows your client to &lt;em&gt;read&lt;/em&gt; stories, not to &lt;em&gt;write&lt;/em&gt; them. He doesn&amp;#8217;t need to know &lt;a href="http://elabs.se/blog/15-you-re-cuking-it-wrong"&gt;what to keep in mind to make it easily regexable&lt;/a&gt; when writing stories, he shouldn&amp;#8217;t be writing anything by himself.&lt;/p&gt;
&lt;p&gt;The client and the programmer are, in this case, a perfect pair to write stories. The client knows &amp;#8212; for the most part &amp;#8212; what&amp;#8217;s supposed to be done, the programmer knows how to write stories and is able to give advice when the client proposes something wacky.&lt;/p&gt;
&lt;p&gt;Your client isn&amp;#8217;t stupid, but he probably doesn&amp;#8217;t know how to write Cucumber stories or build his own application. That&amp;#8217;s why he hired you, remember?&lt;/p&gt;
&lt;h3&gt;Why I don&amp;#8217;t use Cucumber&lt;/h3&gt;
&lt;p&gt;At &lt;a href="http://80beans.com"&gt;80beans&lt;/a&gt;, we sit down with clients to create &lt;a href="http://pivotaltracker.com/"&gt;Pivotal Tracker&lt;/a&gt; stories which we turn into acceptance tests later. We write these tests in &lt;a href="http://jeffkreeftmeijer.com/2010/steak-because-cucumber-is-for-vegetarians"&gt;Steak&lt;/a&gt;, which is a really simple acceptance testing library based on &lt;a href="http://rspec.info"&gt;RSpec&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This way the client gets an overview of the features we&amp;#8217;re building, which ones we&amp;#8217;re currently working on or how many we&amp;#8217;ve already delivered. The programmers write tests in Ruby, so they spend less time writing regular expressions and more time writing application code or playing ping-pong.&lt;/p&gt;
&lt;p&gt;Still, I understand that writing tests together with your client gives both the programmers and the client a better understanding of the project, because they went over every single link, form and button together. I would love to know how many of you use Cucumber and if you ever wrote tests while sitting down with a client, though.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/vpQ8VOVgxeo" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/clients-writing-cucumber-stories</feedburner:origLink></entry>
  
  <entry>
    <title type="html">The mighty reflog and the amazing bisect</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/0dPvX1zhRE4/the-mighty-reflog-and-the-amazing-bisect" />
    
    <id>http://jeffkreeftmeijer.com/2010/the-mighty-reflog-and-the-amazing-bisect</id>
    <updated>2010-08-30T00:00:00+02:00</updated>
    <summary>Telling you what Git's reflog and bisect do and why they're awesome.</summary>
    <content type="html">
      
        &lt;p&gt;This week I want to take the time to tell you what &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt;&amp;#8217;s &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-reflog.html"&gt;reflog&lt;/a&gt; and &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-bisect.html"&gt;bisect&lt;/a&gt; commands do and give you a use-case for both so you know when you can actually &lt;em&gt;use&lt;/em&gt; them.&lt;/p&gt;
&lt;p&gt;This might be old news for you, but I&amp;#8217;ve come across people that use Git but never heard of both or are afraid to use them because they&amp;#8217;re scary. In fact, they&amp;#8217;re not scary. They have the ability to save your ass and make you find bugs faster. Isn&amp;#8217;t that &lt;em&gt;Awesome&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/awesome.jpg" title="Awesome robot dinosaurs" alt="Awesome robot dinosaurs" /&gt;&lt;/p&gt;
&lt;h3&gt;The mighty reflog&lt;/h3&gt;
&lt;p&gt;You just did a &lt;code&gt;git reset&lt;/code&gt; that put you a couple of commits back or you just rebased your whole repository into one commit (don&amp;#8217;t do that) and you realize you didn&amp;#8217;t really want that.&lt;/p&gt;
&lt;p&gt;You probably knew you can reset commits, but did you know you can do that &amp;#8212; and more &amp;#8212; on a lot of &lt;em&gt;actions&lt;/em&gt; (like merges, pulls and rebases) as well? Simply use &lt;code&gt;git reflog&lt;/code&gt; to get an overview of the last actions you did:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; git reflog
&lt;span class="go"&gt;0db8285 HEAD@{0}: HEAD~5: updating HEAD&lt;/span&gt;
&lt;span class="go"&gt;177762a HEAD@{1}: commit: change affiliate field names&lt;/span&gt;
&lt;span class="go"&gt;bb5d920 HEAD@{2}: pull origin develop: Merge made by recursive.&lt;/span&gt;
&lt;span class="go"&gt;f6fade5 HEAD@{3}: commit: sort the category brands by name on ...&lt;/span&gt;
&lt;span class="go"&gt;9309873 HEAD@{4}: checkout: moving from feature/affiliate to develop&lt;/span&gt;
&lt;span class="go"&gt;92a80c2 HEAD@{5}: checkout: moving from develop to feature/affiliate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Because every action in this list has a SHA1 hash, you can use the commands you&amp;#8217;re familiar with. In our example, we want to reset to before we messed up:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; git reset 177762a
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now you moved back to right before you decided to reset or rebase. It&amp;#8217;s like nothing ever happened.&lt;/p&gt;
&lt;h3&gt;The amazing bisect&lt;/h3&gt;
&lt;p&gt;Something broke and you found out it worked six commits ago (in the release tagged &amp;ldquo;v0.1.24&amp;rdquo;) by doing a quick &lt;code&gt;git checkout v0.1.24&lt;/code&gt; and running your tests. However, you don&amp;#8217;t know which commit introduced the bug, but you want to find out what changed, who did it and if you can revert it quickly.&lt;/p&gt;
&lt;p&gt;A really quick way to do something like this is to use &lt;code&gt;git bisect&lt;/code&gt;. After starting, you have to specify a &amp;ldquo;good&amp;rdquo; commit and a &amp;ldquo;bad&amp;rdquo; one. In this case you know the commit tagged &amp;ldquo;v0.1.24&amp;rdquo; worked and the &amp;ldquo;develop&amp;rdquo; branch is broken:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; git bisect start
&lt;span class="gp"&gt;$&lt;/span&gt; git bisect good v0.1.24
&lt;span class="gp"&gt;$&lt;/span&gt; git bisect bad develop
&lt;span class="go"&gt;Bisecting: 2 revisions left to test after this (roughly 1 step)&lt;/span&gt;
&lt;span class="go"&gt;[5dec197fedabd9db02cc1621f5bbdb2e8defeb48] Merge branch hotfix/...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;What happened? Well, you switched off your development branch and &lt;code&gt;git bisect&lt;/code&gt; took you back three commits so you&amp;#8217;re at the one in the middle between the &amp;ldquo;good&amp;rdquo; and the &amp;ldquo;bad&amp;rdquo; one.&lt;/p&gt;
&lt;p&gt;Next, run your tests and see if the bug was in this commit already. Let&amp;#8217;s say it was, so you mark this commit &amp;ldquo;bad&amp;rdquo; as well:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; git bisect bad
&lt;span class="go"&gt;Bisecting: 0 revisions left to test after this (roughly 0 steps)&lt;/span&gt;
&lt;span class="go"&gt;[9dea486b10f14475beb56e9d67c6dd45c8fab088] sort the category ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You&amp;#8217;re taken to the next commit to test and you find out it worked in this one, so you mark it &amp;ldquo;good&amp;rdquo; and Git will tell you the commit that broke stuff:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; git bisect good
&lt;span class="go"&gt;154f34cd41619eaace63122480c8aa7180f7dbe6 is the first bad commit&lt;/span&gt;
&lt;span class="go"&gt;commit 154f34cd41619eaace63122480c8aa7180f7dbe6&lt;/span&gt;
&lt;span class="go"&gt;Author: Thijs Cadier&lt;/span&gt;
&lt;span class="go"&gt;Date:   Sat Aug 28 19:20:40 2010 +0200&lt;/span&gt;

&lt;span class="go"&gt;    An editor can publish a photo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now we know which commit introduced the bug, so you can use &lt;code&gt;git show&lt;/code&gt; to see what changed and fix it &amp;#8212; or maybe even use &lt;code&gt;git revert&lt;/code&gt; &amp;#8212; right away. Did you do this manually before? I did and I can tell you bisecting is a &lt;em&gt;lot&lt;/em&gt; faster.&lt;/p&gt;
&lt;h3&gt;Want more?&lt;/h3&gt;
&lt;p&gt;These are all use-cases that I&amp;#8217;ve run into, but the commands I talked about have a lot more cool features you might want to check out. Two good places to start would be &lt;a href="http://gitready.com"&gt;Git Ready&lt;/a&gt; by &lt;a href="http://twitter.com/qrush" title="Nick Quaranto"&gt;@qrush&lt;/a&gt; and &lt;a href="http://book.git-scm.com/"&gt;the Git Community Book&lt;/a&gt;, since they explain stuff like &lt;a href="http://gitready.com/intermediate/2009/02/09/reflog-your-safety-net.html"&gt;reflogging&lt;/a&gt; and &lt;a href="http://book.git-scm.com/5_finding_issues_-_git_bisect.html"&gt;bisecting&lt;/a&gt; more thoroughly.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/0dPvX1zhRE4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/the-mighty-reflog-and-the-amazing-bisect</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Git your act together</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/uh9eUH7NZII/git-your-act-together" />
    
    <id>http://jeffkreeftmeijer.com/2010/git-your-act-together</id>
    <updated>2010-08-23T00:00:00+02:00</updated>
    <summary>Explaining some things that I think would make using Git a bit more enjoyable when working in a team.</summary>
    <content type="html">
      
        &lt;p&gt;Last week I released &lt;a href="http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/"&gt;an article on git-flow&lt;/a&gt;, a tool that helps you keep your &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt; repositories organized. Today I&amp;#8217;d like to explain some things that tend to annoy me while working with other people and using Git.&lt;/p&gt;
&lt;p&gt;This article possibly won&amp;#8217;t tell you anything you didn&amp;#8217;t know already, but I just wanted to get this out there and tell you some things that I think would make working with Git a bit more enjoyable.&lt;/p&gt;
&lt;h3&gt;Write good commit messages&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://wildbit.com/blog/2008/11/11/the-importance-of-commit-messages/"&gt;Commit messages are important&lt;/a&gt;. They document the project&amp;#8217;s progress and they&amp;#8217;re a great way to see what has been done in a commit without having to read the code. Also, commit messages make it easy to dive into &lt;code&gt;git log&lt;/code&gt; and find that commit you want to review or revert.&lt;/p&gt;
&lt;p&gt;Obviously, you should think about your commit message before typing &lt;code&gt;git commit -m '&lt;/code&gt;, slamming your hand on your keyboard, adding the last &lt;code&gt;'&lt;/code&gt; and pressing return. Yes, people actually do that.&lt;/p&gt;
&lt;p&gt;Another thing is trying to be funny in their messages, blaming other people for writing bad code or writing stuff like &amp;ldquo;I don&amp;#8217;t know what the hell I was thinking.&amp;rdquo;. Newsflash: You&amp;#8217;re not funny, you&amp;#8217;re being an idiot.&lt;/p&gt;
&lt;p&gt;Check out &lt;a href="http://whatthecommit.com"&gt;whatthecommit.com&lt;/a&gt; for more bad examples.&lt;/p&gt;
&lt;p&gt;I don&amp;#8217;t want to know how you feel, what you ate, what time it is or who fucked up in the first place. Please tell me what you did and &lt;em&gt;how&lt;/em&gt; you did it. Don&amp;#8217;t try to be clever, witty or funny. Document your change as good as you can and nothing else.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;&lt;a href="http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html"&gt;A note about Git commit messages&lt;/a&gt;&amp;rdquo; by &lt;a href="http://twitter.com/tpope/" title="Tim Pope"&gt;@tpope&lt;/a&gt; might be a good read if you think you need some guidelines.&lt;/p&gt;
&lt;p&gt;&lt;a name="commit-all-the-fucking-time"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Commit all the fucking time&lt;/h3&gt;
&lt;p&gt;You&amp;#8217;re at work and your client sends you an email saying he wants a couple of small bugs fixed and the header to be blue instead of green. Other than the rest of your clients, he&amp;#8217;s right: these are really small bugs and changing the header&amp;#8217;s color is just a one-line change.&lt;/p&gt;
&lt;p&gt;After getting yourself some coffee, you fix the bugs and change the header in about five minutes. You commit everything:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="go"&gt;commit 32faa25585e162f82206b8ce791ec098d6e34677&lt;/span&gt;
&lt;span class="go"&gt;Author: Jeff Kreeftmeijer&lt;/span&gt;
&lt;span class="go"&gt;Date:   Mon Aug 23 11:27:10 2010 +0200&lt;/span&gt;

&lt;span class="go"&gt;  Fix some bugs and make the header blue.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After some time and some commits, the client calls you and asks you to undo the header&amp;#8217;s background color back to green. What do you do now?&lt;/p&gt;
&lt;p&gt;You can&amp;#8217;t &lt;code&gt;git revert 32faa25585e162f82206b8ce791ec098d6e34677&lt;/code&gt;, because that will bring back the bugs you fixed in that commit too. You&amp;#8217;ll have to manually check what the original hue of green was and change it back.&lt;/p&gt;
&lt;p&gt;Seriously. Just commit one thing at a time. Don&amp;#8217;t wait until you go home, don&amp;#8217;t fix three &lt;em&gt;really&lt;/em&gt; simple bugs before committing, commit &lt;em&gt;every single change separately&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name="rebase-and-amend-to-get-rid-of-oops-commits"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Rebase &amp;amp; &amp;#8212;amend to get rid of &amp;ldquo;oops&amp;rdquo;-commits&lt;/h3&gt;
&lt;p&gt;Let&amp;#8217;s say you wrote this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;started&lt;/span&gt;
  &lt;span class="n"&gt;update_attributes&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="ss"&gt;:started_at&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And you committed it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="go"&gt;commit a5d8e964f6ac22809ffcd7d38ec503e36a27e00b&lt;/span&gt;
&lt;span class="go"&gt;Author: Jeff Kreeftmeijer&lt;/span&gt;
&lt;span class="go"&gt;Date:   Mon Aug 23 15:11:10 2010 +0200&lt;/span&gt;

&lt;span class="go"&gt;  Add Task#start to set the started_at attribute to the current time.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After you committed, you find out that you missed the closing parenthesis on line four. You quickly fix it and commit the new version:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="go"&gt;commit b3ff4620c2accd674427322cd1c10d634ab63d3a&lt;/span&gt;
&lt;span class="go"&gt;Author: Jeff Kreeftmeijer&lt;/span&gt;
&lt;span class="go"&gt;Date:   Mon Aug 23 15:14:48 2010 +0200&lt;/span&gt;

&lt;span class="go"&gt;  Oops! Forgot the closing parenthesis in Task#start.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It might happen that the second commit didn&amp;#8217;t fix everything either and you have to commit a fix again. Maybe once more, because it&amp;#8217;s been a long day. You&amp;#8217;d end up with a bunch of commits fixing problems caused by previous commits.&lt;/p&gt;
&lt;p&gt;Well, now it works. After a week you don&amp;#8217;t like this method anymore and want to remove it, which should be easy because you committed small chunks, right? No. You can&amp;#8217;t just &lt;code&gt;git revert a5d8e964f6ac22809ffcd7d38ec503e36a27e00b&lt;/code&gt;, you&amp;#8217;ll have to revert both separately.&lt;/p&gt;
&lt;p&gt;Whenever you find yourself writing &amp;ldquo;oops&amp;rdquo; because you&amp;#8217;re fixing a mistake you made in the last commit, please &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html"&gt;rebase&lt;/a&gt; them into one. Don&amp;#8217;t worry, it sounds harder than it is.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s stick with the example we used above. We want to squash the last &lt;em&gt;two&lt;/em&gt; commits into one:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git rebase -i HEAD~2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A text editor opens and shows you something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="go"&gt;pick a5d8e96 Add Task#start to set the started_at attribute to the current time.&lt;/span&gt;
&lt;span class="go"&gt;pick b3ff462 Oops! Forgot the closing parenthesis in Task#start.&lt;/span&gt;

&lt;span class="gp"&gt;#&lt;/span&gt; Rebase a5d8e96..b3ff462 onto a5d8e96
&lt;span class="gp"&gt;#&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt; Commands:
&lt;span class="gp"&gt;#&lt;/span&gt;  p, &lt;span class="nv"&gt;pick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; use commit
&lt;span class="gp"&gt;#&lt;/span&gt;  r, &lt;span class="nv"&gt;reword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; use commit, but edit the commit message
&lt;span class="gp"&gt;#&lt;/span&gt;  e, &lt;span class="nv"&gt;edit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; use commit, but stop &lt;span class="k"&gt;for &lt;/span&gt;amending
&lt;span class="gp"&gt;#&lt;/span&gt;  s, &lt;span class="nv"&gt;squash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; use commit, but meld into previous commit
&lt;span class="gp"&gt;#&lt;/span&gt;  f, &lt;span class="nv"&gt;fixup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; like &lt;span class="s2"&gt;&amp;quot;squash&amp;quot;&lt;/span&gt;, but discard this commit&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;s log message
&lt;span class="gp"&gt;#&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt; If you remove a line here THAT COMMIT WILL BE LOST.
&lt;span class="gp"&gt;#&lt;/span&gt; However, &lt;span class="k"&gt;if &lt;/span&gt;you remove everything, the rebase will be aborted.
&lt;span class="gp"&gt;#&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you replace the second occurrence of &amp;ldquo;pick&amp;rdquo; with &amp;ldquo;squash&amp;rdquo; and save, another text editor pops up asking you to write a new commit message. After you saved that one, your commits will be merged into one, making your repository cleaner and allowing you to &lt;code&gt;git revert&lt;/code&gt; the whole thing at once.&lt;/p&gt;
&lt;p&gt;If you want more information about rebasing, be sure to read &amp;ldquo;&lt;a href="http://www.gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html"&gt;Squashing commits with Rebase&lt;/a&gt;&amp;rdquo; on &lt;a href="http://www.gitready.com"&gt;Git Ready&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If the commit you want to squash into is the last commit, you can even directly add new changes to it by using the &lt;code&gt;--amend&lt;/code&gt; flag when committing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="go"&gt;  git commit --amend&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;I&amp;#8217;m not perfect either&lt;/h3&gt;
&lt;p&gt;Before you start digging through my repositories: I&amp;#8217;ve learned a lot in the last couple of months and I&amp;#8217;m improving, but I&amp;#8217;m not perfect either. I&amp;#8217;d love to learn what you do to keep your Git repositories maintainable, so be sure to put your best tip in the comments.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/uh9eUH7NZII" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/git-your-act-together</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Why aren't you using git-flow?</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/pp7uiwMVNO8/why-arent-you-using-git-flow" />
    
    <id>http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow</id>
    <updated>2010-08-19T00:00:00+02:00</updated>
    <summary>Using Vincent Driessen's branching model and Git extensions to keep your repositories organized.</summary>
    <content type="html">
      
        &lt;p&gt;In January of this year, &lt;a href="http://twitter.com/nvie" title="Vincent Driessen"&gt;@nvie&lt;/a&gt; published &lt;a href="http://nvie.com/posts/a-successful-git-branching-model"&gt;&amp;ldquo;A successful Git branching model&amp;rdquo;&lt;/a&gt;, in which he explained how he keeps his Git repositories nice and tidy. In addition to that, he released &lt;a href="http://github.com/nvie/gitflow"&gt;git-flow&lt;/a&gt;; a bunch of Git extensions to make following this model extremely easy.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m astounded that some people never heard of it before, so in this article I&amp;#8217;ll try to tell you why it can make you happy and cheerful all day.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/gitflow.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;After installing git-flow, you can start a new repository in the current directory or convert an existing one to the new branch structure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git flow init&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will ask you a bunch of questions, but you probably want to accept the default values:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;No branches exist yet. Base branches must be created now.
Branch name for production releases: [master] 
Branch name for "next release" development: [develop] 
How to name your supporting branch prefixes?
Feature branches? [feature/] 
Release branches? [release/] 
Hotfix branches? [hotfix/] 
Support branches? [support/] 
Version tag prefix? []&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After you&amp;#8217;ve answered the questions, git flow sets your default branch to &lt;code&gt;develop&lt;/code&gt; (or whatever you named it) automatically, since that&amp;#8217;s the one you&amp;#8217;ll be working in.&lt;/p&gt;
&lt;p&gt;Now, simply use Git like you&amp;#8217;re used to, but only work on some small features in the &lt;code&gt;develop&lt;/code&gt; branch. If you need to work on a bigger feature, just create a feature branch based on &lt;code&gt;develop&lt;/code&gt;. Let&amp;#8217;s say you want to add a login page:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git flow feature start login&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create a new branch called &lt;code&gt;feature/login&lt;/code&gt;, based on our &lt;code&gt;develop&lt;/code&gt; branch and switches to it. Commit away and after you finish working on the login page, simply finish it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git flow feature finish login&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#8217;ll merge &lt;code&gt;feature/login&lt;/code&gt; back to &lt;code&gt;develop&lt;/code&gt; and delete the feature branch.&lt;/p&gt;
&lt;p&gt;When you&amp;#8217;re feature complete, simply start a release branch &amp;#8212; again, based on &lt;code&gt;develop&lt;/code&gt; &amp;#8212; to bump the version number and fix the last bugs before releasing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git flow release start v0.1.0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you finish a release branch, it&amp;#8217;ll merge your changes to &lt;code&gt;master&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; back to &lt;code&gt;develop&lt;/code&gt;, so you don&amp;#8217;t have to worry about your &lt;code&gt;master&lt;/code&gt; being ahead of &lt;code&gt;develop&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The last thing that makes git-flow awesome is it&amp;#8217;s ability to handle hotfixes. You start and finish a hotfix branch like anything else, but it&amp;#8217;s based on &lt;code&gt;master&lt;/code&gt; so you can quickly fix it when something&amp;#8217;s broken production and merge it back to &lt;code&gt;master&lt;/code&gt; and &lt;code&gt;develop&lt;/code&gt; using &lt;code&gt;finish&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Awesome, right? Now, what are you waiting for?&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/pp7uiwMVNO8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Bundler, because your gems depend on gems too</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/uo6f0VsHq38/bundler-because-your-gems-depend-on-gems-too" />
    
    <id>http://jeffkreeftmeijer.com/2010/bundler-because-your-gems-depend-on-gems-too</id>
    <updated>2010-08-16T00:00:00+02:00</updated>
    <summary>Bundler made gem dependencies in Rails projects a lot easier, but what about gems themselves?</summary>
    <content type="html">
      
        &lt;p&gt;&lt;a href="http://gembundler.com"&gt;Bundler&lt;/a&gt; made gem dependencies in Rails projects a lot easier. It creates a &amp;ldquo;bubble&amp;rdquo; of gems you listed in your &lt;code&gt;Gemfile&lt;/code&gt;, which means it only loads the ones you specified. While this might sound annoying, it keeps you from having incomplete gem dependency specifications like you did when you were still using &lt;code&gt;config.gem&lt;/code&gt;. This makes it easier to keep your &lt;code&gt;Gemfile&lt;/code&gt;  complete and your project simple to get up and running.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/bundler.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;But what about gems themselves? We use the &lt;code&gt;.gemspec&lt;/code&gt; file to specify gem dependencies for our gems, which &amp;#8212; when complete &amp;#8212; makes installing and using gems a breeze since it installs all dependencies automatically. Great.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m not talking about &lt;em&gt;using&lt;/em&gt; gems, though. I&amp;#8217;m talking about working &lt;em&gt;on&lt;/em&gt; and &lt;em&gt;contributing to&lt;/em&gt; gems. Your &lt;code&gt;.gemspec&lt;/code&gt; doesn&amp;#8217;t help contributors when they clone your project and want to get the tests running, they still have to read through it and install everything manually.&lt;/p&gt;
&lt;p&gt;Giving your gem a &lt;code&gt;Gemfile&lt;/code&gt; partially solves this problem. If you specify everything your gem depends on in your &lt;code&gt;Gemfile&lt;/code&gt;, contributors can quickly install everything they need without having to look anything up. The downside is that now we have two files (the &lt;code&gt;.gemspec&lt;/code&gt; and &lt;code&gt;Gemfile&lt;/code&gt;) that specify dependencies, which is nasty.&lt;/p&gt;
&lt;p&gt;Lucky for you, Bundler loves you. I didn&amp;#8217;t know this, but it can &lt;a href="http://gembundler.com/rubygems.html"&gt;read and install gems from your &lt;code&gt;.gemspec&lt;/code&gt;&lt;/a&gt;. To get it up and running, create a &lt;code&gt;Gemfile&lt;/code&gt; and add:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gemspec&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will check your &lt;code&gt;.gemspec&lt;/code&gt; for runtime and development dependencies (specified with &lt;code&gt;gem.add_runtime_dependency&lt;/code&gt; and &lt;code&gt;gem.add_development_dependency&lt;/code&gt;) and install them to the correct &lt;a href="http://gembundler.com/groups.html"&gt;group&lt;/a&gt; when running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ bundle install&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the only place we specify dependencies is in the &lt;code&gt;.gemspec&lt;/code&gt;, but Bundler can install everything for us making our gem and its dependencies easy to install for users and contributors.&lt;/p&gt;
&lt;p&gt;Oh, since you have a &lt;code&gt;Gemfile&lt;/code&gt; in your project now, here&amp;#8217;s a little bonus: You can use &lt;a href="http://envylabs.com/"&gt;Envy Labs&lt;/a&gt;&amp;#8217; &lt;a href="http://rubytracker.com/"&gt;Ruby Tracker&lt;/a&gt; to get notified when new versions of your dependencies get released so you can make sure your gem keeps working with the latest stuff. Nice tip, right?&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/uo6f0VsHq38" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/bundler-because-your-gems-depend-on-gems-too</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Node.js, web sockets and talking mice</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/P9qy7bdKThU/node-js-web-sockets-and-talking-mice" />
    
    <id>http://jeffkreeftmeijer.com/2010/node-js-web-sockets-and-talking-mice</id>
    <updated>2010-08-09T00:00:00+02:00</updated>
    <summary>Wrapping up my Node.js web socket experiment with a mouse chat.</summary>
    <content type="html">
      
        &lt;div class="notice" id="mouse_toggle"&gt;
If you see extra mouse cursors moving around: don&amp;#8217;t worry, they&amp;#8217;re part of the demo. You can always &lt;a href="#"&gt;disable&lt;/a&gt; them if you want.
&lt;/div&gt;
&lt;p&gt;Since my first two articles about my &lt;a href="http://nodejs.org"&gt;Node.js&lt;/a&gt; experiment were a great success, got a lot of reponses and even inspired some people to get their hands dirty, I decided to dive into the demo code once more and write a very last article about it.&lt;/p&gt;
&lt;p&gt;If you have no idea what this is all about, please start by reading &amp;ldquo;&lt;a href="http://jeffkreeftmeijer.com/2010/experimenting-with-node-js"&gt;Experimenting with Node.js&lt;/a&gt;&amp;rdquo; and &amp;ldquo;&lt;a href="http://jeffkreeftmeijer.com/2010/things-i-learned-from-my-node.js-experiment"&gt;Things I learned from my Node.js experiment&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;This time, I&amp;#8217;ve added the ability to chat with other cursors &amp;#8212; with &lt;a href="http://en.gravatar.com"&gt;Gravatar&lt;/a&gt; support! &amp;#8212; you can try it out by putting your e-mail address in the left input, your message in the right and hitting enter.&lt;/p&gt;
&lt;form id="chat"&gt;
&lt;input class="placeholder" id="email" placeholder="E-mail address"/&gt;&lt;input class="placeholder" id="text" maxlength="140" placeholder="Message"/&gt;&lt;input type="submit" style="display:none;"/&gt;
&lt;/form&gt;
&lt;p class="small"&gt;I&amp;#8217;m only using your e-mail address to fetch your Gravatar. I&amp;#8217;m not saving anything.&lt;/p&gt;
&lt;p&gt;When you submit the form, it sends your e-mail address and message to the web socket server. After your e-mail address is encrypted to MD5 with Node&amp;#8217;s &lt;a href="http://nodejs.org/api.html#crypto-236"&gt;crypto&lt;/a&gt; module, it gets broadcasted to the other clients together with your message:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;speak&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;md5&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
                  &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hex&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;
&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The encrypted e-mail address and message also get sent back to the client it came from, to be able to show users their own messages after sending them out.&lt;/p&gt;
&lt;p&gt;When the message gets received on the client side, a bit of &lt;a href="http://jquery.com"&gt;jQuery&lt;/a&gt; makes sure the Gravatar and the message get displayed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nx"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeouts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#mouse_&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; img&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#mouse_&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;img src=&amp;quot;http://www.gravatar.com/avatar/&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;email&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;?s=20&amp;quot; /&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;text&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;    
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#mouse_&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; .chat&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#mouse_&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; .chat&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;text&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;   
  &lt;span class="nx"&gt;timeouts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;$(&amp;#39;#mouse_&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; .chat&amp;#39;).hide()&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Also, a timeout gets created that removes the message from the screen after five seconds to try to keep your screen from overflowing.&lt;/p&gt;
&lt;p&gt;Like before, the &lt;a href="http://gist.github.com/488562"&gt;Gist&lt;/a&gt; with all the code I wrote for the experiment is updated in case you want to use it to build something awesome. If you do, please let me know. I&amp;#8217;d love to see what you come up with and I might even write another very last article.&lt;/p&gt;
&lt;p&gt;That wraps up my Node.js-web-socket-mouse-cursor-experiment, thanks again for the responses everyone. You certainly helped me out a lot trying to build this thing. As you might have guessed, I&amp;#8217;m seriously excited about Node.js and there will probably be more experiments and articles like this.&lt;/p&gt;
&lt;p&gt;Stay tuned and please let me know how you liked the experiment in the comments.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/P9qy7bdKThU" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/node-js-web-sockets-and-talking-mice</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Things I learned from my Node.js experiment</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/rBzU8z19zb0/things-i-learned-from-my-node.js-experiment" />
    
    <id>http://jeffkreeftmeijer.com/2010/things-i-learned-from-my-node.js-experiment</id>
    <updated>2010-08-03T00:00:00+02:00</updated>
    <summary>After letting my Node.js and web sockets experiment run for a week, I decided to improve the code a bit.</summary>
    <content type="html">
      
        &lt;p&gt;Last week, I published an article about &lt;a href="http://jeffkreeftmeijer.com/2010/experimenting-with-node-js"&gt;my very first experiment&lt;/a&gt; with &lt;a href="http://nodejs.org"&gt;Node.js&lt;/a&gt;. Being completely new to the whole thing, I decided to create a simple demo with web sockets.&lt;/p&gt;
&lt;p&gt;As you can read in last week&amp;#8217;s article, I created a page that checks your mouse cursor&amp;#8217;s location and sends it to the web socket. The web socket broadcasts your mouse location to every other user on the page, so everyone will see your cursor moving around. That means you will see everybody else&amp;#8217;s cursor moving around on your screen as well.&lt;/p&gt;
&lt;p&gt;&lt;object width="440" height="308"&gt;&lt;br /&gt;
  &lt;param name="allowfullscreen" value="true" /&gt;&lt;br /&gt;
  &lt;param name="allowscriptaccess" value="always" /&gt;&lt;br /&gt;
  &lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=13805413&amp;amp;server=vimeo.com&amp;amp;show_title=0&amp;amp;show_byline=0&amp;amp;show_portrait=0&amp;amp;color=ff9933&amp;amp;fullscreen=1" /&gt;&lt;br /&gt;
  &lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=13805413&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=ff9933&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always"  width="440" height="308"&gt;&lt;/embed&gt;&lt;br /&gt;
&lt;/object&gt;&lt;/p&gt;
&lt;p&gt;After letting my experiment run for a week, I learned a lot and improved the code. In this article I&amp;#8217;ll go over some things I learned and how I fixed some issues.&lt;/p&gt;
&lt;p&gt;Oh, and I&amp;#8217;ve updated &lt;a href="http://gist.github.com/488562"&gt;the Gist&lt;/a&gt;, in case you want to use the code.&lt;/p&gt;
&lt;h3&gt;People love fancy tricks with web sockets&lt;/h3&gt;
&lt;p&gt;After posting the article, it jumped to the &lt;a href="http://news.ycombinator.com/item?id=1548321"&gt;Hacker News&lt;/a&gt; front page within minutes and stayed there for over 26 hours, got &lt;a href="http://tweetmeme.com/story/1782313910/experimenting-with-nodejs-jeff-kreeftmeijer"&gt;retweeted like crazy&lt;/a&gt; and got featured on &lt;a href="http://5by5.tv/devshow/17"&gt;episode #17 of the Dev show&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A lot of people commented or sent me replies via twitter and it was fun to see how cursors chased each other around, felt the urge to &lt;a href="http://twitter.com/jnunemaker/status/19599899506"&gt;hump&lt;/a&gt; or &lt;a href="http://twitter.com/evilhackerdude/status/19687401793"&gt;bang&lt;/a&gt; other cursors, &lt;a href="http://www.reddit.com/r/programming/comments/ctvtz/experimenting_with_nodejs/c0v8243"&gt;draw shapes&lt;/a&gt;, or make sure nobody could read the text by constantly hovering over it.&lt;/p&gt;
&lt;p&gt;Thanks for the enthusiasm everybody, it was a lot of fun.&lt;/p&gt;
&lt;h3&gt;Node.js handled everything perfectly&lt;/h3&gt;
&lt;p&gt;Since this was my first experiment with Node.js, I expected cursors to start moving slow and the server to eventually crash. Especially because of this unexpected stream of visitors.&lt;/p&gt;
&lt;p&gt;I added a rate limit of forty milliseconds &amp;#8212; &lt;code&gt;mousemove()&lt;/code&gt; gets fired &lt;em&gt;a lot&lt;/em&gt; when you move your mouse &amp;#8212; to take some load off the server and I got &lt;a href="http://god.rubyforge.org/"&gt;God&lt;/a&gt; to monitor it all and restart the server if it crashed.&lt;/p&gt;
&lt;p&gt;However, except for some mistakes I made which caused the server to crash when somebody injected some javascript, Node.js handled everything perfectly. On a 256 MB &lt;span class="caps"&gt;VPS&lt;/span&gt;. Amazing.&lt;/p&gt;
&lt;h3&gt;You can fall back to flash&lt;/h3&gt;
&lt;p&gt;I posted a big notice above my article telling everyone to use Safari or Chrome, since they have support for web sockets. I didn&amp;#8217;t know you could fall back to flash at the time.&lt;/p&gt;
&lt;p&gt;Right now, the example runs on &lt;a href="http://twitter.com/rauchg" title="Guillermo Rauch"&gt;@rauchg&lt;/a&gt;&amp;#8216;s &lt;a href="http://github.com/LearnBoost/Socket.IO-node"&gt;Socket.IO&lt;/a&gt; instead of &lt;a href="http://github.com/miksago/node-websocket-server"&gt;web-socket-server&lt;/a&gt;, which automatically switches to flash when your browser doesn&amp;#8217;t support web sockets.&lt;/p&gt;
&lt;p&gt;When you do this for yourself, please start the server with &lt;code&gt;sudo&lt;/code&gt;. I couldn&amp;#8217;t get it to work properly and eventually got help from Guillermo himself in the #node.js &lt;span class="caps"&gt;IRC&lt;/span&gt; channel.&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://jeffkreeftmeijer.com/2010/experimenting-with-node-js/"&gt;demo&lt;/a&gt; is still online, if you want to take it for a spin in your web socket-less browser.&lt;/p&gt;
&lt;h3&gt;Injecting data into web sockets is easy&lt;/h3&gt;
&lt;p&gt;Since I needed to send cursor locations on &lt;code&gt;mousemove()&lt;/code&gt;, I had to create a connection to the web socket using Javascipt. This made it quite easy to send anything you want by injecting some Javascript into the page and calling &lt;code&gt;conn.send&lt;/code&gt;. The web socket server would simply broadcast it to the other clients.&lt;/p&gt;
&lt;p&gt;I was amazed to see &lt;a href="http://twitter.com/einaros" title="Einar Otto Stangvik"&gt;@einaros&lt;/a&gt; took the time to inject multiple cursors into the page and rotate them in 3D space. He even made &lt;a href="http://www.youtube.com/watch?v=jULOA7mSOac"&gt;a video&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;How would we prevent users from doing something like this? I haven&amp;#8217;t come up with a solution yet, since I think it&amp;#8217;s impossible to find out if the messages get sent on &lt;code&gt;mousemove()&lt;/code&gt; or if they&amp;#8217;re simply called using a javascript console. I&amp;#8217;d love to hear your thoughts on this.&lt;/p&gt;
&lt;h3&gt;I should have sanitized the messages&lt;/h3&gt;
&lt;p&gt;Although I ignored evil messages on the client side &amp;#8212; I checked if the &amp;#8220;action&amp;#8221; was &amp;#8220;move&amp;#8221; or &amp;#8220;close&amp;#8221; &amp;#8212; the Node.js server would still broadcast any &lt;span class="caps"&gt;JSON&lt;/span&gt; message it received. That meant injecting something like this would result in it being sent to every other client:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;  &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;injected!&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;haha!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Again, nothing would happen if a client would receive this message because it wouldn&amp;#8217;t contain anything it would understand. Still, not very nice to just broadcast this to everyone.&lt;/p&gt;
&lt;p&gt;Right now, the message listener catches syntax errors when trying to parse the message and checks if the action is &amp;#8220;close&amp;#8221; or &amp;#8220;move&amp;#8221;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;connection&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SyntaxError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Invalid JSON:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;close&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;move&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Ivalid request:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;    
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;More?&lt;/h3&gt;
&lt;p&gt;If you have any more feedback on the code sample, be sure to let me know. &lt;a href="http://gist.github.com/488562"&gt;The gist&lt;/a&gt; is updated and be sure to check out the &lt;a href="http://jeffkreeftmeijer.com/2010/experimenting-with-node-js/"&gt;new and improved version of the experiment&lt;/a&gt;.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/rBzU8z19zb0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/things-i-learned-from-my-node.js-experiment</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Experimenting with Node.js</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/83GIwMNWrM4/experimenting-with-node-js" />
    
    <id>http://jeffkreeftmeijer.com/2010/experimenting-with-node-js</id>
    <updated>2010-07-26T00:00:00+02:00</updated>
    <summary>Using Node.js and a bit of jQuery to annoy you.</summary>
    <content type="html">
      
        &lt;div class="notice" id="mouse_toggle"&gt;
If you see extra mouse cursors moving around: don&amp;#8217;t worry, they&amp;#8217;re part of the demo. You can always &lt;a href="#"&gt;disable&lt;/a&gt; them if you want.
&lt;/div&gt;
&lt;div class="notice"&gt;
I&amp;#8217;ve written a &lt;a href="http://jeffkreeftmeijer.com/2010/things-i-learned-from-my-node.js-experiment/"&gt;follow-up&lt;/a&gt; on this article, in which I improved a lot of the code. Be sure to read that one too!
&lt;/div&gt;
&lt;p&gt;If you&amp;#8217;re using a browser that supports web sockets, you might see some extra mouse cursors moving around. These are actually other people also looking at this page right now, live, as we speak. If you don&amp;#8217;t see anything, try to open up this page in another browser window next to this one and move your mouse in it.&lt;/p&gt;
&lt;p&gt;This is an experiment I did to play around with &lt;a href="http://nodejs.org"&gt;Node.js&lt;/a&gt; and web sockets. I&amp;#8217;ve put &lt;a href="http://gist.github.com/488562"&gt;everything in a Gist&lt;/a&gt; in case you want to try it out yourself. I&amp;#8217;ll explain how it works in this article.&lt;/p&gt;
&lt;h3&gt;Web socket server&lt;/h3&gt;
&lt;p&gt;Using &lt;a href="http://twitter.com/miksago"&gt;@miksago&lt;/a&gt;&amp;#8216;s &lt;a href="http://github.com/miksago/node-websocket-server"&gt;node-websocket-server&lt;/a&gt; made it extremely easy to send and receive messages from a web socket. Here&amp;#8217;s the code that runs the server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/lib/ws&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;connection&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;close&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;action&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;close&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After including the node-websocket-server library and creating the server, I add some listeners to know when clients disconnect or send a message and make sure messages get sent to the other clients. Whenever it receives a &lt;span class="caps"&gt;JSON&lt;/span&gt; message, it includes the connection&amp;#8217;s id before broadcasting it to the clients to make it possible to find out which cursor we need to move.&lt;/p&gt;
&lt;p&gt;I saved it as &lt;code&gt;server.js&lt;/code&gt;, so starting the server is as simple as running &lt;code&gt;node server.js&lt;/code&gt;. To make sure it keeps running, I daemonized it with &lt;a href="http://god.rubyforge.org/"&gt;God&lt;/a&gt;, using the same config file I used in the &amp;ldquo;&lt;a href="http://jeffkreeftmeijer.com/2010/daemonizing-navvy-with-god"&gt;Daemonizing Navvy with God&lt;/a&gt;&amp;rdquo; article.&lt;/p&gt;
&lt;h3&gt;Receiving messages&lt;/h3&gt;
&lt;p&gt;Now, in a regular javascript file &amp;#8212; with some &lt;a href="http://jquery.com"&gt;jQuery&lt;/a&gt; &amp;#8212; I included into this page, I connect to the web socket like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;connect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;WebSocket&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ws://jeffkreeftmeijer.com:8000&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;action&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;close&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#mouse_&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;action&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;move&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, this connects to the server we just started. When a message is received, it checks the action it&amp;#8217;s supposed to perform. If the action is &amp;ldquo;move&amp;rdquo;, it&amp;#8217;ll move a mouse cursor on the screen using the &lt;code&gt;move()&lt;/code&gt; function I&amp;#8217;ll show you later. If it&amp;#8217;s &amp;ldquo;close&amp;rdquo;, it means that the client disconnected and his cursor has to be removed from the screen.&lt;/p&gt;
&lt;h3&gt;Sending messages&lt;/h3&gt;
&lt;p&gt;Now we&amp;#8217;re able to receive messages, move and delete cursors. The last thing we need is the client to be able to send out messages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;mousemove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;ratelimit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;action&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;move&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;h&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Whenever you move your mouse, the &lt;code&gt;.mousemouse()&lt;/code&gt; function gets triggered that sends some &lt;span class="caps"&gt;JSON&lt;/span&gt; with the mouse position and screen size to the socket. The &lt;code&gt;ratelimit&lt;/code&gt; method makes sure that there&amp;#8217;s a forty millisecond interval between messages.&lt;/p&gt;
&lt;h3&gt;Moving the cursors&lt;/h3&gt;
&lt;p&gt;So, when the other clients receive a &amp;ldquo;move&amp;rdquo; message, it calls the &lt;code&gt;move()&lt;/code&gt; function, like I showed you before. It looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#mouse_&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s1"&gt;&amp;#39;&amp;lt;div class=&amp;quot;mouse&amp;quot; id=&amp;quot;mouse_&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;quot;/&amp;gt;&amp;#39;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#mouse_&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;left&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;px&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;top&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;px&amp;#39;&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It creates a div for the new mouse if it doesn&amp;#8217;t exist yet and moves it to the right position. Also, the x-position of the mouse gets calculated while keeping the difference in screen size in mind. This way it gets calculated from the center of the page, instead of from the left.&lt;/p&gt;
&lt;h3&gt;Blew your mind?&lt;/h3&gt;
&lt;p&gt;Tracking mouse movement and showing cursors to other clients is cool, but not useful in any way (although you could think of some cool use-cases for this). What this example &lt;em&gt;does&lt;/em&gt; show is that you can do pretty impressive things using web sockets and Node.js. And it was a great excuse to play around with it.&lt;/p&gt;
&lt;p&gt;This was the first thing I did using Node.js, so the code is probably far from perfect. If you know a way to improve it, please &lt;a href="http://gist.github.com/488562"&gt;fork the Gist&lt;/a&gt; and show me how it should be done. I&amp;#8217;ll update the article.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m excited about Node.js and I&amp;#8217;ll probably write and play around with it some more in the future, so stay tuned.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/83GIwMNWrM4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/experimenting-with-node-js</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Abandoned open source projects</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/bNnidxRlZl4/abandoned-open-source-projects" />
    
    <id>http://jeffkreeftmeijer.com/2010/abandoned-open-source-projects</id>
    <updated>2010-07-19T00:00:00+02:00</updated>
    <summary>About some problems I'm having with old open source projects and a Github feature request to try and fix them.</summary>
    <content type="html">
      
        &lt;p&gt;The first time I tried to create my own blog &amp;#8212; yes, it took me a couple of tries before I ended up with &lt;a href="http://github.com/mojombo/jekyll"&gt;Jekyll&lt;/a&gt; &amp;#8212; I wanted to use &lt;a href="http://www.tumblr.com/"&gt;Tumblr&lt;/a&gt;&amp;#8217;s &lt;a href="http://www.tumblr.com/docs/en/api"&gt;&lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt; to write articles using their back end, fetch everything and display it all on my website. I know that sounds stupid now, but bear with me for a minute.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/abandoned.jpg" title="I found myself abandoning the whole project" alt="I found myself abandoning the whole project" /&gt;&lt;/p&gt;
&lt;p&gt;Because I couldn&amp;#8217;t find a Ruby &lt;span class="caps"&gt;API&lt;/span&gt; wrapper that suited me and thought it would be wicked cool to write one myself, I created my first open source project. I couldn&amp;#8217;t think of a witty and clever name, so I just went with &amp;ldquo;&lt;a href="http://github.com/jeffkreeftmeijer/tumblr"&gt;Tumblr&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I had a lot of fun writing it and I even stopped working on the blog I built it for in the first place. It didn&amp;#8217;t have any tests, but I didn&amp;#8217;t really care about that back then so I didn&amp;#8217;t take the time to add any. I released it as a gem and some people started using it. Great.&lt;/p&gt;
&lt;p&gt;After a while, I realized I never used it myself and I started to dislike the project, but I decided to keep supporting it. That worked until I got a full-time job at &lt;a href="http://80beans.com"&gt;80beans&lt;/a&gt; and found myself abandoning the whole thing while working on more interesting stuff and even cooler open source projects.&lt;/p&gt;
&lt;p&gt;Today, it seems like the project is more popular then when I still worked on it. People are creating &lt;a href="http://github.com/jeffkreeftmeijer/tumblr/issues"&gt;issues&lt;/a&gt; I can&amp;#8217;t fix because I haven&amp;#8217;t been into the Tumblr &lt;span class="caps"&gt;API&lt;/span&gt; for a while now, nor am I planning to. Also, the project has more watchers than ever and it&amp;#8217;s the only project that pops up when you google for my name.&lt;/p&gt;
&lt;h3&gt;Marking projects as abandoned&lt;/h3&gt;
&lt;p&gt;I could add a nasty banner to my &lt;code&gt;README&lt;/code&gt; file telling people the project is abandoned and you shouldn&amp;#8217;t use it or you should fix any bugs you find yourself, but that&amp;#8217;s not what I think a &lt;code&gt;README&lt;/code&gt; is for. I could even remove the issues page so people can&amp;#8217;t report issues anymore, but I don&amp;#8217;t want that either. There&amp;#8217;s some helpful information in there.&lt;/p&gt;
&lt;p&gt;Instead, I would love to see a feature in &lt;a href="http://github.com"&gt;Github&lt;/a&gt; to mark a project as abandoned. Here&amp;#8217;s the idea:&lt;/p&gt;
&lt;p&gt;The project owner would have a check box in the project&amp;#8217;s admin area to mark the project as abandoned. Maybe the repository can get the abandoned state after not being updated for a certain amount of time automatically too, but I&amp;#8217;m not sure how long that&amp;#8217;s supposed to take.&lt;/p&gt;
&lt;p&gt;When abandoned, the repository page shows a message telling everyone that the project isn&amp;#8217;t being worked on anymore, you should fix bugs yourself and maybe contact the project owner if you want to start maintaining it.&lt;/p&gt;
&lt;p&gt;The project would have an &amp;ldquo;abandoned&amp;rdquo; tag next to its name when it gets displayed in the search results, so you don&amp;#8217;t have to compare last commit dates anymore. The abandoned projects could even be pushed down on the results page.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/github_search_abandoned.jpg" title="The project would have an &amp;amp;ldquo;abandoned&amp;amp;rdquo; tag next to its name when it gets displayed in the search results" alt="The project would have an &amp;amp;ldquo;abandoned&amp;amp;rdquo; tag next to its name when it gets displayed in the search results" /&gt;&lt;/p&gt;
&lt;p&gt;I don&amp;#8217;t think the issue tracker should be disabled for abandoned projects, since users can solve problems in there without needing the project owner. But when creating issues, you should see a message again saying the project is abandoned and it could take a while for anyone to respond.&lt;/p&gt;
&lt;h3&gt;What do you think?&lt;/h3&gt;
&lt;p&gt;This would be a good way for project owners to notify people they stopped supporting the project and will probably help them to find somebody that wants to start maintaining it. Also, it would become easier for users to find active projects and prevent them from using code that nobody has worked on for months.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve created &lt;a href="http://support.github.com/discussions/feature-requests/987-abandoned-projects"&gt;a feature request in Github&amp;#8217;s support forums&lt;/a&gt; to see if they&amp;#8217;re willing to start implementing something like this. I would love to hear your opinion too, so what do you think?&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/bNnidxRlZl4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/abandoned-open-source-projects</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Stop hacking, start fixing</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/aWI7UpEMKFQ/stop-hacking-start-fixing" />
    
    <id>http://jeffkreeftmeijer.com/2010/stop-hacking-start-fixing</id>
    <updated>2010-07-12T00:00:00+02:00</updated>
    <summary>Highlighting some mistakes I made in Navvy's specs, providing a solution and suggesting you do the same.</summary>
    <content type="html">
      
        &lt;p&gt;While reading through &lt;a href="http://jeffkreeftmeijer.com/navvy"&gt;Navvy&lt;/a&gt;&amp;#8217;s &lt;a href="http://github.com/jeffkreeftmeijer/navvy/blob/develop/spec/job_spec.rb"&gt;job specs&lt;/a&gt;, I found some really ugly &lt;code&gt;Time&lt;/code&gt; comparisons:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;should set the created_at date&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Navvy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:speak&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first_job&lt;/span&gt;
  &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;be_instance_of&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;
  &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
  &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;	
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p/&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;should set the run_at date to about 16 seconds from now&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;failed_job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Navvy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:speak&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Betsy&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;failed_job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stub!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:times_failed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and_return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
  &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;failed_job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retry&lt;/span&gt;
  &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run_at&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_i&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p/&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;should mark the job as complete when keep is true&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Navvy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Navvy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;
  &lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;
  &lt;span class="n"&gt;job_count&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;started_at&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;be_instance_of&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;
  &lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completed_at&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;be_instance_of&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Are you laughing? That&amp;#8217;s fine, it&amp;#8217;s funny. But please think about the last time &lt;em&gt;you&lt;/em&gt; did something like this while you&amp;#8217;re rolling over the floor in laughter. You&amp;#8217;re probably just as guilty as I am.&lt;/p&gt;
&lt;h3&gt;You&amp;#8217;re doing it wrong&lt;/h3&gt;
&lt;p&gt;Let&amp;#8217;s go over some problems, starting with the first code snippet.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Navvy::Job.enqueue&lt;/code&gt; creates a new job in the database and we want to be sure it sets the &lt;code&gt;created_at&lt;/code&gt; field. So we create a job and check it&amp;#8217;s &lt;code&gt;created_at&lt;/code&gt; value. Great.&lt;/p&gt;
&lt;p&gt;We don&amp;#8217;t know exactly how long it will take to create the job, so we create it and check it the &lt;code&gt;created_at&lt;/code&gt; is an instance of &lt;code&gt;Time&lt;/code&gt;, if it&amp;#8217;s less than &lt;code&gt;Time.now&lt;/code&gt; and less than 10 seconds ago.&lt;/p&gt;
&lt;p&gt;It never happened to me, but what would happen if &amp;#8212; for some strange reason &amp;#8212; the job would take more than 10 seconds to be created? In that case something is obviously wrong, but it has &lt;em&gt;nothing&lt;/em&gt; to do with the &lt;code&gt;created_at&lt;/code&gt; value. That&amp;#8217;ll probably be just fine.&lt;/p&gt;
&lt;p&gt;The second one is nasty as well. We want to rerun the job after sixteen seconds if it already failed twice, so we create a job and mock out the &lt;code&gt;.times_failed&lt;/code&gt; method to return &lt;code&gt;2&lt;/code&gt;. After that, we set the &lt;code&gt;time&lt;/code&gt; variable to &lt;code&gt;Time.now&lt;/code&gt;. We call &lt;code&gt;.retry&lt;/code&gt; on the job and expect the &lt;code&gt;run_at&lt;/code&gt; value to be 16 seconds more than our &lt;code&gt;time&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;Again: wrong. Theoretically, it could be possible that &lt;code&gt;.retry&lt;/code&gt; takes more than a second. In that case, our test will fail.&lt;/p&gt;
&lt;p&gt;In the last snippet, I seem to have given up hope. The test just checks if &lt;code&gt;started_at&lt;/code&gt; and &lt;code&gt;failed_at&lt;/code&gt; are instances of &lt;code&gt;Time&lt;/code&gt;. It doesn&amp;#8217;t really care what it is, it could be ten years in the past. Useless.&lt;/p&gt;
&lt;h3&gt;I guess I should have told him to &amp;ldquo;Freeze&amp;rdquo;.&lt;/h3&gt;
&lt;p&gt;&lt;sub&gt;No more &lt;a href="http://www.imdb.com/title/tt0111438/quotes"&gt;Timecop quotes&lt;/a&gt;, promise.&lt;/sub&gt;&lt;/p&gt;
&lt;p&gt;I did &lt;a href="http://github.com/jeffkreeftmeijer/navvy/commit/6bde639b13ff11f1756eca39e5cde93cdd83a853"&gt;this commit&lt;/a&gt; to clean up the mess a bit, using &lt;a href="http://twitter.com/jtrupiano" title="John Trupiano"&gt;@jtrupiano&lt;/a&gt;&amp;#8217;s &lt;a href="http://github.com/jtrupiano/timecop"&gt;Timecop&lt;/a&gt; to &amp;ldquo;freeze&amp;rdquo; time:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Timecop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2010&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Timecop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Since Timecop froze &lt;code&gt;Time.now&lt;/code&gt;, it&amp;#8217;ll always return January 1&lt;sup&gt;st&lt;/sup&gt; 2010, midnight. This means the first code snippet I showed you can be changed to this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;should set the created_at date&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Navvy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:speak&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first_job&lt;/span&gt;
  &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I didn&amp;#8217;t know Timecop existed when I started building Navvy, but I didn&amp;#8217;t search for it either. Instead, I wrote ugly and brittle tests that happened to do the job.&lt;/p&gt;
&lt;p&gt;My point is: If you run into a problem like this, find a solution. Somebody out there probably had the same problem before. If not, think of a great solution yourself and release it. Somebody might run into the same problem in the future and thank you for the amazing work you did to make their life easier:&lt;/p&gt;
&lt;p&gt;Thanks John, Timecop is awesome!&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/aWI7UpEMKFQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/stop-hacking-start-fixing</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Stumbling into Vim</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/763A0dcFzwE/stumbling-into-vim" />
    
    <id>http://jeffkreeftmeijer.com/2010/stumbling-into-vim</id>
    <updated>2010-07-06T00:00:00+02:00</updated>
    <summary>After using Textmate for everything In the last few years, I checked out Vim last week. Here's what happened.</summary>
    <content type="html">
      
        &lt;p&gt;I&#x2019;ve been using &lt;a href="http://macromates.com/"&gt;Textmate&lt;/a&gt; since I switched to the Mac a while back. It worked properly and I was fine with it. I never looked into &lt;a href="http://www.vim.org"&gt;Vim&lt;/a&gt; until a few months after &lt;a href="http://twitter.com/antekpiechnik"&gt;@antekpiechinik&lt;/a&gt; told me I should. That was last week.&lt;/p&gt;
&lt;p&gt;To be honest, I thought Vim was for people that felt too cool to go out of their terminals and that it didn&#x2019;t really add anything other than &#x201c;I&#x2019;m in the terminal&#x201d;-awesomeness. Needless to say, I was wrong.&lt;/p&gt;
&lt;p&gt;After I got excited by &lt;a href="http://vimeo.com/6999927"&gt;this &amp;ldquo;Welcome to Vim&amp;rdquo;-video&lt;/a&gt;, I dove into the &lt;code&gt;vimtutor&lt;/code&gt; to learn about normal, edit and visual mode, jumping though files, deleting, adding copying and editing stuff, running terminal commands from my text editor and stuff like that. &lt;code&gt;vimtutor&lt;/code&gt; is awesome, it walks you through the very basics to get familiar with the commands you&amp;#8217;ll be using most.&lt;/p&gt;
&lt;p&gt;After finishing that, I realized Vim was blazingly fast if you know the right shortcuts to use. Also, I felt quite confident about the Vim skills I learned, which lasted for about ten minutes until I tried to edit an actual Ruby file, got stuck and came to the conclusion that I forgot half of what I&amp;#8217;ve learned.&lt;/p&gt;
&lt;p&gt;Although I&amp;#8217;m still struggling, I believe using Vim will be a lot faster in comparison to Textmate after a while. I&amp;#8217;ll probably have to go through the &lt;code&gt;vimtutor&lt;/code&gt; again some time, though.&lt;/p&gt;
&lt;h3&gt;Customizing Vim&lt;/h3&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/vim.jpg" title="MacVim with the Railscasts color scheme and NERDTree" alt="MacVim with the Railscasts color scheme and NERDTree" /&gt;&lt;/p&gt;
&lt;p&gt;I&#x2019;ve never really taken the time to customize Textmate, except for installing the &lt;a href="http://railscasts.com/about"&gt;Railscasts theme&lt;/a&gt; and the &lt;a href="http://github.com/drnic/ruby-tmbundle"&gt;Ruby&lt;/a&gt; and &lt;a href="http://github.com/drnic/ruby-on-rails-tmbundle"&gt;Rails&lt;/a&gt; bundles. That was all I needed. No RSpec or Git bundles, I prefer to run specs and push to my repository in the terminal. Heck, I never even used Textmate&#x2019;s snippets and forgot to use CMD+T to open files all the time.&lt;/p&gt;
&lt;p&gt;After using Vim for a while, I realized I was missing some things. So I installed the &lt;a href="http://www.vim.org/scripts/script.php?script_id=2175"&gt;Railscasts Vim color scheme&lt;/a&gt; and &lt;a href="http://www.vim.org/scripts/script.php?script_id=1658"&gt;NERDTree&lt;/a&gt; as substitutes for Textmate&amp;#8217;s theme and project drawer.&lt;/p&gt;
&lt;p&gt;NERDTree is an awesome plugin, but since I got tired of typing &lt;code&gt;:NERDTreeToggle&lt;/code&gt; to open and close it I mapped it to my F2 key. Super fast.&lt;/p&gt;
&lt;p&gt;Vim has no line numbers by default, which creeped me out. Putting &lt;code&gt;set number&lt;/code&gt; in my &lt;code&gt;./~vimrc&lt;/code&gt; file did the trick. I searched around and stole some settings from other people too. Man, this thing is customizable.&lt;/p&gt;
&lt;p&gt;Oh, I&#x2019;ve put everything in my &lt;a href="http://github.com/jeffkreeftmeijer/dotfiles"&gt;dotfiles repository on Github&lt;/a&gt;. In case you need it.&lt;/p&gt;
&lt;p&gt;As I said, I still have a lot to learn but I think I&amp;#8217;ll stick to Vim for at least a while. So, keep an eye on this space for more vim stuff in the future and if you have some tips: be sure to let me know!&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/763A0dcFzwE" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/stumbling-into-vim</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Finally: Releasing Navvy 0.3</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/1gPZdfGBHrw/finally-releasing-navvy-03" />
    
    <id>http://jeffkreeftmeijer.com/2010/finally-releasing-navvy-03</id>
    <updated>2010-06-28T00:00:00+02:00</updated>
    <summary>Announcing a new version of Navvy, the database agnostic Ruby background job processor.</summary>
    <content type="html">
      
        &lt;p&gt;It&amp;#8217;s been a while since the last proper release of &lt;a href="http://jeffkreeftmeijer.com/navvy"&gt;Navvy&lt;/a&gt; &amp;#8212; the database agnostic Ruby background job processor &amp;#8212; but like I &lt;a href="http://jeffkreeftmeijer.com/2010/rise-of-the-navvy/"&gt;promised a while back&lt;/a&gt;, I&amp;#8217;ve been working on a new version whenever I could find the time.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/steam_navvy.jpg" title="Navvy 0.3" alt="Navvy 0.3" /&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m really excited to release &lt;a href="http://rubygems.org/gems/navvy/versions/0.3.0"&gt;Navvy 0.3&lt;/a&gt; and I think you&amp;#8217;re going to love it. It&amp;#8217;s &lt;a href="http://www.youtube.com/watch?v=Nx7v815bYUw"&gt;great, fantastic, amazing and magical&lt;/a&gt; and I&amp;#8217;d like to highlight four &amp;ldquo;tent poles&amp;rdquo; for you today.&lt;/p&gt;
&lt;p&gt;Ok, no more Apple marketing techniques. Promise.&lt;/p&gt;
&lt;h3&gt;Mongoid&lt;/h3&gt;
&lt;p&gt;In addition to the adapters for &lt;a href="http://api.rubyonrails.org/classes/ActiveRecord/Base.html"&gt;ActiveRecord&lt;/a&gt;, &lt;a href="http://datamapper.org/"&gt;DataMapper&lt;/a&gt;, &lt;a href="http://sequel.rubyforge.org/"&gt;Sequel&lt;/a&gt; and &lt;a href="http://mongomapper.com"&gt;MongoMapper&lt;/a&gt;, Navvy 0.3 intoduces a &lt;a href="http://mongoid.org/"&gt;Mongoid&lt;/a&gt; adapter that works exactly like the rest. Special thanks to &lt;a href="http://twitter.com/TomK32"&gt;@TomK32&lt;/a&gt; and &lt;a href="http://twitter.com/rubenfonseca"&gt;@rubenfonseca&lt;/a&gt; for helping out.&lt;/p&gt;
&lt;h3&gt;Navvy::Logger&lt;/h3&gt;
&lt;p&gt;The previous logger &amp;#8212; called &lt;code&gt;Navvy::Log&lt;/code&gt; &amp;#8212; was rather nasty due to some bad design decisions I made a while back, so I decided to completely rewrite it. Now it extends the &lt;a href="http://ruby-doc.org/core/classes/Logger.html"&gt;Logger&lt;/a&gt; from the standard Ruby library, which makes it more reliable and a lot smaller.&lt;/p&gt;
&lt;p&gt;Navvy will log to &lt;code&gt;STDOUT&lt;/code&gt; by default, but using &lt;code&gt;Navvy.configure&lt;/code&gt; you can provide your own logger:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;Navvy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;~/navvy.log&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Daemons&lt;/h3&gt;
&lt;p&gt;As I said in &lt;a href="http://jeffkreeftmeijer.com/2010/daemonizing-navvy-with-god"&gt;my previous Navvy article&lt;/a&gt;, I want to encourage users to use a process monitor (like &lt;a href="http://god.rubyforge.org/"&gt;God&lt;/a&gt;) since I believe handling background processes is none of Navvy&#x2019;s business.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s why Navvy&amp;#8217;s generator doesn&amp;#8217;t create the &lt;code&gt;script/navvy&lt;/code&gt; file anymore. Also, the nasty &lt;code&gt;Navvy::Worker.daemonize&lt;/code&gt; is gone. If you want to daemonize your worker process, you should set it up yourself now.&lt;/p&gt;
&lt;h3&gt;Generators&lt;/h3&gt;
&lt;p&gt;Navvy has had a Rails generator to create migrations for a while now, but it was ActiveRecord only. In 0.3, the Rails 2 and Rails 3 generators can create migrations for Sequel as well.&lt;/p&gt;
&lt;p&gt;As you probably know, DataMapper, MongoMapper and Mongoid don&amp;#8217;t need migrations. That means all generators are really easy to set up now.&lt;/p&gt;
&lt;h3&gt;Cleanups&lt;/h3&gt;
&lt;p&gt;Last but not least, Navvy got cleaned up quite a bit since 0.2. For example: the gem isn&amp;#8217;t built with &lt;a href="http://github.com/technicalpickles/jeweler"&gt;Jeweler&lt;/a&gt; anymore, which resulted in a cleaner &lt;code&gt;Rakefile&lt;/code&gt;, a cleaner &lt;code&gt;.gemspec&lt;/code&gt;  and no more need for that nasty &lt;code&gt;VERSION&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;For contributors (&lt;a href="http://wiki.github.com/jeffkreeftmeijer/navvy/contributors"&gt;&amp;hearts;&lt;/a&gt;), Navvy uses &lt;a href="http://gembundler.com"&gt;Bundler&lt;/a&gt; now, so you can quickly install everything you need to start hacking on Navvy.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s it. Be sure to give Navvy a try some time. The &lt;a href="http://wiki.github.com/jeffkreeftmeijer/navvy/"&gt;wiki&lt;/a&gt; is quite comprehensive but if you run into any problems, be sure to &lt;a href="http://github.com/inbox/new/jeffkreeftmeijer"&gt;ask&lt;/a&gt;, or &lt;a href="http://github.com/jeffkreeftmeijer/navvy/issues"&gt;create an issue on Github&lt;/a&gt;.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/1gPZdfGBHrw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/finally-releasing-navvy-03</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Railsconf 2010</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/cnJ9y9TBiU8/railsconf-2010" />
    
    <id>http://jeffkreeftmeijer.com/2010/railsconf-2010</id>
    <updated>2010-06-14T00:00:00+02:00</updated>
    <summary>About some of my personal highlights from Railsconf 2010 in Baltimore.</summary>
    <content type="html">
      
        &lt;p&gt;After an eight hour flight and a three hour train ride, &lt;a href="http://twitter.com/roy" title="Roy Tomeij"&gt;@roy&lt;/a&gt;, &lt;a href="http://twitter.com/thijsc" title="Thijs Cadier"&gt;@thijsc&lt;/a&gt;, &lt;a href="http://twitter.com/roncadier" title="Ron Cadier"&gt;@roncadier&lt;/a&gt;, &lt;a href="http://twitter.com/matsimitsu" title="Robert Beekman"&gt;@matsimitsu&lt;/a&gt;, &lt;a href="http://twitter.com/antekpiechnik" title="Antek Piechnik"&gt;@antekpiechnik&lt;/a&gt; and I arrived in Baltimore for &lt;a href="http://en.oreilly.com/rails2010"&gt;Railsconf&lt;/a&gt;. In this article I&amp;#8217;ll talk about some of my personal highlights.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/piechnik/4686817595/in/set-72157624235560242"&gt;&lt;img src="http://jeffkreeftmeijer.com/images/baltimore.jpg" title="Sunset in Baltimore by Antek Piechnik" alt="Sunset in Baltimore by Antek Piechnik" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Since we didn&amp;#8217;t go to any &lt;a href="http://en.oreilly.com/rails2010/public/schedule/stype/3+hour+tutorial"&gt;tutorial sessions&lt;/a&gt;, our Railsconf started on Tuesday, which gave us the time to eat burgers, drink beers and go to Washington before the conference.&lt;/p&gt;
&lt;p&gt;&lt;a name="steal"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In &lt;a href="http://twitter.com/jnunemaker" title="John Nunemaker"&gt;@jnunemaker&lt;/a&gt;&amp;#8216;s &amp;ldquo;&lt;a href="http://en.oreilly.com/rails2010/public/schedule/detail/14323"&gt;Don&#x2019;t repeat yourself, repeat others&lt;/a&gt;&amp;rdquo;, John gave a great peek into some stuff he learned while building &lt;a href="http://mongomapper.com/"&gt;MongoMapper&lt;/a&gt;. He urged everyone in the room to steal and write about it and explained why most of his open source projects are stolen as well. MongoMapper&amp;#8217;s internals &amp;#8212; like the plugin system &amp;#8212; look really nice, I have to look into that soon.&lt;/p&gt;
&lt;p&gt;For his book &amp;#8212; &lt;a href="http://www.amazon.com/gp/product/1934356166?ie=UTF8&amp;amp;tag=jeffkreef-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1934356166"&gt;Agile Webdevelopment with Rails&lt;/a&gt; &amp;#8212; &lt;a href="http://twitter.com/samruby" title="Sam Ruby"&gt;@samruby&lt;/a&gt; needed a test suite to check if the code samples keep working in newer versions of Rails. In &lt;a href="http://en.oreilly.com/rails2010/public/schedule/detail/12359"&gt;his talk&lt;/a&gt; he showed a &lt;a href="https://rails.lighthouseapp.com/projects/8994/tickets/4781-rjs-function-tests-fail"&gt;bug report he submitted to the Rails Lighthouse&lt;/a&gt;, created using his open source project called &lt;a href="http://github.com/rubys/gorp"&gt;Gorp&lt;/a&gt; (amongst others). His approach seems like a really nice way to submit a proper bug report and a little script to reproduce the problem without manually having to generate rails applications, generate scaffolds, edit files and test manually.&lt;/p&gt;
&lt;p&gt;On Tuesday evening, &lt;a href="http://twitter.com/wycats" title="Yehuda Katz"&gt;@wycats&lt;/a&gt; gave a &lt;a href="http://en.oreilly.com/rails2010/public/schedule/detail/14132"&gt;fantastic keynote&lt;/a&gt; that really inspired me to get my hands dirty. That meant I dove into &lt;a href="http://jeffkreeftmeijer.com/navvy"&gt;Navvy&lt;/a&gt; after drinking some beers at the &lt;a href="http://www.blueboxgrp.com/"&gt;Blue Box Group&lt;/a&gt; party later that night. And no, I didn&amp;#8217;t get anything done.&lt;/p&gt;
&lt;p&gt;I missed the Ruby Hero awards that night, but I&amp;#8217;d like to congratulate &lt;a href="http://twitter.com/fxn" title="Xavier Noria"&gt;@fxn&lt;/a&gt;, &lt;a href="http://twitter.com/josevalim" title="José Valim"&gt;@josevalim&lt;/a&gt;, &lt;a href="http://twitter.com/qrush" title="Nick Quaranto"&gt;@qrush&lt;/a&gt;, &lt;a href="http://twitter.com/tenderlove" title="Aaron Patterson"&gt;@tenderlove&lt;/a&gt;, &lt;a href="http://twitter.com/wayneeseguin" title="Wayne E. Seguin"&gt;@wayneeseguin&lt;/a&gt; and &lt;a href="http://twitter.com/seacreature" title="Gregory Brown"&gt;@seacreature&lt;/a&gt;. Great job and thanks for all your work guys!&lt;/p&gt;
&lt;p&gt;I was really looking forward to &lt;a href="http://twitter.com/defunkt" title="Chris Wanstrath"&gt;@defukt&lt;/a&gt;&amp;#8216;s &lt;a href="http://en.oreilly.com/rails2010/public/schedule/detail/14595"&gt;talk&lt;/a&gt; about &lt;a href="http://code.google.com/p/redis/"&gt;Redis&lt;/a&gt; and &lt;a href="http://github.com/defunkt/resque"&gt;Resque&lt;/a&gt; in &lt;a href="https://github.com/"&gt;Github&lt;/a&gt;. Chris gave some great insights into their background job processing strategy, which gave me some new ideas to steal (&lt;a href="#steal"&gt;Nunemaker said I could&lt;/a&gt;) for Navvy. It didn&amp;#8217;t bother me during the talk, but I would&amp;#8217;ve loved some slides for later reference since he gave a lot of information I can&amp;#8217;t really remember right now.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://twitter.com/josephwilk" title="Joseph Wilk"&gt;@josephwilk&lt;/a&gt; had a talk called &lt;a href="http://en.oreilly.com/rails2010/public/schedule/detail/14404"&gt;Rocket Fueled Cucumbers&lt;/a&gt; in which he explained some optimizations &lt;a href="http://songkick.com"&gt;Songkick&lt;/a&gt; did to their slow &lt;a href="http://cukes.info/"&gt;Cucumber&lt;/a&gt; test suites. Although &lt;a href="http://jeffkreeftmeijer.com/2010/steak-because-cucumber-is-for-vegetarians"&gt;I switched to Steak&lt;/a&gt; a while back, I learned a lot from this talk and it got me thinking about some stuff we could still do with Steak in the future. Expect more on this soon.&lt;/p&gt;
&lt;p&gt;On Wednesday evening, &lt;a href="http://twitter.com/deadprogram" title="Ron Evans"&gt;@deadprogram&lt;/a&gt; organized the &lt;a href="http://en.oreilly.com/rails2010/public/schedule/detail/14604"&gt;Music Jam&lt;/a&gt;, which treated us to some musical awesomeness I wasn&amp;#8217;t really expecting from a bunch of programmers. Great show, I didn&amp;#8217;t know you guys are rockstars as well.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/piechnik/4686736677/in/set-72157624242706784/"&gt;&lt;img src="http://jeffkreeftmeijer.com/images/music_jam.jpg" title="Joe Alba at the Railsconf Music Jam by Antek Piechnik" alt="Joe Alba at the Railsconf Music Jam by Antek Piechnik" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The last day, &lt;a href="http://twitter.com/imf" title="Ian McFarland"&gt;@imf&lt;/a&gt; gave a talk called &lt;a href="http://en.oreilly.com/rails2010/public/schedule/detail/14554"&gt;Agile the Pivotal Way&lt;/a&gt; bout the way they work at &lt;a href="http://pivotallabs.com"&gt;Pivotal Labs&lt;/a&gt;. Ian covered things like pair programming, having breakfast and playing ping pong at work and it was a great eye opener for all of us. We even made an appointment to come over to their New York office to have a look around. Expect a post about this on the &lt;a href="http://80beans.com"&gt;80beans&lt;/a&gt; blog soon.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve had a great time this year and I hope to be able to come next year again. We&amp;#8217;re in New York for the last part of the 80beans US trip right now (&lt;a href="http://www.flickr.com/photos/piechnik/sets/72157624235560242/"&gt;awesome pictures by Antek&lt;/a&gt;), but we&amp;#8217;ll be back home on Wednesday.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;ve sent me a question or pull request: sorry for the wait, I&amp;#8217;ll get back to you after I come back.&lt;/p&gt;
&lt;p&gt;Oh, and if you&amp;#8217;re ever in Baltimore, be sure to visit &lt;a href="http://www.abbeyburgerbistro.com/"&gt;the Abbey Burger Bistro&lt;/a&gt; for awesome burgers, beer and free wifi. Just sayin&amp;#8217;.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/cnJ9y9TBiU8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/railsconf-2010</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Daemonizing Navvy with God</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/ETt72z83pyQ/daemonizing-navvy-with-god" />
    
    <id>http://jeffkreeftmeijer.com/2010/daemonizing-navvy-with-god</id>
    <updated>2010-05-31T00:00:00+02:00</updated>
    <summary>Let's stop using the included daemon script and actually monitor our background process, shall we?</summary>
    <content type="html">
      
        &lt;p&gt;In &lt;a href="http://github.com/jeffkreeftmeijer/navvy/tree/v0.2.1"&gt;version 0.2.1&lt;/a&gt;,  &lt;a href="http://jeffkreeftmeijer.com/navvy"&gt;Navvy&lt;/a&gt; introduced a &lt;em&gt;really&lt;/em&gt; simple &lt;a href="http://daemons.rubyforge.org/"&gt;Daemon&lt;/a&gt; script to allow users to run Navvy&amp;#8217;s worker in the background.&lt;/p&gt;
&lt;p&gt;While some people asked for this, you might argue that running &lt;code&gt;rake navvy:work &amp;amp;&lt;/code&gt; would do the exact same thing. It runs the worker, like the rake task does. It only does it in the background, like rake&amp;#8217;s &lt;code&gt;&amp;amp;&lt;/code&gt; argument does.&lt;/p&gt;
&lt;p&gt;To try and get some more control over my Worker process, I decided to let &lt;a href="http://god.rubyforge.org/"&gt;God&lt;/a&gt; daemonize it. This allowed me to monitor my process instead of just starting it and hoping it would do it&amp;#8217;s thing. Also, God allows you to set memory limits and automatically restart &amp;#8212; and notify me &amp;#8212; when the worker dies.&lt;/p&gt;
&lt;h3&gt;God config&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;m assuming you put this block in a &lt;code&gt;Rakefile&lt;/code&gt; somewhere to load Navvy&amp;#8217;s tasks:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;navvy/tasks&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;LoadError&lt;/span&gt;
  &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:navvy&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;abort&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Couldn&amp;#39;t find Navvy. &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; 
      &lt;span class="s2"&gt;&amp;quot;Please run `gem install navvy` to use Navvy&amp;#39;s tasks.&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now, we&amp;#8217;ll create a God configuration file called &lt;code&gt;navvy.god&lt;/code&gt; in the same directory as our &lt;code&gt;Rakefile&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;God&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;watch&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;navvy&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="c1"&gt;# default&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;rake navvy:work&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;log/navvy.log&amp;quot;&lt;/span&gt;

  &lt;span class="c1"&gt;# determine the state on startup&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:init&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:up&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:start&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:process_running&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# determine when process has finished starting&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:up&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:process_running&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  
    &lt;span class="c1"&gt;# failsafe&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:tries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:start&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# start if process is not running&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:up&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:process_running&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This config will start a process called &lt;code&gt;navvy&lt;/code&gt;. It&amp;#8217;ll check if the process is still running every 30 seconds and restart it if it isn&amp;#8217;t.&lt;/p&gt;
&lt;p&gt;Now, you can start the worker by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ god -c navvy.god&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the worker&amp;#8217;s status:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ god status&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or stop it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ god stop navvy&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cool, huh?&lt;/p&gt;
&lt;h3&gt;What about Navvy&amp;#8217;s daemon script?&lt;/h3&gt;
&lt;p&gt;What I&amp;#8217;ve shown you does exactly what Navvy&amp;#8217;s included daemon script does, but keeps monitoring it. If you want more &amp;#8212; like email notifications and memory limits &amp;#8212; check out &lt;a href="http://god.rubyforge.org/"&gt;God&amp;#8217;s website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m thinking about completely removing the daemon script from Navvy in the next version and encouraging users to start using some kind of process monitor, since I believe handling background processes is none of Navvy&amp;#8217;s business. Navvy is a job processor. I think we should focus on that.&lt;/p&gt;
&lt;p&gt;What do you think? Your opinion is always welcome.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/ETt72z83pyQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/daemonizing-navvy-with-god</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Rise of the Navvy</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/rqfXlpoI-7w/rise-of-the-navvy" />
    
    <id>http://jeffkreeftmeijer.com/2010/rise-of-the-navvy</id>
    <updated>2010-05-24T00:00:00+02:00</updated>
    <summary>The what, why and how of the database agnostic Ruby background job processor.</summary>
    <content type="html">
      
        &lt;p&gt;It all started on a cold winter&amp;#8217;s day in December of 2009 as I decided to start a personal project using &lt;a href="http://mongomapper.com"&gt;MongoMapper&lt;/a&gt;, which needed to run background jobs.&lt;/p&gt;
&lt;p&gt;Back then, &lt;a href="http://twitter.com/collectiveidea" title="Collective Idea"&gt;@collectiveidea&lt;/a&gt;&amp;#8216;s &lt;a href="http://github.com/collectiveidea/delayed_job"&gt;delayed_job fork&lt;/a&gt; was still tightly mingled with &lt;a href="http://api.rubyonrails.org/classes/ActiveRecord/Base.html"&gt;ActiveRecord&lt;/a&gt;, so I couldn&amp;#8217;t just pop that in as I normally would. After failing to find good alternatives I stumbled upon &lt;a href="http://twitter.com/zbelzer" title="Zach Belzer"&gt;@zbelzer&lt;/a&gt;&amp;#8217;s &lt;a href="http://github.com/zbelzer/delayed_job"&gt;fork&lt;/a&gt; in which he did an &lt;a href="http://github.com/zbelzer/delayed_job/commit/1ad8eab91786f262f4d4bfeab0a76c1517c06e7e"&gt;&amp;ldquo;initial attempt at MongoDB support&amp;rdquo;&lt;/a&gt;. I decided to go with that.&lt;/p&gt;
&lt;p&gt;I found a couple of problems with Zach&amp;#8217;s fork &amp;#8212; it was based on &lt;a href="http://twitter.com/tobi" title="Tobias Lütke"&gt;@tobi&lt;/a&gt;&amp;#8216;s &lt;a href="http://github.com/tobi/delayed_job"&gt;original delayed_job&lt;/a&gt; to name one &amp;#8212; , so I created a Mongo based fork based on Zach&amp;#8217;s work myself.&lt;/p&gt;
&lt;p&gt;While working on that, it didn&amp;#8217;t take me long to realize that I was rewriting stuff I&amp;#8217;d never really need to use. So, I decided to create a simple, database agnostic background job processor myself and I called it &lt;a href="http://jeffkreeftmeijer.com/navvy"&gt;Navvy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;object width="450" height="360"&gt;&lt;br /&gt;
  &lt;param name="movie" value="http://www.youtube.com/v/5CU7oAS2XkY&amp;hl=en_US&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;br /&gt;
  &lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;br /&gt;
  &lt;embed src="http://www.youtube.com/v/5CU7oAS2XkY&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="450" height="260"&gt;&lt;/embed&gt;&lt;br /&gt;
&lt;/object&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Navvy is a shorter form of navigator (UK) or navigational engineer (&lt;span class="caps"&gt;USA&lt;/span&gt;) and is particularly applied to describe the manual labourers working on major civil engineering projects.&lt;br /&gt;
&amp;#8212; &lt;a href="http://en.wikipedia.org/wiki/Navvy"&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I initially built Navvy with MongoMapper and ActiveRecord adapters, but &lt;a href="http://twitter.com/antekpiechnik" title="Antek Piechnik"&gt;@antekpiechnik&lt;/a&gt; came out of nowhere and created an adapter for &lt;a href="http://sequel.rubyforge.org/"&gt;Sequel&lt;/a&gt;. In the spirit of the &lt;a href="http://railsbridge.org/news_items/10"&gt;January 16 and 17 Bugmash&lt;/a&gt;, &lt;a href="http://twitter.com/matsimitsu" title="Robert Beekman"&gt;@matsimitsu&lt;/a&gt; and I jumped in to get the whole thing up to speed with Rails 3.&lt;/p&gt;
&lt;h3&gt;&amp;ldquo;Aren&amp;#8217;t you the guy who built Navvy?&amp;rdquo;&lt;/h3&gt;
&lt;p&gt;After &lt;a href="http://www.rubyflow.com/items/3280"&gt;announcing&lt;/a&gt; it, it didn&amp;#8217;t take long before &lt;a href="http://ruby5.envylabs.com/episodes/45-episode-43-january-19-2010/stories/350-navvy-database-agnostic-background-jobs"&gt;Ruby5&lt;/a&gt; picked it up and people started actually using it. It was great to hear that &lt;a href="http://twitter.com/jnunemaker/status/7996463096" title="John Nunemaker"&gt;@jnunemaker&lt;/a&gt; decided to use Navvy in &lt;a href="http://harmonyapp.com"&gt;Harmony&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://thechangelog.com/post/356754061/navvy-simple-database-agnostic-ruby-background-job-proce"&gt;The Changelog&lt;/a&gt; did a post on Navvy, which meant it became a featured repo on &lt;a href="http://github.com/explore"&gt;Github Explore&lt;/a&gt; for about a week, John gave another hat tip on &lt;a href="http://thechangelog.com/post/360070705/episode-0-1-1-john-nunemaker-from-mongomapper"&gt;Episode 0.1.1 of the Changelog&lt;/a&gt; and more and more people started &lt;a href="http://wiki.github.com/jeffkreeftmeijer/navvy/contributors"&gt;contributing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks to everyone who helped build, fix bugs and get the word out about the project. It was a crazy ride and I can never get enough of the &amp;ldquo;Aren&amp;#8217;t you the guy who built Navvy?&amp;rdquo;-question. Feels good, thanks.&lt;/p&gt;
&lt;h3&gt;Future&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://thechangelog.com/post/495099940/delayed-job-hits-2-0"&gt;delayed_job 2&lt;/a&gt; got released a while back adding support for multiple backends. Although database agnosticism was the first and most important goal for Navvy, I think there&amp;#8217;s still a place for both, since there are still some cool things in Navvy (You can &lt;a href="http://wiki.github.com/jeffkreeftmeijer/navvy/installation-usage-in-pure-ruby"&gt;use it in pure Ruby&lt;/a&gt; for example).&lt;/p&gt;
&lt;p&gt;Today, Navvy has a &lt;a href="http://datamapper.org/"&gt;DataMapper&lt;/a&gt; adapter, job priorities, the ability to retry failed jobs and it&amp;#8217;s own daemon. It&amp;#8217;s been a bit quiet around Navvy for the last couple of weeks, but I&amp;#8217;ve been working on a &lt;a href="http://github.com/jeffkreeftmeijer/navvy/commits/feature/mongoid"&gt;Mongoid adapter&lt;/a&gt; with &lt;a href="http://twitter.com/tomk32" title="Thomas R. Koll"&gt;@TomK32&lt;/a&gt;, together with some other stuff that I hope to release soon.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m excited to continue working on Navvy. Stay tuned!&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/rqfXlpoI-7w" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/rise-of-the-navvy</feedburner:origLink></entry>
  
  <entry>
    <title type="html">I Bugmashed</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/rMGTwRTvHeE/i-bugmashed" />
    
    <id>http://jeffkreeftmeijer.com/2010/i-bugmashed</id>
    <updated>2010-05-19T00:00:00+02:00</updated>
    <summary>My experiences in the May 15 &amp; 16 Rails 3 Bugmash.</summary>
    <content type="html">
      
        &lt;p&gt;I participated in the &lt;a href="http://railsbridge.org/news_items/12"&gt;May 15 &amp;amp; 16 Rails 3 Bugmash&lt;/a&gt; last weekend. This was my first Bugmash (if you don&amp;#8217;t count getting &lt;a href="http://jeffkreeftmeijer.com/navvy"&gt;Navvy&lt;/a&gt; up to speed with Rails 3 in &lt;a href="http://railsbridge.org/news_items/11"&gt;the last one&lt;/a&gt;). I had lots of fun, resolved two tickets that applied to the Rails &lt;code&gt;master&lt;/code&gt; and the &lt;code&gt;2-3-stable&lt;/code&gt; branch, reviewed some patches by others, got five commits into Rails, &lt;a href="http://bugmash.com/"&gt;finished in second place&lt;/a&gt;,  and got on &lt;a href="http://contributors.rubyonrails.org/contributors/jeff-kreeftmeijer/commits"&gt;the contributor list&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/flytrap.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;All Saturday (I couldn&amp;#8217;t make it on Sunday), experienced Rails contributors and core team members were available for questions in the #railsbridge &lt;span class="caps"&gt;IRC&lt;/span&gt; channel. Everybody was very helpful and friendly, which  made it a lot easier to dive in and debug in the Rails source.&lt;/p&gt;
&lt;p&gt;Something that also made everything a little easier was &lt;a href="http://github.com/jeroenvandijk/rails_test_box"&gt;rails_test_box&lt;/a&gt;. It uses &lt;a href="http://virtualbox.org"&gt;VirtualBox&lt;/a&gt; and &lt;a href="http://vagrantup.com/"&gt;Vagrant&lt;/a&gt; to set up an isolated testing environment so you can run Rails&amp;#8217; tests without worrying about messing up your development environment.&lt;/p&gt;
&lt;p&gt;It installs &lt;a href="http://www.mysql.com/"&gt;MySQL&lt;/a&gt; and &lt;a href="http://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt; for you, together with &lt;a href="http://rvm.beginrescueend.com/"&gt;&lt;span class="caps"&gt;RVM&lt;/span&gt;&lt;/a&gt;, so you can test on multiple databases and in multiple Rubies. And yes, it&amp;#8217;ll even clone Rails in a shared directory, so you can develop on your Mac like you&amp;#8217;re used to. Also, be sure to read &lt;a href="http://blog.cookiestack.com/post/571168451/testing-rails-for-the-masses"&gt;Jeroen&amp;#8217;s article on the Cookiestack blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve learned a lot during the Bugmash and I highly recommend every Rails developer to give it a shot next time. Thanks &lt;a href="http://railsbridge.org/"&gt;RailsBridge&lt;/a&gt;, &lt;a href="http://twitter.com/josevalim"&gt;@josevalim&lt;/a&gt;, &lt;a href="http://twitter.com/spastorino"&gt;@spastorino&lt;/a&gt;, &lt;a href="http://twitter.com/rizwanreza"&gt;@rizwanreza&lt;/a&gt;, &lt;a href="http://github.com/jeroenvandijk"&gt;jeroenvandijk&lt;/a&gt; and everyone else who participated and helped out. You&amp;#8217;re awesome.&lt;/p&gt;
&lt;h3&gt;Now what?&lt;/h3&gt;
&lt;p&gt;Like &lt;a href="http://railsbridge.org/news_items/14"&gt;the Railsbridge post&lt;/a&gt; stated: this Bugmash was the &amp;ldquo;most effective yet&amp;rdquo;. That&amp;#8217;s great, but we&amp;#8217;re not done yet. If you&amp;#8217;ve participated in the Bugmash, you know that the &lt;a href="https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/bins/41908"&gt;Bugmash tag in the Rails issue tracker&lt;/a&gt; still has over 100 unresolved tickets.&lt;/p&gt;
&lt;p&gt;I suggest we keep going and spend some of our time to &lt;a href="http://www.enlightsolutions.com/articles/pledge-3-percent-for-rails-3/"&gt;keep giving back&lt;/a&gt;, like fixing a small bug, writing some tests or reviewing a patch. Who&amp;#8217;s with me?&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/rMGTwRTvHeE" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/i-bugmashed</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Setting up MongoDB, MongoMapper, GridFS, Devise &amp; Carrierwave on OSX</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/2vL-zkBRMpc/setting-up-mongodb-mongomapper-gridfs-devise-carrierwave-on-osx" />
    
    <id>http://jeffkreeftmeijer.com/2010/setting-up-mongodb-mongomapper-gridfs-devise-carrierwave-on-osx</id>
    <updated>2010-05-17T00:00:00+02:00</updated>
    <summary>This would have saved me a lot of trouble.</summary>
    <content type="html">
      
        
          &lt;p&gt;This is a guest post written by &lt;a href="http://twitter.com/matsimitsu" title="Robert Beekman"&gt;@matsimitsu&lt;/a&gt;. Robert is a Ruby programmer at &lt;a href="http://80beans.com"&gt;80beans&lt;/a&gt; and a serious &lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; fanatic.
        &lt;div class="notice"&gt;
Sorry, this article is a bit outdated. Lucky for you, &lt;a href="http://twitter.com/antekpiechnik"&gt;@antekpiechnik&lt;/a&gt; wrote &lt;a href="http://antekpiechnik.com/posts/setting-up-carrierwave-file-uploads-using-gridfs-on-rails-3-and-mongoid"&gt;a really nice guide&lt;/a&gt; in which he uses Mongoid and Rails 3. Be sure to check that out too!
&lt;/div&gt;
&lt;p&gt;For a little side project I wanted to experiment with &lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; and &lt;a href="http://www.mongodb.org/display/DOCS/GridFS"&gt;GridFS&lt;/a&gt; on Rails 2.3.5, but it took me a while to get everything up and running.&lt;/p&gt;
&lt;p&gt;It wasn&amp;#8217;t really a problem with Mongrel, but as soon as Apache and &lt;a href="http://www.modrails.com/"&gt;Passenger&lt;/a&gt; came in the picture the problems began.&lt;/p&gt;
&lt;h3&gt;The gems&lt;/h3&gt;
&lt;p&gt;Development setup:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;carrierwave&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;devise&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mongo_mapper&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:version&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0.7.0&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mongomapper_ext&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;will_paginate&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you want to use GridFS with MongoDB, make sure you use &lt;a href="http://github.com/jnunemaker/mongomapper"&gt;MongoMapper&lt;/a&gt; 0.7.0, because later versions cause:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mongo/GridFS not found&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Test setup:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rspec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:lib&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;spec&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rspec-rails&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:lib&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;spec/rails&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;machinist_mongo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:version&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;1.1.0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:lib&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;machinist/mongo_mapper&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;faker&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;steak&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;capybara&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;MongoMapper&lt;/h3&gt;
&lt;p&gt;To use &lt;a href="http://github.com/jnunemaker/mongomapper"&gt;MongoMapper&lt;/a&gt; add the following to your &lt;code&gt;config/initializers&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# config/initializers/mongo_mapper.rb&lt;/span&gt;

&lt;span class="c1"&gt;# load YAML and connect&lt;/span&gt;
&lt;span class="n"&gt;database_yaml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;YAML&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RAILS_ROOT&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/config/database.yml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;database_yaml&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;database_yaml&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;adapter&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;MongoDB&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;mongo_database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;database_yaml&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="no"&gt;MongoMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Mongo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mongo_database&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;host&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27017&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:pool_size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:timeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;MongoMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="n"&gt;mongo_database&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;database&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;defined?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PhusionPassenger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="no"&gt;PhusionPassenger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:starting_worker_process&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;forked&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
     &lt;span class="no"&gt;MongoMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect_to_master&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;forked&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;PusionPassenger&lt;/code&gt; section can be removed if you are not planning to run this on Apache and Passenger.&lt;/p&gt;
&lt;p&gt;If you &lt;em&gt;do&lt;/em&gt; plan to run it, make sure this is in your config. It will make sure new spawned Apache children still have a MongoDB database connection and prevent you from pulling out all your hair.&lt;/p&gt;
&lt;p&gt;You can now setup your &lt;code&gt;config/database.yml&lt;/code&gt; config. Example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# config/database.yml&lt;/span&gt;

&lt;span class="n"&gt;development&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mongodb&lt;/span&gt;
  &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mmyapp_development&lt;/span&gt;
  &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;localhost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Devise&lt;/h3&gt;
&lt;p&gt;Setting up &lt;a href="http://github.com/plataformatec/devise"&gt;Devise&lt;/a&gt; is pretty easy, just use the following snippet:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# config/initializers/devise.rb&lt;/span&gt;

&lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mailer_sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;info@example.com&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:mongo_mapper&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And use it in your (user) model:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;MongoMapper&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Document&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;MongoMapperExt&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Slugizer&lt;/span&gt;

  &lt;span class="n"&gt;devise&lt;/span&gt; &lt;span class="ss"&gt;:registerable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:database_authenticatable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:recoverable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:rememberable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:trackable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:validatable&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Since we are on MongDB you can skip the migrations!&lt;/p&gt;
&lt;h3&gt;Carrierwave&lt;/h3&gt;
&lt;p&gt;Next up is image uploading, I used &lt;a href="http://github.com/jnicklas/carrierwave"&gt;Carrierwave&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Again some configuration, this time make sure that the &lt;code&gt;application_#{Rails.env}&lt;/code&gt; is consistent with your &lt;code&gt;database.yml&lt;/code&gt; file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# config/initializers/carrierwave.rb&lt;/span&gt;

&lt;span class="no"&gt;CarrierWave&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grid_fs_database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;application_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grid_fs_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grid_fs_access_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/GridFS&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:grid_fs&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With Carrierwave configured we can now implement it in our model.&lt;/p&gt;
&lt;p&gt;An example is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Photo&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;carrierwave/orm/mongomapper&amp;#39;&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;MongoMapper&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Document&lt;/span&gt;

  &lt;span class="n"&gt;mount_uploader&lt;/span&gt; &lt;span class="ss"&gt;:image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ImageUploader&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note the &lt;code&gt;mount_uploader&lt;/code&gt;. This will tell Carrierwave to look for a file called &lt;code&gt;image_uploader&lt;/code&gt; in &lt;code&gt;/app/uploaders/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That file should contain some of the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# app/uploaders/image_uploader.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImageUploader&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;CarrierWave&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Uploader&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;CarrierWave&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ImageScience&lt;/span&gt;

  &lt;span class="n"&gt;storage&lt;/span&gt; &lt;span class="ss"&gt;:grid_fs&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;store_dir&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;files/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extension_white_list&lt;/span&gt;
    &lt;span class="sx"&gt;%w(jpg jpeg gif png)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="ss"&gt;:small_thumb&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="ss"&gt;:resize_to_fill&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;A couple of things are important here, first the &lt;code&gt;storage :grid_fs&lt;/code&gt;. It will tell Carrierwave to store the files in GridFS.&lt;/p&gt;
&lt;p&gt;Next is the &lt;code&gt;def store_dir&lt;/code&gt;. This is used to generate the path to the images.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/GridFS/files/[ID]/[filename].[ext]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the path is prefixed with &lt;code&gt;/GridFS&lt;/code&gt;. This is the value we have set in the config of Carrierwave.&lt;/p&gt;
&lt;p&gt;To serve out the files from Rails we are using a &lt;a href="http://gist.github.com/264077"&gt;GridFS metal&lt;/a&gt;. This will increase the speed of image serving.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# app/metal/grid_fs.rb&lt;/span&gt;

&lt;span class="c1"&gt;# Rails metal to be used with Carrierwave (GridFS) and MongoMapper&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mongo&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mongo/GridFS&amp;#39;&lt;/span&gt;

&lt;span class="c1"&gt;# Allow the metal piece to run in isolation&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/../../config/environment&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;defined?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GridData&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Metal&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PATH_INFO&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^\/GridFS\/(.+)$/&lt;/span&gt;
      &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$1&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;GridFS&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;GridStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MongoMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;GridFS&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;GridStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MongoMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
          &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;File not found.&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;File not found.&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;An important thing to note is the regex part:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if env["PATH_INFO"] =~ /^\/GridFS\/(.+)$/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The GridFS in the regex is the same as in your Carrierwave config!&lt;/p&gt;
&lt;h3&gt;&lt;span class="caps"&gt;OSX&lt;/span&gt; and open files&lt;/h3&gt;
&lt;p&gt;At some point when I began stress testing MongoDB by requesting a &lt;em&gt;lot&lt;/em&gt; of files from GridFS. MongoDB would become unresponsive and eventually crash the Rails application.&lt;/p&gt;
&lt;p&gt;After a deep dive into the logs I found out that MongoDB has a lot of files open. More then the 256 that &lt;span class="caps"&gt;OSX&lt;/span&gt; wil allow by default.&lt;/p&gt;
&lt;p&gt;To fix this use the &lt;a href="http://serverfault.com/questions/15564/where-are-the-default-ulimits-specified-on-os-x-10-5"&gt;following from serverfault&lt;/a&gt;, it will basically set the open files limit a lot higher for &lt;span class="caps"&gt;OSX&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;To change any of these limits, add a line (you may need to create the file first) to &lt;code&gt;/etc/launchd.conf&lt;/code&gt;, the arguments are the same as passed to the &lt;code&gt;launchctl&lt;/code&gt; command. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo echo "limit maxfiles 1024 unlimited" &amp;gt; /etc/launchd.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However &lt;code&gt;launchd&lt;/code&gt; has already started your login shell, so the simplest way to make these changes take effect is to restart our machine.&lt;/p&gt;
&lt;h3&gt;That&amp;#8217;s it!&lt;/h3&gt;
&lt;p&gt;Well that&amp;#8217;s it! you should now have a working Rails 2.3.5 stack with MongoMapper, files served from GridFS, authentication from Devise and image uploading with Carrierwave.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/2vL-zkBRMpc" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/setting-up-mongodb-mongomapper-gridfs-devise-carrierwave-on-osx</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Testing your Machinist blueprints</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/0AUG9Mjuo8I/testing-your-machinist-blueprints" />
    
    <id>http://jeffkreeftmeijer.com/2010/testing-your-machinist-blueprints</id>
    <updated>2010-05-10T00:00:00+02:00</updated>
    <summary>A little spec I wrote to ensure my blueprints stay in shape.</summary>
    <content type="html">
      
        &lt;p&gt;At &lt;a href="http://80beans.com"&gt;80beans&lt;/a&gt; we had an old Rails project still on fixtures, so we decided to start switching it over to blueprints using &lt;a href="http://github.com/notahat/machinist"&gt;Machinist&lt;/a&gt;. Nothing extreme, except that the project had over 60 models and a couple of fixtures were broken, fixed, broken and fixed again.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/blueprint.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;As I started writing blueprints, I realized that it would be nice to have some specs to make sure the bare minimum  (&lt;code&gt;Model.make&lt;/code&gt; without any arguments) kept working at all times and we didn&amp;#8217;t have to dive in to realize that the problem was one of the new blueprints, not the application.&lt;/p&gt;
&lt;p&gt;So I whipped up &amp;#8212; and &lt;a href="http://gist.github.com/391949"&gt;Gisted&lt;/a&gt; &amp;#8212;  &lt;code&gt;machinist_spec.rb&lt;/code&gt; that goes through your &lt;code&gt;app/models&lt;/code&gt; directory, runs &lt;code&gt;.make&lt;/code&gt; for everything it finds and expects it to work. Simple.&lt;/p&gt;
&lt;p&gt;Also, our application used a mailer &amp;#8212; in the &lt;code&gt;models&lt;/code&gt; directory &amp;#8212; which, of course, didn&amp;#8217;t need any blueprints. I&amp;#8217;ve excluded it from the spec by extracting it from the array on line 5.&lt;/p&gt;
&lt;p&gt;Want it? Just put &lt;a href="http://gist.github.com/391949"&gt;this&lt;/a&gt; into &lt;code&gt;spec/machinist_spec.rb&lt;/code&gt;. If you have anything to add or improve, please fork.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/0AUG9Mjuo8I" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/testing-your-machinist-blueprints</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Steak: because Cucumber is for vegetarians!</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/uzsntyNGzso/steak-because-cucumber-is-for-vegetarians" />
    
    <id>http://jeffkreeftmeijer.com/2010/steak-because-cucumber-is-for-vegetarians</id>
    <updated>2010-05-03T00:00:00+02:00</updated>
    <summary>Minimalist acceptance testing using pure Ruby with Rspec, Steak and Webrat or Capybara.</summary>
    <content type="html">
      
        &lt;div class="notice"&gt;Capybara has an RSpec &lt;span class="caps"&gt;DSL&lt;/span&gt; now, allowing you to steak-acceptance testing without Steak. Be sure to read &lt;a href="http://jeffkreeftmeijer.com/2011/acceptance-testing-using-capybaras-new-rspec-dsl/"&gt;Acceptance testing using Capybara&amp;#8217;s new RSpec &lt;span class="caps"&gt;DSL&lt;/span&gt;&lt;/a&gt; too.&lt;/div&gt;
&lt;p&gt;I&amp;#8217;m not going to tell you &lt;a href="http://robots.thoughtbot.com/post/543043259/unit-and-functional-tests-are-as-useful-as-100-code"&gt;why you should write acceptance (or integration) tests&lt;/a&gt;, but you should. I used &lt;a href="http://cukes.info/"&gt;Cucumber&lt;/a&gt; for a while now and I love it, but I think writing my tests in a &lt;a href="http://www.martinfowler.com/bliki/BusinessReadableDSL.html"&gt;business-readable domain-specific language&lt;/a&gt; and translating them into Ruby using step definitions is a bit too much sometimes. And I&amp;#8217;m not a vegetarian.&lt;/p&gt;
&lt;p&gt;Luckily, we have &lt;a href="http://github.com/cavalle/steak"&gt;Steak&lt;/a&gt; by &lt;a href="http://twitter.com/cavalle" title="Luismi Cavallé"&gt;@cavalle&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/steak.jpg"/&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Steak is like Cucumber but in plain Ruby. No explicit givens, whens or thens. No steps, no English, just Ruby: RSpec and Steak. That&#x2019;s all.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Right now the stable version of Steak only works with Rails 2.x and &lt;a href="http://rspec.info/"&gt;RSpec&lt;/a&gt; 1.x, but there&amp;#8217;s a &lt;a href="http://github.com/cavalle/steak/tree/rails3"&gt;Rails 3 branch&lt;/a&gt; where some work is being done to support Rails 3.x and RSpec 2.&amp;#215;. I&amp;#8217;ve been using that one, but this article should also be fine for you when you&amp;#8217;re on Rails 2.&amp;#215;.&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;OK, throw this into your &lt;code&gt;Gemfile&lt;/code&gt; (I&amp;#8217;m specifying the &lt;code&gt;0.4.0.a4&lt;/code&gt; release here, that&amp;#8217;s the most recent alpha release from the &lt;code&gt;rails3&lt;/code&gt; branch). Also, I&amp;#8217;ll be using &lt;a href="http://github.com/jnicklas/capybara"&gt;Capybara&lt;/a&gt; for this example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;steak&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0.4.0.a4&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;capybara&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And install your bundle:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ bundle install&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or &#x2014; if you&#x2019;re using Rails 2.x &#x2014; put this in &lt;code&gt;config/environment.rb&lt;/code&gt; (Just using the stable Rails 2 release of Steak here):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;steak&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;capybara&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rake gems:install RAILS_ENV=test&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can use the Steak generators to set everything up. You have to specify which driver you&amp;#8217;re using (Steak also has a generator for &lt;a href="http://github.com/brynary/webrat"&gt;Webrat&lt;/a&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rails generate steak --capybara # or -- webrat&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Please remember to use &lt;code&gt;script/generate&lt;/code&gt; instead of &lt;code&gt;rails generate&lt;/code&gt; when on Rails 2.&amp;#215;.&lt;/p&gt;
&lt;p&gt;The generator created the &lt;code&gt;spec/acceptance&lt;/code&gt; directory in your project, in which you can find &lt;code&gt;acceptance_helper.rb&lt;/code&gt; and the &lt;code&gt;support&lt;/code&gt; directory. The &lt;code&gt;support&lt;/code&gt; directory holds &lt;code&gt;paths.rb&lt;/code&gt; &amp;#8212; which should be familiar if you&amp;#8217;ve used Cucumber before &amp;#8212; and &lt;code&gt;helpers.rb&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Acceptance specs&lt;/h3&gt;
&lt;p&gt;Now, let&amp;#8217;s write our first acceptance spec &amp;#8212; no, we&amp;#8217;re not calling it a &amp;ldquo;feature&amp;rdquo;. It&amp;#8217;s a spec. &amp;#8212; by using another generator (I&amp;#8217;m creating an articles spec here):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rails generate acceptance_spec articles&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Steak generated &lt;code&gt;spec/acceptance/articles_spec.rb&lt;/code&gt;, which looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/acceptance_helper&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;feature&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Feature name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%q{&lt;/span&gt;
&lt;span class="sx"&gt;  In order to ...&lt;/span&gt;
&lt;span class="sx"&gt;  As a ...&lt;/span&gt;
&lt;span class="sx"&gt;  I want to ...&lt;/span&gt;
&lt;span class="sx"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;scenario&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Scenario name&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you&amp;#8217;re a Cucumber user, the feature description should look familiar. I&amp;#8217;ll try to describe what I&amp;#8217;m doing like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;feature&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Articles&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%q{&lt;/span&gt;
&lt;span class="sx"&gt;  In order to have an awesome blog&lt;/span&gt;
&lt;span class="sx"&gt;  As an author&lt;/span&gt;
&lt;span class="sx"&gt;  I want to create and manage articles&lt;/span&gt;
&lt;span class="sx"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Great. On to our first scenario. In the articles index we want to show all articles in a list.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;scenario&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Article index&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;One&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Two&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;article_index&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;One&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Two&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;First, I create two articles called &amp;ldquo;One&amp;rdquo; and &amp;ldquo;Two&amp;rdquo; (please, use a tool like &lt;a href="http://github.com/notahat/machinist"&gt;Machinist&lt;/a&gt; for that). Then I visit &lt;code&gt;article_index&lt;/code&gt; and check if the article titles show up. Really simple.&lt;/p&gt;
&lt;p&gt;The last thing to do to get this thing running is to create the path for &lt;code&gt;article_index&lt;/code&gt; in &lt;code&gt;spec/acceptance/support/paths.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;NavigationHelpers&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;homepage&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;article_index&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;/articles&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Simple, right? I&amp;#8217;m not going to walk you through building and testing this application here right now, but I think you get the point.&lt;/p&gt;
&lt;h3&gt;Minimalist acceptance testing&lt;/h3&gt;
&lt;p&gt;Steak just provides some aliases (like &lt;code&gt;scenario&lt;/code&gt; and &lt;code&gt;feature&lt;/code&gt;) for RSpec&amp;#8217;s methods &amp;ldquo;providing you with the language of acceptance testing&amp;rdquo;. Here&amp;#8217;s a snippet from  &lt;a href="http://github.com/cavalle/steak/blob/master/lib/steak.rb"&gt;Steak&amp;#8217;s library&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Spec::Example::ExampleGroupMethods&lt;/span&gt;
  &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="n"&gt;scenario&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;
  &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Spec::DSL::Main&lt;/span&gt;
  &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="n"&gt;feature&lt;/span&gt; &lt;span class="n"&gt;describe&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;So, if you know your RSpec, the only thing you&amp;#8217;ll have to do is dive into &lt;a href="http://github.com/jnicklas/capybara"&gt;Capybara&lt;/a&gt; (or &lt;a href="http://github.com/brynary/webrat"&gt;Webrat&lt;/a&gt;) for a bit and you&amp;#8217;ll be writing acceptance specs in no time.&lt;/p&gt;
&lt;h3&gt;Running your spec&lt;/h3&gt;
&lt;p&gt;You might have guessed, but running your acceptance specs is exactly the same as running any other spec. After all, it&amp;#8217;s just a minimal layer on top of RSpec:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ spec spec/acceptance/articles_spec.rb&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Cucumber vs Steak?&lt;/h3&gt;
&lt;p&gt;Cucumber is highly focused on creating a business-readable &lt;span class="caps"&gt;DSL&lt;/span&gt;, and does so perfectly. I understand the thought behind making stakeholders able to read what&amp;#8217;s going on in their projects, but I wonder how many people actually do this. Also, one might argue that &amp;#8212; from a Ruby programmer&amp;#8217;s perspective &amp;#8212; Steak&amp;#8217;s specs are in fact more readable than Cucumber&amp;#8217;s.&lt;/p&gt;
&lt;p&gt;Steak takes away the extra step of translating everything from English to Ruby and is incredibly easy to learn (especially when you have some RSpec experience). It&amp;#8217;s completely incomparable to Cucumber though and which one to choose is simply a matter of taste. And I love me some Steak.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/uzsntyNGzso" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/steak-because-cucumber-is-for-vegetarians</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Making Machinist master MongoMapper &amp; Mongoid</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/NAT4F37GvuI/making-machinist-master-mongomapper-mongoid" />
    
    <id>http://jeffkreeftmeijer.com/2010/making-machinist-master-mongomapper-mongoid</id>
    <updated>2010-04-26T00:00:00+02:00</updated>
    <summary>Using machinist_mongo to use Machinist with mongoDB ORMs.</summary>
    <content type="html">
      
        &lt;p&gt;A couple of months back, I wrote a minimal &lt;a href="http://github.com/nmerouze/machinist_mongo/commit/dad976b321881657753d50d37bd151b268ab91bd"&gt;install guide&lt;/a&gt; for &lt;a href="http://github.com/nmerouze/machinist_mongo"&gt;machinist_mongo&lt;/a&gt; by &lt;a href="http://twitter.com/nmerouze"&gt;@nmerouze&lt;/a&gt; and &lt;a href="http://twitter.com/shingara"&gt;@shingara&lt;/a&gt;. Back then it was called machinist_mongomapper, it didn&amp;#8217;t support &lt;a href="http://mongoid.com"&gt;Mongoid&lt;/a&gt; yet and the Rails beta hadn&amp;#8217;t even been released yet, so I think it&amp;#8217;s about time for an update.&lt;/p&gt;
&lt;p&gt;You know &lt;a href="http://github.com/notahat/machinist"&gt;Machinist&lt;/a&gt;, right? Here&amp;#8217;s the short explanation:&lt;/p&gt;
&lt;blockquote&gt;&amp;ldquo;Fixtures aren&amp;#8217;t fun. Machinist is.&lt;br /&gt;
&lt;br /&gt;
Machinist makes it easy to create test data within your tests. It generates data for the fields you don&amp;#8217;t care about, and constructs any necessary associated objects, leaving you to only specify the fields you do care about in your tests.&amp;rdquo;&lt;/blockquote&gt;
&lt;p&gt;I&amp;#8217;m not going to go into Machinist itself here, so be sure to read the &lt;a href="http://github.com/notahat/machinist"&gt;&lt;span class="caps"&gt;README&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On to installing machinist_mongo. Put this in your &lt;code&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;machinist_mongo&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ bundle install&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or &#x2014; if you&#x2019;re using Rails 2.x &#x2014; put this in &lt;code&gt;config/environment.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;machinist_mongo&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rake gems:install&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now create your &lt;code&gt;spec/blueprints.rb&lt;/code&gt; or &lt;code&gt;test/blueprints.rb&lt;/code&gt; file as you normally would, only using &lt;a href="http://mongoid.com"&gt;Mongoid&lt;/a&gt; or &lt;a href="http://mongomapper.com"&gt;MongoMapper&lt;/a&gt; instead of ActiveRecord:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;machinist/mongo_mapper&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# or mongoid&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sham&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Don&amp;#8217;t forget to require the new &lt;code&gt;blueprints.rb&lt;/code&gt; file in your &lt;code&gt;spec_helper&lt;/code&gt; (or &lt;code&gt;test_helper&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/blueprints&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And you&amp;#8217;re all set. Again, be sure read &lt;a href="http://github.com/notahat/machinist"&gt;Machinist&amp;#8217;s &lt;span class="caps"&gt;README&lt;/span&gt;&lt;/a&gt; to find out how to create your objects and get this thing running.&lt;/p&gt;
&lt;p&gt;machinist_mongo aims to support &lt;em&gt;all&lt;/em&gt; &lt;a href="http://www.mongodb.org/"&gt;mongoDB&lt;/a&gt; ORMs, so If you&amp;#8217;re using something other than MongoMapper or Mongoid, please be sure to write a adapter for it. Also, can anyone tell me why this isn&amp;#8217;t merged into Machinist itself? Because I think it should.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/NAT4F37GvuI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/making-machinist-master-mongomapper-mongoid</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Authenticating via Github with Guestlist</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/3e_nmJG7pt0/authenticating-via-github-with-guestlist" />
    
    <id>http://jeffkreeftmeijer.com/2010/authenticating-via-github-with-guestlist</id>
    <updated>2010-04-19T00:00:00+02:00</updated>
    <summary>Introducing Guestlist: a Rubygem for user authentication using Github credentials.</summary>
    <content type="html">
      
        &lt;p&gt;In last week&amp;#8217;s article &amp;#8212; &lt;a href="http://jeffkreeftmeijer.com/2010/simple-authentication-with-warden"&gt;&amp;ldquo;Simple authentication with Warden&amp;rdquo;&lt;/a&gt; &amp;#8212; I explained that I wanted to allow users to log in with their &lt;a href="http://github.com"&gt;Github&lt;/a&gt; credentials in a little application I&amp;#8217;m working on.&lt;/p&gt;
&lt;p&gt;The idea is that everyone with a Github account can simply log into the application by entering their  username and token and we&amp;#8217;ll automatically create an account with information fetched from the &lt;a href="http://develop.github.com"&gt;Github &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt;. If the user already exists in our database, we won&amp;#8217;t try to fetch anything. We already stored it.&lt;/p&gt;
&lt;p&gt;I started working on it and realized that this may also come in handy for somebody else sometime, so  I named it &lt;a href="http://github.com/jeffkreeftmeijer/guestlist"&gt;Guestlist&lt;/a&gt; and released it.&lt;/p&gt;
&lt;p&gt;The installation is really simple, just throw this in your &lt;code&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;guestlist&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ bundle install&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or &amp;#8212; if you&amp;#8217;re using Rails 2.x &amp;#8212; put this in &lt;code&gt;config/environment.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;guestlist&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rake gems:install&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Include &lt;code&gt;Guestlist&lt;/code&gt; in your user model (mine is called &lt;code&gt;User&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="c1"&gt;# It works with ActiveRecord too. ;)&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;MongoMapper&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Document&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Guestlist&lt;/span&gt;

  &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ss"&gt;:login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ss"&gt;:encrypted_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Make sure you have both &lt;code&gt;login&lt;/code&gt; and &lt;code&gt;encrypted_token&lt;/code&gt; fields in your database if you&amp;#8217;re using ActiveRecord (&lt;a href="http://gist.github.com/369610"&gt;here&amp;#8217;s a migration&lt;/a&gt;), or if you&amp;#8217;re using  &lt;a href="http://mongomapper.com"&gt;MongoMapper&lt;/a&gt;, add some &lt;code&gt;key&lt;/code&gt;s to your model like in the example above.&lt;/p&gt;
&lt;p&gt;Believe it or not, you&amp;#8217;re all set. Let&amp;#8217;s try it (you can find your token on the &lt;a href="https://github.com/account"&gt;Github account page&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authenticate&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;github_login&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;github_token&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Guestlist will automatically create a new &lt;code&gt;User&lt;/code&gt; for you and return that, or false if you&amp;#8217;ve entered a wrong login or token. The user&amp;#8217;s token is stored &amp;#8212; yes, encrypted &amp;#8212; , so next time you log in, it&amp;#8217;ll just authenticate you via the database. If the token changes for some strange reason, Guestlist will update your user record when you log in with your new token.&lt;/p&gt;
&lt;p&gt;Also, it works great with &lt;a href="http://jeffkreeftmeijer.com/2010/simple-authentication-with-warden"&gt;Warden&lt;/a&gt;. Here&amp;#8217;s a strategy you can use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate!&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:login&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:token&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nil?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;fail&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Couldn&amp;#39;t log in&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;success!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Have fun and don&amp;#8217;t forget to &lt;a href="http://github.com/jeffkreeftmeijer/guestlist/issues"&gt;submit your issues&lt;/a&gt; or &lt;a href="http://github.com/jeffkreeftmeijer/guestlist"&gt;fork the project&lt;/a&gt; if you have a great idea.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/3e_nmJG7pt0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/authenticating-via-github-with-guestlist</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Simple authentication with Warden</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/vOWhraAbrfI/simple-authentication-with-warden" />
    
    <id>http://jeffkreeftmeijer.com/2010/simple-authentication-with-warden</id>
    <updated>2010-04-13T00:00:00+02:00</updated>
    <summary>No registration or confirmation. Authentication middleware that gets the job done.</summary>
    <content type="html">
      
        &lt;p&gt;There are a lot of Ruby authentication libraries out there, which can do about everything like sending confirmation emails and resetting passwords. I didn&amp;#8217;t really want that. My plan was to write a little application that could authenticate using &lt;a href="http://github.com"&gt;Github&lt;/a&gt; credentials (more on Github authentication in &lt;a href="http://jeffkreeftmeijer.com/2010/authenticating-via-github-with-guestlist"&gt;&amp;ldquo;Authenticating via Github with Guestlist&amp;rdquo;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;This meant I didn&amp;#8217;t need email confirmations, password reset functionality or even registration. Also, I didn&amp;#8217;t want to log in using an email address and password or check my own database to see if the user exists. So, no &lt;a href="http://github.com/binarylogic/authlogic"&gt;Authlogic&lt;/a&gt; or &lt;a href="http://github.com/thoughtbot/clearance"&gt;Clearance&lt;/a&gt; for me. I had to go find a more low-level solution.&lt;/p&gt;
&lt;p&gt;It didn&amp;#8217;t take long before I found &lt;a href="http://github.com/hassox/warden"&gt;Warden&lt;/a&gt;, a &amp;ldquo;General Rack Authentication Framework&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/jail.jpg" alt="Warden, General Rack Authentication Framework"/&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Warden is rack based middleware, designed to provide a mechanism for authentication in Ruby web applications. It is a common mechanism that fits into the Rack Machinery to offer powerful options for authentication.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Remember: it does &lt;em&gt;not&lt;/em&gt; do registration, confirmation and the like. If you want anything like that, use &lt;a href="http://github.com/plataformatec/devise"&gt;Devise&lt;/a&gt;, a Rails authentication system &lt;em&gt;based on&lt;/em&gt; Warden. &lt;a href="http://twitter.com/rbates"&gt;@rbates&lt;/a&gt; also did a &lt;a href="http://railscasts.com/episodes/209-introducing-devise"&gt;great Railscast on Devise&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Warden uses the concept of cascading strategies to determine if a request should be authenticated.  Warden will try strategies one after another until either one succeeds, no Strategies are found relevant or a strategy fails.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An example of a &lt;a href="http://wiki.github.com/hassox/warden/strategies"&gt;strategy&lt;/a&gt; would be a user logging in with his username and password:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;Warden&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Strategies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:my_strategy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;valid?&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:username&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate!&lt;/span&gt;
    &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_by_username_and_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:username&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# you should encrypt this. ;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    
    &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nil?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;fail&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Couldn&amp;#39;t log in&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;success!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;valid?&lt;/code&gt; method checks if the strategy is valid. In the example above it will return false when the username and password aren&amp;#8217;t both in the params. In that case it will fail without even having to try and find the user.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;authenticate!&lt;/code&gt; calls &lt;code&gt;fail!&lt;/code&gt; with a message when the authentication failed. If the authentication passes, it&amp;#8217;ll pass the &lt;code&gt;User&lt;/code&gt; instance to &lt;code&gt;success!&lt;/code&gt;. Pretty simple.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m not going into any specific stuff here, but if you&amp;#8217;re using Rails you might want to check out &lt;a href="http://github.com/HP/rails_warden_mongoid_example"&gt;rails_warden_mongoid_example&lt;/a&gt;. It&amp;#8217;s a pretty simple and understandable application that shows you how to use Warden. Also, be sure to read the &lt;a href="http://wiki.github.com/hassox/warden"&gt;wiki&lt;/a&gt;, it has pretty good &lt;a href="http://wiki.github.com/hassox/warden/setup"&gt;setup&lt;/a&gt; and &lt;a href="http://wiki.github.com/hassox/warden/examples"&gt;example&lt;/a&gt; pages and there&amp;#8217;s a lot more cool stuff in there.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/vOWhraAbrfI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/simple-authentication-with-warden</feedburner:origLink></entry>
  
  <entry>
    <title type="html">Don't put your *.gemspec in your Rakefile</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/D0x0JztkC4Y/dont-put-your-gemspec-in-your-rakefile" />
    
    <id>http://jeffkreeftmeijer.com/2010/dont-put-your-gemspec-in-your-rakefile</id>
    <updated>2010-04-03T00:00:00+02:00</updated>
    <summary>Why *.gemspecs in repositories aren't as stupid as I thought and why Rakefiles don't have anything to do with anything.</summary>
    <content type="html">
      
        &lt;p&gt;Yesterday I wrote &lt;a href="http://jeffkreeftmeijer.com/2010/gitignore-your-gemspec"&gt;&amp;ldquo;.gitignore your *.gemspec&amp;rdquo;&lt;/a&gt;, an article about how annoying it is to have your .gemspec in your repository.&lt;/p&gt;
&lt;p&gt;Just to be clear; I was &lt;em&gt;not&lt;/em&gt; talking about completely removing .gemspecs from gems and I was &lt;em&gt;not&lt;/em&gt; talking to people writing their .gemspecs themselves. My previous post was about using &lt;a href="http://github.com/technicalpickles/jeweler"&gt;Jeweler&lt;/a&gt;. When using Jeweler, you put your gem information in your Rakefile and generate your .gemspec when you need it.&lt;/p&gt;
&lt;p&gt;The problem I found with this was people unnecessarily updating the .gemspec file &amp;#8212; it&amp;#8217;s as easy as running &lt;code&gt;rake .gemspec&lt;/code&gt; &amp;#8212; in patches, so I suggested to stop putting .gemspecs in gem repositories.&lt;/p&gt;
&lt;p&gt;I think I started a discussion.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://twitter.com/rbates/status/11487541301"&gt;@rbates made a comment&lt;/a&gt; about five minutes after I posted. Here&amp;#8217;s the deal; &lt;a href="http://gembundler.com"&gt;Bundler&lt;/a&gt; has the ability bundle a library from its git repository or from a local path by specifying this in your Gemfile:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# from git&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yourgem&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:git&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;git://github.com/user/yourgem.git&amp;#39;&lt;/span&gt;

&lt;span class="c1"&gt;# from a local path&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yourgem&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:path&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;~/code/yourgem.git&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you load a library like this, it &lt;em&gt;does&lt;/em&gt; need to have a .gemspec in its code if it has any dependencies, otherwise Bundler will be unable to resolve and include these dependencies in the bundle.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://twitter.com/joshpeek/statuses/11489775203"&gt;@joshpeek suggests&lt;/a&gt; that Jeweler shouldn&amp;#8217;t even have to write a .gemspec file, but &lt;a href="http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended"&gt;@wycats eventually wrote a blogpost&lt;/a&gt; about adding .gemspecs to your repositories &amp;ldquo;as intended&amp;rdquo;. Be sure to read it.&lt;/p&gt;
&lt;p&gt;I started thinking about other solutions to the initial problem of people updating .gemspecs and wondered; Why are we putting our .gemspecs in our Rakefiles? Is the Rakefile really a place for gem information?&lt;/p&gt;
&lt;p&gt;From the replies to my blogpost, a lot of people were just updating their .gemspecs themselves. I opened one of my .gemspec files and didn&amp;#8217;t remember why I had to generate all this.&lt;/p&gt;
&lt;p&gt;You don&amp;#8217;t &lt;em&gt;need&lt;/em&gt; a tool to generate a .gemspec for you, it&amp;#8217;s as simple as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;Gem&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Specification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yourgem&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0.0.1&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_s&lt;/span&gt;
  
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;an awesome gem&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;extended description&amp;quot;&lt;/span&gt;
  
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authors&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Me Myself&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;One Other&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;me@example.com&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://github.com/user/yourgem&amp;#39;&lt;/span&gt;
  
  &lt;span class="c1"&gt;# ensure the gem is built out of versioned files&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rakefile&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{bin,lib,man,test,spec}/**/*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;&amp;#39;README*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;LICENSE*&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="sb"&gt;`git ls-files -z`&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you&amp;#8217;re extremely lazy, check out &lt;a href="http://gist.github.com/356455"&gt;@mislav&amp;#8217;s TextMate snippet&lt;/a&gt; that generates a new gemspec just by typing &lt;code&gt;gemspec&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The file is easy to read and easy to update. To release a gem, do:&lt;/p&gt;
&lt;pre&gt;
$ gem build gem.gemspec
$ gem push gem-0.0.1.gem
&lt;/pre&gt;
&lt;p&gt;This solves the issue of having your gem information in your repository twice &amp;#8212; in your Rakefile and in your .gemspec &amp;#8212; and will probably keep people from updating it unintentionally when patching your gem.&lt;/p&gt;
&lt;p&gt;So, In my previous article I didn&amp;#8217;t consider installing gems from git or a local path. Right now, you &lt;em&gt;do&lt;/em&gt; need your .gemspec in your repository when you want your users to be able to do that.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/D0x0JztkC4Y" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/dont-put-your-gemspec-in-your-rakefile</feedburner:origLink></entry>
  
  <entry>
    <title type="html">.gitignore your *.gemspec</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/kIrIFwT-ny8/gitignore-your-gemspec" />
    
    <id>http://jeffkreeftmeijer.com/2010/gitignore-your-gemspec</id>
    <updated>2010-04-02T00:00:00+02:00</updated>
    <summary>Being overly dramatic about putting the *.gemspec in your gitignore file to keep people from patching it.</summary>
    <content type="html">
      
        &lt;p&gt;I read &lt;a href="http://twitter.com/pengwynn/status/11321765532"&gt;@pengwynn&amp;#8217;s tweet&lt;/a&gt; asking people to stop touching his gemspec and I totally agreed, it&amp;#8217;s pretty annoying that people keep sending patches for your gems with updated gemspecs, right? Updating the gemspec is the gem owners job. Stop it.&lt;/p&gt;
&lt;p&gt;It didn&amp;#8217;t occur to me until I read &lt;a href="http://twitter.com/qrush/status/11323012967"&gt;@qrush&amp;#8217;s response&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the past, we had to push gemspecs to our repos to make github build gems. Newsflash; &lt;a href="http://gems.github.com"&gt;Github stopped doing that&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Right now you&amp;#8217;re probably using &lt;a href="http://github.com/technicalpickles/jeweler"&gt;Jeweler&lt;/a&gt; to quickly generate a gemspec and release your gem.&lt;/p&gt;
&lt;p&gt;So, why are you pushing your gemspec to your repo? The only time you ever need one is when installing your gem locally or releasing it. You always generate a new gemspec when you do that, right? That&amp;#8217;s my point.&lt;/p&gt;
&lt;p&gt;Still, lots of people &amp;#8212; even the cool kids &amp;#8212; can&amp;#8217;t kill their old habits. Lets get overly dramatic about this and try to stop this madness. Add this to your &lt;code&gt;.gitignore&lt;/code&gt; file;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;*.gemspec&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, &lt;code&gt;rm [yourgem].gemspec&lt;/code&gt; from your repo and tell all your friends to read this post and do the same. Let&amp;#8217;s do this. Who&amp;#8217;s with me?&lt;/p&gt;
&lt;div class="notice"&gt;
This post has a follow-up. Be sure to read &lt;a href="http://jeffkreeftmeijer.com/2010/dont-put-your-gemspec-in-your-rakefile"&gt;&amp;ldquo;Don&amp;#8217;t put your *.gemspec in your Rakefile&amp;rdquo;&lt;/a&gt; too.
&lt;/div&gt;

      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/kIrIFwT-ny8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/gitignore-your-gemspec</feedburner:origLink></entry>
  
  <entry>
    <title type="html">&amp;ldquo;Dude, what's up with this background image?&amp;rdquo;</title>
    
      <link href="http://feedproxy.google.com/~r/jeffkreeftmeijer/~3/iB3rDBG5qC4/dude-whats-up-with-this-background-image" />
    
    <id>http://jeffkreeftmeijer.com/2010/dude-whats-up-with-this-background-image</id>
    <updated>2010-03-27T00:00:00+01:00</updated>
    <summary>About the mobypicture-auto-website-background-image-fetcher.</summary>
    <content type="html">
      
        &lt;p&gt;I&amp;#8217;ve always quite liked the scaling background image on &lt;a href="http://fronteers.nl"&gt;the Fronteers website&lt;/a&gt;, so I stole their idea to give my website some pizazz.&lt;/p&gt;
&lt;p&gt;Since I didn&amp;#8217;t have a great background &amp;#8212; nor am I graphically sane enough to think of something &amp;#8212; I decided to do something fun and fetch my latest &lt;a href="http://mobypicture.com"&gt;Mobypicture&lt;/a&gt; photo. That&amp;#8217;s why there&amp;#8217;s always a completely irrelevant image as my website&amp;#8217;s background, like &lt;a href="http://www.mobypicture.com/user/jkreeftmeijer/view/6221055"&gt;@roy ordering plane tickets&lt;/a&gt; or &lt;a href="http://www.mobypicture.com/user/jkreeftmeijer/view/6204421"&gt;@antekpiechink getting us stroopwafel McFlurry&amp;#8217;s&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://jeffkreeftmeijer.com/images/homepage_screenshot.jpg" alt="latest Mobypicture photo on the background"/&gt;&lt;/p&gt;
&lt;p&gt;Mobypicture doesn&amp;#8217;t provide a direct link to your last image (&lt;a href="http://twitter.com/mathys/status/11036784161"&gt;@mathys liked the idea though&lt;/a&gt;), so a simple call to their &lt;a href="http://api.mobypicture.com/doc"&gt;&lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt; and some &lt;code&gt;cURL&lt;/code&gt; did the trick. The source can be found in &lt;a href="http://github.com/jeffkreeftmeijer/jeffkreeftmeijer.com"&gt;this site&amp;#8217;s repo on Github&lt;/a&gt;.&lt;/p&gt;
      
    &lt;img src="http://feeds.feedburner.com/~r/jeffkreeftmeijer/~4/iB3rDBG5qC4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://jeffkreeftmeijer.com/2010/dude-whats-up-with-this-background-image</feedburner:origLink></entry>
  
</feed>
