<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
  <title>blah blah woof woof articles</title>
  
  <link href="http://openmonkey.com/" hreflang="en" rel="alternate" type="text/html" />
  <author>
    <name>Tim Riley</name>
    <uri>http://openmonkey.com/</uri>
    <email>tim@openmonkey.com</email>
  </author>
  <id>tag:openmonkey.com,2009-04-25:/</id>
  <updated>2009-07-19T10:08:00-07:00</updated>
  <link rel="self" href="http://feeds.feedburner.com/BlahBlahWoofWoof" type="application/atom+xml" /><entry>
    <id>tag:openmonkey.com,2009-04-25:/articles/2009/07/thinking-sphinx-rspec-matchers</id>
    <title type="html">Thinking Sphinx RSpec Matchers</title>
    <published>2009-07-19T10:08:00-07:00</published>
    <updated>2009-07-18T18:33:05-07:00</updated>
    <link href="http://feedproxy.google.com/~r/BlahBlahWoofWoof/~3/8233dpJRYGg/thinking-sphinx-rspec-matchers" hreflang="en" rel="alternate" type="text/html" />
    <content type="xhtml" xml:base="http://openmonkey.com/" xml:lang="en"><div xmlns="http://www.w3.org/1999/xhtml">
        <p>How do you use <a href="http://rspec.info/">RSpec</a> to drive the design of your models that will use <a href="http://ts.freelancing-gods.com/">Thinking Sphinx</a> for search? Say you’re already using <a href="http://cukes.info/">Cucumber</a> for integration tests to verify your index builds correctly and searches return the results you expect. For your models’ specs, you’ll want something that is lighter but doesn’t sacrifice your overall test coverage.</p>
        
        
        	<p>To achieve this, I wrote a couple of small RSpec matchers that inspect the Thinking Sphinx index object on your model to ensure that it contains the fields and attributes that you expect.</p><div class="highlight"><pre><span class="no">Spec</span><span class="o">::</span><span class="no">Matchers</span><span class="o">.</span><span class="n">define</span><span class="p">(</span><span class="ss">:index</span><span class="p">)</span> <span class="k">do</span> <span class="o">|*</span><span class="n">field_names</span><span class="o">|</span>
  <span class="n">description</span> <span class="k">do</span>
    <span class="s2">"have a search index for </span><span class="si">#{</span><span class="n">field_names</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s1">'.'</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>
  <span class="n">match</span> <span class="k">do</span> <span class="o">|</span><span class="n">model</span><span class="o">|</span>
    <span class="n">all_fields</span> <span class="o">=</span> <span class="n">field_names</span><span class="o">.</span><span class="n">dup</span>
    <span class="n">first_field</span> <span class="o">=</span> <span class="n">all_fields</span><span class="o">.</span><span class="n">pop</span>

    <span class="n">model</span><span class="o">.</span><span class="n">sphinx_indexes</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">fields</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">field</span><span class="o">|</span>
      <span class="n">field</span><span class="o">.</span><span class="n">columns</span><span class="o">.</span><span class="n">length</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> 
        <span class="n">field</span><span class="o">.</span><span class="n">columns</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">__stack</span> <span class="o">==</span> <span class="n">all_fields</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span> <span class="n">s</span><span class="o">.</span><span class="n">to_sym</span> <span class="p">}</span> <span class="o">&amp;&amp;</span> 
        <span class="n">field</span><span class="o">.</span><span class="n">columns</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">__name</span> <span class="o">==</span> <span class="n">first_field</span><span class="o">.</span><span class="n">to_sym</span>
    <span class="p">}</span><span class="o">.</span><span class="n">length</span> <span class="o">==</span> <span class="mi">1</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="no">Spec</span><span class="o">::</span><span class="no">Matchers</span><span class="o">.</span><span class="n">define</span><span class="p">(</span><span class="ss">:have_attribute</span><span class="p">)</span> <span class="k">do</span> <span class="o">|*</span><span class="n">attr_names</span><span class="o">|</span>
  <span class="n">description</span> <span class="k">do</span>
    <span class="s2">"have a search attribute for </span><span class="si">#{</span><span class="n">attr_names</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s1">'.'</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>
  <span class="n">match</span> <span class="k">do</span> <span class="o">|</span><span class="n">model</span><span class="o">|</span>
    <span class="n">all_attrs</span> <span class="o">=</span> <span class="n">attr_names</span><span class="o">.</span><span class="n">dup</span>
    <span class="n">first_attr</span> <span class="o">=</span> <span class="n">all_attrs</span><span class="o">.</span><span class="n">pop</span>

    <span class="n">model</span><span class="o">.</span><span class="n">sphinx_indexes</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="kp">attr</span><span class="o">|</span>
      <span class="kp">attr</span><span class="o">.</span><span class="n">columns</span><span class="o">.</span><span class="n">length</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> 
        <span class="kp">attr</span><span class="o">.</span><span class="n">columns</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">__stack</span> <span class="o">==</span> <span class="n">all_attrs</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span> <span class="n">s</span><span class="o">.</span><span class="n">to_sym</span> <span class="p">}</span> <span class="o">&amp;&amp;</span> 
        <span class="kp">attr</span><span class="o">.</span><span class="n">columns</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">__name</span> <span class="o">==</span> <span class="n">first_attr</span><span class="o">.</span><span class="n">to_sym</span>
    <span class="p">}</span><span class="o">.</span><span class="n">length</span> <span class="o">==</span> <span class="mi">1</span>
  <span class="k">end</span>
<span class="k">end</span></pre></div><p>Put these matchers in your <code>spec_helper.rb</code> or somewhere else handy, and then you can use them in your model specs:</p><div class="highlight"><pre><span class="n">describe</span> <span class="no">Question</span> <span class="k">do</span>
  <span class="n">it</span> <span class="p">{</span> <span class="n">should</span> <span class="n">index</span><span class="p">(</span><span class="ss">:topic</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">it</span> <span class="p">{</span> <span class="n">should</span> <span class="n">have_attribute</span><span class="p">(</span><span class="ss">:state</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span></pre></div><p>They read quite nicely in the single-line format above, and the matchers provide a readable description when you run the spec:</p><div class="highlight"><pre>Question
- should have a search index for topic
- should have a search attribute for legacy_mastery</pre></div><p>While these matchers work well for me, I feel that <code>sphinx_indexes</code> is perhaps an object I should leave alone, and not something I can rely on having continued access to. Please leave a comment if you have any suggestions for doing this more cleanly!</p>
        
        
        	<p>What I did learn, however, is how simple it was to write custom matchers for RSpec. If you haven’t tried it before, I strongly suggest you give it a go! RSpec’s matcher <span class="caps">DSL</span> is straightforward, and the documentation has <a href="http://rspec.rubyforge.org/rspec/1.2.8/classes/Spec/Matchers.html">everything you need</a> to get started.</p>
      <xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/BlahBlahWoofWoof/~4/8233dpJRYGg" height="1" width="1" /></div></content>
  <feedburner:origLink>http://openmonkey.com/articles/2009/07/thinking-sphinx-rspec-matchers</feedburner:origLink></entry>
  <entry>
    <id>tag:openmonkey.com,2009-04-25:/articles/2009/07/rspec-ordered-message-expectations</id>
    <title type="html">Using RSpec Ordered Message Expectations to Tighten your Specs</title>
    <published>2009-07-02T00:04:00-07:00</published>
    <updated>2009-07-01T07:33:41-07:00</updated>
    <link href="http://feedproxy.google.com/~r/BlahBlahWoofWoof/~3/DRoxyTl-020/rspec-ordered-message-expectations" hreflang="en" rel="alternate" type="text/html" />
    <content type="xhtml" xml:base="http://openmonkey.com/" xml:lang="en"><div xmlns="http://www.w3.org/1999/xhtml"><p>I quite enjoy the competitive undercurrent of ping pong pair programming. As the person writing the implementation code, it is fun to write something that will turn a test green, but still not necessarily do what my partner was expecting. Taking this approach has also been helpful for improving our specs. Take this example controller spec:</p><div class="highlight"><pre><span class="n">describe</span> <span class="no">ArticlesController</span> <span class="k">do</span>
  <span class="n">describe</span> <span class="s2">"handling create"</span> <span class="k">do</span>
    <span class="n">before</span><span class="p">(</span><span class="ss">:each</span><span class="p">)</span> <span class="k">do</span>
      <span class="vi">@article</span> <span class="o">=</span> <span class="n">mock_model</span><span class="p">(</span><span class="no">Article</span><span class="p">,</span> <span class="ss">:save</span> <span class="o">=&gt;</span> <span class="kp">nil</span><span class="p">)</span>
      <span class="no">Article</span><span class="o">.</span><span class="n">stub</span><span class="p">(</span><span class="ss">:new</span><span class="p">)</span><span class="o">.</span><span class="n">and_return</span><span class="p">(</span><span class="vi">@article</span><span class="p">)</span>
      
      <span class="vi">@user</span> <span class="o">=</span> <span class="n">mock_model</span><span class="p">(</span><span class="no">User</span><span class="p">)</span>
      <span class="n">controller</span><span class="o">.</span><span class="n">stub</span><span class="p">(</span><span class="ss">:current_user</span><span class="p">)</span><span class="o">.</span><span class="n">and_return</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
    <span class="k">end</span>
    
    <span class="n">it</span> <span class="s2">"should build a new article from posted data"</span> <span class="k">do</span>
      <span class="no">Article</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:new</span><span class="p">)</span><span class="o">.</span><span class="n">with</span><span class="p">(</span><span class="s1">'title'</span> <span class="o">=&gt;</span> <span class="s1">'Test Post'</span><span class="p">)</span>
      <span class="n">post</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:article</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="ss">:title</span> <span class="o">=&gt;</span> <span class="s1">'Test Post'</span><span class="p">}</span>
    <span class="k">end</span>
    
    <span class="n">it</span> <span class="s2">"should assign the current user as the article's author"</span> <span class="k">do</span>
      <span class="vi">@article</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:author</span><span class="o">=</span><span class="p">)</span><span class="o">.</span><span class="n">with</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
      <span class="n">post</span> <span class="ss">:create</span>
    <span class="k">end</span>
    
    <span class="n">it</span> <span class="s2">"should save the article"</span>
      <span class="vi">@article</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:save</span><span class="p">)</span>
      <span class="n">post</span> <span class="ss">:create</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span></pre></div><p>This looks like a reasonable set of concise, clear examples, but you can easily make them all pass and without building a controller action that does what you expect:</p><div class="highlight"><pre><span class="k">class</span> <span class="nc">ArticlesController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
  <span class="k">def</span> <span class="nf">create</span>
    <span class="vi">@article</span> <span class="o">=</span> <span class="no">Article</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:article</span><span class="o">]</span><span class="p">)</span>
    <span class="vi">@article</span><span class="o">.</span><span class="n">save</span>
    <span class="vi">@article</span><span class="o">.</span><span class="n">author</span> <span class="o">=</span> <span class="n">current_user</span>
  <span class="k">end</span>
<span class="k">end</span></pre></div><p>This satisfies the examples, but saving the article <em>before</em> assigning the current user as author isn’t what we would have intended. Enter RSpec’s <a href="http://rspec.info/documentation/mocks/message_expectations.html">ordered message expectations</a>. These allow you to specify the order in which you expect an object to receive message calls.</p><div class="highlight"><pre><span class="n">describe</span> <span class="no">ArticlesController</span> <span class="k">do</span>
  <span class="n">describe</span> <span class="s2">"handling create"</span> <span class="k">do</span>
    <span class="n">it</span> <span class="s2">"should save the article after assigning the current user as author"</span>
      <span class="vi">@article</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:author</span><span class="o">=</span><span class="p">)</span><span class="o">.</span><span class="n">with</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span><span class="o">.</span><span class="n">ordered</span>
      <span class="vi">@article</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:save</span><span class="p">)</span><span class="o">.</span><span class="n">ordered</span>
      <span class="n">post</span> <span class="ss">:create</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span></pre></div><p>This example would fail with the above controller action, and force us to write it properly:</p><div class="highlight"><pre><span class="k">class</span> <span class="nc">ArticlesController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
  <span class="k">def</span> <span class="nf">create</span>
    <span class="vi">@article</span> <span class="o">=</span> <span class="no">Article</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:article</span><span class="o">]</span><span class="p">)</span>
    <span class="vi">@article</span><span class="o">.</span><span class="n">author</span> <span class="o">=</span> <span class="n">current_user</span>
    <span class="vi">@article</span><span class="o">.</span><span class="n">save</span>
  <span class="k">end</span>
<span class="k">end</span></pre></div><p>The result is a controller that does what you expect, a stronger set of specs, and an increased capacity for true behaviour driven development. Win, win, win!</p><xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/BlahBlahWoofWoof/~4/DRoxyTl-020" height="1" width="1" /></div></content>
  <feedburner:origLink>http://openmonkey.com/articles/2009/07/rspec-ordered-message-expectations</feedburner:origLink></entry>
  <entry>
    <id>tag:openmonkey.com,2009-04-25:/articles/2009/06/de-wip-your-cucumber-stories</id>
    <title type="html">De-@wip Your Cucumber Stories</title>
    <published>2009-06-29T17:24:00-07:00</published>
    <updated>2009-07-01T07:33:41-07:00</updated>
    <link href="http://feedproxy.google.com/~r/BlahBlahWoofWoof/~3/MG8SCjzjxJY/de-wip-your-cucumber-stories" hreflang="en" rel="alternate" type="text/html" />
    <content type="xhtml" xml:base="http://openmonkey.com/" xml:lang="en"><div xmlns="http://www.w3.org/1999/xhtml">
        <p>We’ve been really hitting the <a href="http://cukes.info/">Cucumber</a> hard in the recent few iterations of our current project. Now that we’re <a href="http://en.wikipedia.org/wiki/Pair_programming#Ping_pong_pair_programming">ping pong pair programming</a>, the Cucumber story is the first thing we write, and we revisit regularly during the <a href="http://jamesshore.com/Blog/Red-Green-Refactor.html">red-green-refactor</a> cycle.</p>
        
        
        	<p>To make this easy, we place a <code>@wip</code> <em>work in progress</em> <a href="http://wiki.github.com/aslakhellesoy/cucumber/tags">tag</a> at the top of the current stories:</p><div class="highlight"><pre><span class="no">Feature</span><span class="p">:</span> <span class="no">Make</span> <span class="n">coffee</span>

  <span class="c1"># This one is done.</span>
  <span class="no">Scenario</span><span class="p">:</span> <span class="no">First</span> <span class="n">coffee</span> <span class="n">of</span> <span class="n">the</span> <span class="n">day</span>
  
  <span class="c1"># This is the one we're working on.</span>
  <span class="vi">@wip</span>
  <span class="no">Scenario</span><span class="p">:</span> <span class="no">Afternoon</span> <span class="n">perk</span> <span class="n">up</span></pre></div><p>Then we use <code>cucumber -t wip</code> to run just the stories in progress. Once the stories are green and we’re happy to move on, we often forget to remove the <code>@wip</code> tags before committing. I wrote a little sed command in a bash alias to make this easier:</p><div class="highlight"><pre><span class="c"># Remove any @wip tags from Cucumber features.</span>
<span class="nb">alias </span><span class="nv">dewip</span><span class="o">=</span><span class="s2">"sed -E -i '' -e '/^[[:blank:]]*@wip$/d;s/,[[:blank:]]*@wip//g;s/@wip,[[:blank:]]*//g' features/**/*.feature"</span></pre></div><p>Throw it in your <code>.bash_profile</code> for ease of access! Here’s a <a href="http://gist.github.com/136294">gist</a> for your forking pleasure.</p>
      <xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/BlahBlahWoofWoof/~4/MG8SCjzjxJY" height="1" width="1" /></div></content>
  <feedburner:origLink>http://openmonkey.com/articles/2009/06/de-wip-your-cucumber-stories</feedburner:origLink></entry>
  <entry>
    <id>tag:openmonkey.com,2009-04-25:/articles/2009/05/rails-templates-as-geek-code</id>
    <title type="html">Rails Templates as the New Geek Code</title>
    <published>2009-05-20T13:00:00-07:00</published>
    <updated>2009-07-01T07:33:41-07:00</updated>
    <link href="http://feedproxy.google.com/~r/BlahBlahWoofWoof/~3/7Xli_qTqV0I/rails-templates-as-geek-code" hreflang="en" rel="alternate" type="text/html" />
    <content type="xhtml" xml:base="http://openmonkey.com/" xml:lang="en"><div xmlns="http://www.w3.org/1999/xhtml">
        <p>Anyone who’s been around the Internet long enough should remember <a href="http://www.geekcode.com/">The Geek Code</a>. This meme sought to provide a – however obsbcure – succinct textual distillation of the attributes and interests of any geek.</p><div class="highlight"><pre>GED/J d-- s:++&gt;: a-- C++(++++) ULU++ P+ L++ E---- W+(-) N+++ o+ K+++ w---
O- M+ V-- PS++&gt;$ PE++&gt;$ Y++ PGP++ t- 5+++ X++ R+++&gt;$ tv+ b+ DI+++ D+++
G+++++ e++ h r-- y++**</pre></div><p>This geek code block represents someone trained in education and law who is tall and dresses casually, knows his way around Linux and Ultrix pretty well, hates emacs, but loves indulging in a little Dilbert. <a href="http://www.geekcode.com/geek.html">Look it up</a>, it’s elaborate.</p>
        
        
        	<p>Today, a Rails developer can provide a <a href="http://m.onkey.org/2008/12/4/rails-templates">template</a> for generating new apps that can uniquely embody all their development tools and preferences in a single place.</p><div class="highlight"><pre><span class="n">gem</span> <span class="s1">'haml'</span>
<span class="n">gem</span> <span class="s1">'mislav-will_paginate'</span><span class="p">,</span>   <span class="ss">:lib</span> <span class="o">=&gt;</span> <span class="s1">'will_paginate'</span><span class="p">,</span>  <span class="ss">:source</span> <span class="o">=&gt;</span> <span class="s1">'http://gems.github.com'</span>
<span class="n">gem</span> <span class="s1">'chriseppstein-compass'</span><span class="p">,</span>  <span class="ss">:lib</span> <span class="o">=&gt;</span> <span class="s1">'compass'</span><span class="p">,</span>        <span class="ss">:source</span> <span class="o">=&gt;</span> <span class="s1">'http://gems.github.com'</span>
<span class="n">gem</span> <span class="s1">'thoughtbot-paperclip'</span><span class="p">,</span>   <span class="ss">:lib</span> <span class="o">=&gt;</span> <span class="s1">'paperclip'</span><span class="p">,</span>      <span class="ss">:source</span> <span class="o">=&gt;</span> <span class="s1">'http://gems.github.com'</span>   <span class="k">if</span> <span class="n">yes?</span><span class="p">(</span><span class="s1">'Paperclip gem?'</span><span class="p">)</span>
<span class="n">gem</span> <span class="s1">'authlogic'</span>                                                                               <span class="k">if</span> <span class="n">yes?</span><span class="p">(</span><span class="s1">'Authlogic gem?'</span><span class="p">)</span></pre></div><p>You’re a haml guy? Right on. Plus compass for <span class="caps">CSS</span>! You must like really things semantic.</p><div class="highlight"><pre><span class="n">file</span> <span class="s1">'.testgems'</span><span class="p">,</span>
<span class="sx">%q{config.gem 'rspec'</span>
<span class="sx">config.gem 'rspec-rails'</span>
<span class="sx">config.gem 'notahat-machinist', :lib =&gt; 'machinist',  :source =&gt; 'http://gems.github.com'</span>
<span class="sx">config.gem 'ianwhite-pickle',   :lib =&gt; 'pickle',     :source =&gt; 'http://gems.github.com'</span>
<span class="sx">config.gem 'webrat'</span>
<span class="sx">config.gem 'cucumber'</span>
<span class="sx">}</span>
<span class="n">run</span> <span class="s1">'cat .testgems &gt;&gt; config/environments/test.rb &amp;&amp; rm .testgems'</span></pre></div><p>RSpec along with Cucumber backed by Machinist and Pickle for test data factories. You must be Australian. But that’s cool, that’s a pretty helpful combo for tests.</p><div class="highlight"><pre><span class="n">git</span> <span class="ss">:init</span>
<span class="n">git</span> <span class="ss">:add</span> <span class="o">=&gt;</span> <span class="s1">'.'</span>
<span class="n">git</span> <span class="ss">:commit</span> <span class="o">=&gt;</span> <span class="s1">'-a -m "Initial commit from AMC Rails template"'</span></pre></div><p>Building a complete Rails template has been really useful for us at the <a href="http://www.amc.org.au/"><span class="caps">AMC</span></a>. We’re often asked to bring up quick, single-purpose apps alongside the many components that we’re building in our mostly service-oriented architecture.</p>
        
        
        	<p>Our template is <a href="http://github.com/timriley/amc-rails-template">available on GitHub</a> for your perusal and reuse. It does everything from setting up plugins and gem dependencies, to generating a deploy script using our custom capistrano extensions, to including a default layout and stylesheet. This is how we roll, captured in a single file.</p>
      <xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/BlahBlahWoofWoof/~4/7Xli_qTqV0I" height="1" width="1" /></div></content>
  <feedburner:origLink>http://openmonkey.com/articles/2009/05/rails-templates-as-geek-code</feedburner:origLink></entry>
  <entry>
    <id>tag:openmonkey.com,2009-04-25:/articles/2009/05/importing-legacy-data-in-rails</id>
    <title type="html">Importing Legacy Data in Rails</title>
    <published>2009-05-02T08:37:00-07:00</published>
    <updated>2009-07-01T07:33:41-07:00</updated>
    <link href="http://feedproxy.google.com/~r/BlahBlahWoofWoof/~3/kTFQNYv-4Qo/importing-legacy-data-in-rails" hreflang="en" rel="alternate" type="text/html" />
    <content type="xhtml" xml:base="http://openmonkey.com/" xml:lang="en"><div xmlns="http://www.w3.org/1999/xhtml">
        <h2>How Did we Get Here?</h2>
        
        
        	<p>Our current <a href="http://www.amc.org.au/">work</a> project is a long-overdue rebuild of a critical business system as a modern Rails web app. We’re building this thing according to agile practices to the best of our ability. Each week we provide a new, working release that is an incremental improvement on the last. We’re not looking to replace the current system in a single swoop, and expect the new system to work alongside the old one for quite a while.</p>
        
        
        	<p>This means that we’ll need to maintain the same data in both systems for the duration of their lives together. We don’t want the new Rails app to access the data directly from the old app’s database, since this would prevent us from following the conventions that makes working with Rails so pleasant. Instead, we’ve come up with a way to import the legacy data into a new database.</p>
        
        
        	<p>In doing this, I’ve built a Rails plugin to make the experience easier. It is called <strong>Acts as Importable</strong> and it is now <a href="http://github.com/timriley/acts-as-importable">available on GitHub</a>. This article will show our technique for importing the legacy data and how Acts as Importable helps.</p>
        
        
        	<h2>Accessing the Legacy Data</h2>
        
        
        	<p>We use ActiveRecord to access the legacy data. It takes a bit of legwork to shoehorn the legacy schema into ActiveRecord models, but once that is done, we have satisfactory access to the data we want to import.</p>
        
        
        	<p>The first thing we do is provide a <code>Legacy::Base</code> model from which all other legacy models can inherit. This provides a single place to define access to the legacy database.</p><div class="highlight"><pre><span class="k">class</span> <span class="nc">Legacy</span><span class="o">::</span><span class="no">Base</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
  <span class="nb">self</span><span class="o">.</span><span class="n">abstract_class</span> <span class="o">=</span> <span class="kp">true</span>
  <span class="n">establish_connection</span> <span class="s2">"legacy_</span><span class="si">#{</span><span class="no">Rails</span><span class="o">.</span><span class="n">env</span><span class="si">}</span><span class="s2">"</span>

  <span class="n">acts_as_importable</span>
<span class="k">end</span></pre></div><p>You can setup the extra database connections in <code>config/database.yml</code>, like below. Our legacy connection is to an <span class="caps">MS SQL</span> server via <span class="caps">ODBC</span>. Tim Lucas has <a href="http://toolmantim.com/articles/getting_rails_talking_to_sqlserver_on_osx_via_odbc">an excellent tutorial</a> on how to get that set up.</p><div class="highlight"><pre><span class="l-Scalar-Plain">legacy_development</span><span class="p-Indicator">:</span>
  <span class="l-Scalar-Plain">adapter</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">sqlserver</span>
  <span class="l-Scalar-Plain">mode</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">odbc</span>
  <span class="l-Scalar-Plain">dsn</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">LEGACY</span>
  <span class="l-Scalar-Plain">autocommit</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">true</span>
  <span class="l-Scalar-Plain">host</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">localhost</span>
  <span class="l-Scalar-Plain">username</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">NTDOMAIN\username</span>
  <span class="l-Scalar-Plain">password</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">password</span></pre></div><p>Now, here are a couple of the legacy models that inherit from <code>Legacy::Base</code> to access the legacy database. For the sake of this article, consider the code examples to come from a quiz application with models for categories, questions, and responses.</p><div class="highlight"><pre><span class="k">class</span> <span class="nc">Legacy</span><span class="o">::</span><span class="no">Question</span> <span class="o">&lt;</span> <span class="no">Legacy</span><span class="o">::</span><span class="no">Base</span>
  <span class="n">set_table_name</span>  <span class="s1">'quiz_questions'</span>
  <span class="n">set_primary_key</span> <span class="s1">'QuestionNumber'</span>
  
  <span class="n">belongs_to</span> <span class="ss">:category</span><span class="p">,</span>
             <span class="ss">:class_name</span>  <span class="o">=&gt;</span> <span class="s1">'Legacy::Category'</span><span class="p">,</span>
             <span class="ss">:foreign_key</span> <span class="o">=&gt;</span> <span class="s1">'CategoryCode'</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">Legacy</span><span class="o">::</span><span class="no">Category</span> <span class="o">&lt;</span> <span class="no">Legacy</span><span class="o">::</span><span class="no">Base</span>
  <span class="n">set_table_name</span> <span class="s1">'quiz_categories'</span>
  <span class="n">set_primary_key</span> <span class="s1">'Code'</span>
<span class="k">end</span></pre></div><p>These are simple examples, but you get the idea. <code>set_table_name</code> and <code>set_primary_key</code> are your friends when you have table names and keys that defy Rails’ conventions. If you want to use ActiveRecord associations to access your legacy data, you will also want to become familiar with the options available for specifying things like the class, join table and column names. Check the rdocs for <code>has_many</code>, <code>belongs_to</code> and <code>has_and_belongs_to_many</code> and look out for options like <code>:class_name</code>, <code>:foreign_key</code>, <code>:join_table</code>, and <code>:association_foreign_key</code>.</p>
        
        
        	<h2>Converting the Legacy Data</h2>
        
        
        	<p>Each of the legacy models provides a <code>to_model</code> method that returns a new model ready to be saved into the new, non-legacy database.</p><div class="highlight"><pre><span class="c1"># New model</span>
<span class="k">class</span> <span class="nc">Category</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="k">end</span>

<span class="c1"># Legacy model</span>
<span class="k">class</span> <span class="nc">Legacy</span><span class="o">::</span><span class="no">Category</span> <span class="o">&lt;</span> <span class="no">Legacy</span><span class="o">::</span><span class="no">Base</span>
  <span class="n">set_table_name</span> <span class="s1">'quiz_categories'</span>
  <span class="n">set_primary_key</span> <span class="s1">'Code'</span>

  <span class="k">def</span> <span class="nf">to_model</span>
    <span class="o">::</span><span class="no">Category</span><span class="o">.</span><span class="n">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>
      <span class="n">c</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">Description</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span></pre></div><h2>Importing the Legacy Data</h2>
        
        
        	<p>Now that the import rules are defined for all the legacy models we care about, how do we get them all into the new database? This is where <a href="http://github.com/timriley/acts_as_importable">Acts as Importable</a> comes in. This plugin helps to smoothen the import of an entire system’s worth of legacy data. You saw it enabled above in the <code>Legacy::Base</code> class:</p><div class="highlight"><pre><span class="k">class</span> <span class="nc">Legacy</span><span class="o">::</span><span class="no">Base</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
  <span class="nb">self</span><span class="o">.</span><span class="n">abstract_class</span> <span class="o">=</span> <span class="kp">true</span>
  <span class="n">establish_connection</span> <span class="s2">"legacy_</span><span class="si">#{</span><span class="no">Rails</span><span class="o">.</span><span class="n">env</span><span class="si">}</span><span class="s2">"</span>

  <span class="n">acts_as_importable</span>
<span class="k">end</span></pre></div><p>Acts as importable will let you import your legacy models in different ways:</p><div class="highlight"><pre><span class="c1"># Import a single question</span>
<span class="no">Legacy</span><span class="o">::</span><span class="no">Question</span><span class="o">.</span><span class="n">import</span><span class="p">(</span><span class="mi">123</span><span class="p">)</span>
<span class="c1"># or</span>
<span class="no">Legacy</span><span class="o">::</span><span class="no">Question</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="mi">123</span><span class="p">)</span><span class="o">.</span><span class="n">import</span>

<span class="c1"># Import all questions</span>
<span class="no">Legacy</span><span class="o">::</span><span class="no">Question</span><span class="o">.</span><span class="n">import_all</span>

<span class="c1"># Import all the questions, 1000 at a time</span>
<span class="no">Legacy</span><span class="o">::</span><span class="no">Question</span><span class="o">.</span><span class="n">import_all_in_batches</span></pre></div><p>These are all different ways of instantiating your legacy models, calling the <code>to_model</code> method, and saving the returned object. The value of the plugin is that it adds a little bit of extra smarts to this basic premise.</p>
        
        
        	<h3>belongs_to Relationships &amp; Caching</h3>
        
        
        	<p>Acts as Importable provides a <code>lookup</code> class method for finding the new ID of an imported legacy model. When each legacy model is imported using the above methods, Acts as Importable saves the legacy model’s class name and ID along with the rest of the data in new model (be sure to provide <code>legacy_class</code> and <code>legacy_id</code> columns for this to work). The first time you <code>lookup</code> a legacy record, it uses <code>ActiveRecord::Base#find</code> with the appropriate values for <code>legacy_class</code> and <code>legacy_id</code> as conditions. It will save the ID in a lookup hash in memory, so that the next time you want to lookup from the same ID, the result is returned without a trip to the database.</p>
        
        
        	<p>This is really best seen in action:</p><div class="highlight"><pre><span class="c1"># New models</span>
<span class="k">class</span> <span class="nc">Question</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
  <span class="n">belongs_to</span> <span class="ss">:category</span>
  <span class="n">has_many</span> <span class="ss">:responses</span><span class="p">,</span> <span class="ss">:dependent</span> <span class="o">=&gt;</span> <span class="ss">:destroy</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Category</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
  <span class="n">has_many</span> <span class="ss">:questions</span>
<span class="k">end</span>

<span class="c1"># Legacy model</span>
<span class="k">class</span> <span class="nc">Legacy</span><span class="o">::</span><span class="no">Question</span> <span class="o">&lt;</span> <span class="no">Legacy</span><span class="o">::</span><span class="no">Base</span>
  <span class="k">def</span> <span class="nf">to_model</span>
    <span class="o">::</span><span class="no">Question</span><span class="o">.</span><span class="n">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">q</span><span class="o">|</span>
      <span class="c1"># import the category association</span>
      <span class="n">q</span><span class="o">.</span><span class="n">category_id</span> <span class="o">=</span> <span class="no">Legacy</span><span class="o">::</span><span class="no">Category</span><span class="o">.</span><span class="n">lookup</span><span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">category</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:id__</span><span class="p">))</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span></pre></div><p>Setting the id for associations directly like this is important for conserving trips to the database importing a model’s <code>belongs_to</code> relationships, just like the one above. Be wary when using lookups for large data sets, however, as it will likely consume quite a bit of memory to store the ID mappings.</p>
        
        
        	<p>As an aside, we use <code>#try(:id__)</code> on the legacy model to provide the ID for the lookup, because <code>#id</code> on a nil value actually returns its <code>#object_id</code>. If the associated object doesn’t exist, we don’t want to pass a bogus ID. Acts as Importable provides <code>Object#try</code> and the <code>ActiveRecord::Base#id__</code> to <code>id</code> alias for you.</p>
        
        
        	<p>The lookups will work automatically if the class name of the model you’re importing is the same as the legacy model’s, eg. <code>Legacy::Question</code> to <code>Question</code>. If the class name of the model you’re creating is different, you can tell that to Acts as Importable so that the lookups can continue to work:</p><div class="highlight"><pre><span class="c1"># New model</span>
<span class="k">class</span> <span class="nc">DifferentThing</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="k">end</span>

<span class="c1"># Legacy model</span>
<span class="k">class</span> <span class="nc">Legacy</span><span class="o">::</span><span class="no">Thing</span>
  <span class="n">acts_as_importable</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="s1">'DifferentThing'</span>
  
  <span class="k">def</span> <span class="nf">to_model</span>
    <span class="o">::</span><span class="no">DifferentThing</span><span class="o">.</span><span class="n">new</span>
  <span class="k">end</span>
<span class="k">end</span></pre></div><h3>has_many Relationships &amp; Building</h3>
        
        
        	<p>You can import <code>has_many</code> relationships quite easily, using the <code>#build</code> method on the association proxy. Here it is in action, expanding on the Question’s <code>#to_model</code> method from above:</p><div class="highlight"><pre><span class="c1"># New models</span>
<span class="k">class</span> <span class="nc">Question</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
  <span class="n">belongs_to</span> <span class="ss">:category</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Response</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
  <span class="n">belongs_to</span> <span class="ss">:question</span>
<span class="k">end</span>

<span class="c1"># Legacy model</span>
<span class="k">class</span> <span class="nc">Legacy</span><span class="o">::</span><span class="no">Question</span> <span class="o">&lt;</span> <span class="no">Legacy</span><span class="o">::</span><span class="no">Base</span>
  <span class="k">def</span> <span class="nf">to_model</span>
    <span class="o">::</span><span class="no">Question</span><span class="o">.</span><span class="n">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">q</span><span class="o">|</span>
      <span class="n">q</span><span class="o">.</span><span class="n">category_id</span> <span class="o">=</span> <span class="no">Legacy</span><span class="o">::</span><span class="no">Category</span><span class="o">.</span><span class="n">lookup</span><span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">category</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:id__</span><span class="p">))</span>
      
      <span class="c1"># Build the responses</span>
      <span class="p">(</span><span class="mi">1</span><span class="o">.</span><span class="n">.</span><span class="mi">5</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
        <span class="n">q</span><span class="o">.</span><span class="n">responses</span><span class="o">.</span><span class="n">build</span><span class="p">(</span><span class="ss">:body</span> <span class="o">=&gt;</span> <span class="nb">self</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="ss">:"response_</span><span class="si">#{</span><span class="n">i</span><span class="si">}</span><span class="ss">"</span><span class="p">))</span>
      <span class="k">end</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span></pre></div><p>When you save the new model, the newly built associated models are saved too.</p>
        
        
        	<h3>Importing Large Sets of Legacy Records</h3>
        
        
        	<p>We ran into some slowness importing legacy models with large numbers of records, or with other models with large amounts of data in particular fields. To get around this, we use <code>#import_all_in_batches</code>, which only retrieves 1000 models at a time for processing. This is based on Jamis Buck’s technique for <a href="http://weblog.jamisbuck.org/2007/4/6/faking-cursors-in-activerecord">faking cursors in ActiveRecord</a>, and as such, it requires a numeric primary key for the legacy models (you’d normally expect this to be the case, but it isn’t for a few of our legacy tables).</p>
        
        
        	<h3>Idempotence of Imports</h3>
        
        
        	<p>As I mentioned in the introduction, the legacy app we’re replacing will remain in use as we incrementally build the new system. We will need to continue to synchronise the legacy data with the new system during this time. We’ll therefore need our import process to be <a href="http://en.wikipedia.org/wiki/Idempotence">idempotent</a>, meaning that multiple imports can run and result in the same set of data at the other end. Mostly, this just means that we’ll want to avoid creation of duplicate records in the new database.</p>
        
        
        	<p>We approach this pretty simply. Each night the old records are deleted and new ones re-imported in their place. You’ll want to pick an approach that best suits your situation.</p>
        
        
        	<p>There is one complication, however, insofar as the new system is used to create some entirely new content that relates to the imported models. If models are deleted and re-imported from the legacy system every night, their IDs would be different each time. To get around this, we hard-code the ID of certain imported models to the same value as their respective legacy model’s ID. This is very simply done:</p><div class="highlight"><pre><span class="k">class</span> <span class="nc">Legacy</span><span class="o">::</span><span class="no">Question</span> <span class="o">&lt;</span> <span class="no">Legacy</span><span class="o">::</span><span class="no">Base</span>
  <span class="k">def</span> <span class="nf">to_model</span>
    <span class="o">::</span><span class="no">Question</span><span class="o">.</span><span class="n">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">q</span><span class="o">|</span>
      <span class="c1"># Hard-code the ID</span>
      <span class="n">q</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">id</span>
      
      <span class="n">q</span><span class="o">.</span><span class="n">category_id</span> <span class="o">=</span> <span class="no">Legacy</span><span class="o">::</span><span class="no">Category</span><span class="o">.</span><span class="n">lookup</span><span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">category</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:id__</span><span class="p">))</span>
      
      <span class="p">(</span><span class="mi">1</span><span class="o">.</span><span class="n">.</span><span class="mi">5</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
        <span class="n">q</span><span class="o">.</span><span class="n">responses</span><span class="o">.</span><span class="n">build</span><span class="p">(</span><span class="ss">:body</span> <span class="o">=&gt;</span> <span class="nb">self</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="ss">:"response_</span><span class="si">#{</span><span class="n">i</span><span class="si">}</span><span class="ss">"</span><span class="p">))</span>
      <span class="k">end</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span></pre></div><h2>Final steps</h2>
        
        
        	<p>There are two final pieces to our import system. Firstly, a way to control the order of the imports:</p><div class="highlight"><pre><span class="k">class</span> <span class="nc">Legacy</span><span class="o">::</span><span class="no">Importer</span>
  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">run</span>
    <span class="no">Legacy</span><span class="o">::</span><span class="no">Category</span><span class="o">.</span><span class="n">import_all</span>
    <span class="no">Legacy</span><span class="o">::</span><span class="no">Question</span><span class="o">.</span><span class="n">import_all_in_batches</span>

    <span class="c1"># Flush all the lookup tables</span>
    <span class="no">Legacy</span><span class="o">::</span><span class="no">Category</span><span class="o">.</span><span class="n">flush_lookups!</span>
    <span class="no">Legacy</span><span class="o">::</span><span class="no">Question</span><span class="o">.</span><span class="n">flush_lookups!</span>
  <span class="k">end</span>
<span class="k">end</span></pre></div><p>It is important to control the order if your imported models are going to relate to each other. Some records will need to exist before the others can link to them.</p>
        
        
        	<p>And finally, a rake task:</p><div class="highlight"><pre><span class="n">namespace</span> <span class="ss">:legacy</span> <span class="k">do</span>
  <span class="n">desc</span> <span class="s2">"Import the legacy data."</span>
  <span class="n">task</span> <span class="ss">:import</span> <span class="o">=&gt;</span> <span class="ss">:environment</span> <span class="k">do</span>
    <span class="no">Legacy</span><span class="o">::</span><span class="no">Importer</span><span class="o">.</span><span class="n">run</span>
  <span class="k">end</span>
<span class="k">end</span></pre></div><p>That’s it! This approach certainly works to our liking, but I would love to hear your thoughts on this issue. Please feel free to post a comment below.</p>
        
        
        	<h2>Further Reading</h2>
        
        
        	<p>These articles were of great use in getting our legacy imports up and running:</p>
        
        
        	<ul>
        	<li><a href="http://pragdave.blogs.pragprog.com/pragdave/2006/01/sharing_externa.html">Sharing External ActiveRecord Connections</a></li>
        		<li><a href="http://www.pathf.com/blogs/2008/03/using-activerec/">Agile Ajax » Using ActiveRecord to Migrate Legacy Data</a></li>
        	</ul>
      <xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/BlahBlahWoofWoof/~4/kTFQNYv-4Qo" height="1" width="1" /></div></content>
  <feedburner:origLink>http://openmonkey.com/articles/2009/05/importing-legacy-data-in-rails</feedburner:origLink></entry>
  <entry>
    <id>tag:openmonkey.com,2009-04-25:/articles/2009/03/adaptive-script-console-shell-alias-for-both-rails-and-sinatra</id>
    <title type="html">Adaptive script/console Shell Alias for both Rails and Sinatra</title>
    <published>2009-03-06T15:55:00-08:00</published>
    <updated>2009-07-01T07:33:41-07:00</updated>
    <link href="http://feedproxy.google.com/~r/BlahBlahWoofWoof/~3/whchNFTMCEY/adaptive-script-console-shell-alias-for-both-rails-and-sinatra" hreflang="en" rel="alternate" type="text/html" />
    <content type="xhtml" xml:base="http://openmonkey.com/" xml:lang="en"><div xmlns="http://www.w3.org/1999/xhtml">
        <p>Like many keystroke-efficient Rails hackers, I’ve long had a line in my <code>.bash_profile</code> file to alias <code>sc</code> to <code>script/console</code>, along with a <a href="http://gist.github.com/74761">host of other tricks</a>.</p>
        
        
        	<p>This shortcut was more than sufficient until recently, when I started writing Sinatra apps. The minimal framework that it is, Sinatra doesn’t provide a console script like Rails, but I found you can easily achieve the same effect by running <code>irb -r your_sinatra_app.rb</code>.</p>
        
        
        	<p>Not wanting my fingers to have to deviate from habits long held, I changed my <code>sc</code> alias into a full-blown bash function that will drop you into a Rails console, Sinatra console or just a plain irb console based on your location within the filesystem:</p><div class="highlight"><pre><span class="k">function </span>sc <span class="o">{</span>
  <span class="k">if</span> <span class="o">[</span> -x script/console <span class="o">]</span>; <span class="k">then</span>
<span class="k">    </span>script/console
  <span class="k">else</span>
<span class="k">    </span><span class="nv">sinatra_rb</span><span class="o">=</span><span class="sb">`</span>egrep -l <span class="s2">"^require.+sinatra.$"</span> *.rb 2&gt;/dev/null<span class="sb">`</span>
    <span class="k">if</span> <span class="o">[</span> -e <span class="nv">$sinatra_rb</span> <span class="o">]</span>; <span class="k">then</span>
<span class="k">      </span>irb -r <span class="nv">$sinatra_rb</span>
    <span class="k">else</span>
<span class="k">      </span>irb
    <span class="k">fi</span>
<span class="k">  fi</span>
<span class="o">}</span></pre></div><p>Throw it in your <code>.bash_profile</code> and have fun!</p>
      <xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/BlahBlahWoofWoof/~4/whchNFTMCEY" height="1" width="1" /></div></content>
  <feedburner:origLink>http://openmonkey.com/articles/2009/03/adaptive-script-console-shell-alias-for-both-rails-and-sinatra</feedburner:origLink></entry>
</feed>
