<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>oinopa ponton</title>
    <link>http://oinopa.com</link>
    <language>en</language>
    <webMaster>bj.schaefer@gail.com (Bernerd Schaefer)</webMaster>
    <pubDate>2011-03-03T06:37:53-08:00</pubDate>
    <copyright>Copyright 2010</copyright>
    <ttl>60</ttl>
    
    <item>
      <title>Multi-Locale Cucumber Features</title>
      <link>http://oinopa.com/2011/03/03/multi-locale-cucumber-features.html</link>
      <pubDate>Thu Mar 03 00:00:00 -0800 2011</pubDate>
      <guid>http://oinopa.com/archives/2011/03/03/multi-locale-cucumber-features/</guid>
      <description>&lt;p&gt;So here&amp;#8217;s the problem: Your site is available in multiple languages. How do you write features that ensure everthing works regardless of the language?&lt;/p&gt;

&lt;p&gt;I can imagine some truly clever solutions to this problem. This isn&amp;#8217;t one of them. Instead, this is a practical approach to solving the problem &amp;#8211; with the assumption that you&amp;#8217;re not using your features to communicate with non-technical stakeholders.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s start with this simple feature describing a user logging in and viewing his profile, and then see how we can transform it to suit our needs:&lt;/p&gt;
&lt;div class='highlight'&gt;
&lt;pre&gt;
Feature: User views his profile

  Scenario:
    Given I am logged in as a normal user
    When I go to the home page
    And I follow &quot;Profile&quot;
    Then I should see &quot;Your Profile&quot;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id='step_1_use_your_translation_keys'&gt;Step 1: Use your translation keys&lt;/h2&gt;

&lt;p&gt;Okay. While that feature might pass, it&amp;#8217;s not going to work if we change the default locale &amp;#8211; and it won&amp;#8217;t even work if we change our translation keys, so let&amp;#8217;s change it up a bit:&lt;/p&gt;
&lt;div class='highlight'&gt;
&lt;pre&gt;
    Given I am logged in as a normal user
    When I go to the home page
    And I follow &lt;strong&gt;t(profile)&lt;/strong&gt;
    Then I should see &lt;strong&gt;t(your_profile)&lt;/strong&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Pretty? No. But it should be familiar to anyone who&amp;#8217;s used the I18n gem, and it&amp;#8217;s super easy to plug into cucumber:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='c1'&gt;# features/step_definitions/locale_steps.rb&lt;/span&gt;

&lt;span class='c1'&gt;# Allows translation within cucumber features.&lt;/span&gt;
&lt;span class='c1'&gt;#&lt;/span&gt;
&lt;span class='c1'&gt;#   When I follow t(self_generated)&lt;/span&gt;
&lt;span class='c1'&gt;#     =&amp;gt; When I follow &amp;quot;#{t(:self_generated)}&amp;quot;&lt;/span&gt;
&lt;span class='c1'&gt;#&lt;/span&gt;
&lt;span class='no'&gt;Given&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;
&lt;span class='sr'&gt;/ ^&lt;/span&gt;
&lt;span class='sr'&gt;  (.*)              # I should see&lt;/span&gt;
&lt;span class='sr'&gt;  (t\(([^)]+)\))    # t(self_generated)&lt;/span&gt;
&lt;span class='sr'&gt;  (.*)              # within &amp;quot;#main&amp;quot;&lt;/span&gt;
&lt;span class='sr'&gt;  $&lt;/span&gt;
&lt;span class='sr'&gt;/x&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;first&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;_&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='n'&gt;last&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='no'&gt;Given&lt;/span&gt; &lt;span class='sx'&gt;%Q[&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;first&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='sx'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;t&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='si'&gt;}&lt;/span&gt;&lt;span class='sx'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;last&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='sx'&gt;]&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id='step_2_test_multiple_locales'&gt;Step 2: Test multiple locales&lt;/h2&gt;

&lt;p&gt;Now that we&amp;#8217;re using our translation keys, we can enhance our feature to test against multiple locales. We&amp;#8217;ll want something like this:&lt;/p&gt;
&lt;div class='highlight'&gt;
&lt;pre&gt;
Feature: User views his profile

  Scenario:
    &lt;strong&gt;Given my locale is &quot;en&quot;&lt;/strong&gt;
    And I am logged in as a normal user
    When I go to the home page
    And I follow t(profile)
    Then I should see t(your_profile)

  Scenario:
    &lt;strong&gt;Given my locale is &quot;de&quot;&lt;/strong&gt;
    And I am logged in as a normal user
    When I go to the home page
    And I follow t(profile)
    Then I should see t(your_profile)
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This, too, is easy to support. We can add the following step to our locale steps:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='c1'&gt;# features/step_definitions/locale_steps.rb&lt;/span&gt;

&lt;span class='no'&gt;Given&lt;/span&gt;&lt;span class='sr'&gt; /^my locale is &amp;quot;([^&amp;quot;]*)&amp;quot;$/&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;locale&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='no'&gt;I18n&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;locale&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;locale&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 then add some cleanup code into our support files, to prevent the locale from one scenario bleeding into the others.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='c1'&gt;# features/support/locale.rb&lt;/span&gt;

&lt;span class='no'&gt;After&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='no'&gt;I18n&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;locale&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;I18n&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;default_locale&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id='step_3_use_scenario_outlines'&gt;Step 3: Use scenario outlines&lt;/h2&gt;

&lt;p&gt;We could stop there.. but we&amp;#8217;re not done yet! We certainly don&amp;#8217;t want to duplicate (or triplicate, or&amp;#8230;) all of our scenarios, so let&amp;#8217;s use a scenario outline to clean this up:&lt;/p&gt;
&lt;div class='highlight'&gt;
&lt;pre&gt;
Feature: User views his profile

  Scenario Outline:
    &lt;strong&gt;Given my locale is &quot;&amp;lt;locale&amp;gt;&quot;&lt;/strong&gt;
    And I am logged in as a normal user
    When I go to the home page
    And I follow t(profile)
    Then I should see t(your_profile)

    &lt;strong&gt;Examples:
      | locale |
      | en     |
      | de     |&lt;/strong&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And there we go! Now we&amp;#8217;ve verified that our UI works in both English and German, with just a handful of new step definitions. Happy cuking!&lt;/p&gt;</description>
      
    </item>
    
    <item>
      <title>Using Devise? Want a faster test suite?</title>
      <link>http://oinopa.com/2011/02/05/want-a-faster-test-suite.html</link>
      <pubDate>Sat Feb 05 00:00:00 -0800 2011</pubDate>
      <guid>http://oinopa.com/archives/2011/02/05/want-a-faster-test-suite/</guid>
      <description>&lt;p&gt;Here&amp;#8217;s the short version: making your password hashes expensive to compute is great for production environments, but not so much for your tests.&lt;/p&gt;

&lt;p&gt;And now the longer version.&lt;/p&gt;

&lt;p&gt;Inspired by some recent &lt;a href='http://37signals.com/svn/posts/2742-the-road-to-faster-tests'&gt;blog&lt;/a&gt; &lt;a href='http://blog.carbonfive.com/2011/02/02/crank-your-specs/'&gt;posts&lt;/a&gt;, I decided to run &lt;a href='https://github.com/tmm1/perftools.rb/'&gt;perftools.rb&lt;/a&gt; against my spec suite to diagnose some slowness.&lt;/p&gt;

&lt;p&gt;Low and behold, something really strange appeared at the top of the output:&lt;/p&gt;
&lt;div class='highlight'&gt;
&lt;pre&gt;
Finished in 109.78 seconds

Total: 12182 samples
    &lt;strong&gt;3542 29.1% 29.1%    3542 29.1% BCrypt::Engine.__bc_crypt&lt;/strong&gt;
    2262  18.6%  47.6%     2262  18.6% garbage_collector
    1590  13.1%  60.7%     2488  20.4% Kernel#require
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Hm&amp;#8230; 29.1% of CPU time spent inside &lt;code&gt;BCrypt&lt;/code&gt;? Wondering where that might be coming from, I started digging around and found this:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&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;stretches&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;10&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;encryptor&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='ss'&gt;:bcrypt&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Ah! According to the documentation for &lt;a href='https://github.com/brianmario/bcrypt-ruby'&gt;bcrypt-ruby&lt;/a&gt; a cost factor of 10 (devise turns &lt;code&gt;stretches&lt;/code&gt; into cost factor when using bcrypt) is quite slow. Well, &lt;em&gt;intentionally&lt;/em&gt; slow: &amp;#8220;If an attacker was using Ruby to check each password, they could check ~140,000 passwords a second with MD5 but only ~450 passwords a second with bcrypt().&amp;#8221;&lt;/p&gt;

&lt;p&gt;Unfortunately, our test suite is the attacker now: most factories depend on a user, and each new user we create has to generate one of these expensive hashes.&lt;/p&gt;

&lt;p&gt;So &amp;#8212; what would happen if we replace the bcrypt encryptor with our own encryptor class:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='c1'&gt;# spec/support/devise.rb&lt;/span&gt;
&lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;Devise&lt;/span&gt;
  &lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;Encryptors&lt;/span&gt;
    &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Plain&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='k'&gt;class&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='nb'&gt;self&lt;/span&gt;
        &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;digest&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;password&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='n'&gt;password&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;salt&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='s2'&gt;&amp;quot;&amp;quot;&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='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&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;encryptor&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='ss'&gt;:plain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And with that in place, let&amp;#8217;s try running out suite again:&lt;/p&gt;
&lt;div class='highlight'&gt;
&lt;pre&gt;
Finished in &lt;strong&gt;65.72 seconds&lt;/strong&gt;

Total: 8428 samples
    2202  26.1%  26.1%     2202  26.1% garbage_collector
    1484  17.6%  43.7%     2329  27.6% Kernel#require
     684   8.1%  51.9%      684   8.1% IO#write
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Success! We managed to save &lt;strong&gt;44 seconds&lt;/strong&gt; by not encrypting user passwords in the test environment! Next step? Digging into all that time in &lt;code&gt;garbage_collector&lt;/code&gt; and &lt;code&gt;Kernel#require&lt;/code&gt;&amp;#8230;&lt;/p&gt;</description>
      
    </item>
    
    <item>
      <title>An XP Question</title>
      <link>http://oinopa.com/2010/10/26/an-xp-question.html</link>
      <pubDate>Tue Oct 26 00:00:00 -0700 2010</pubDate>
      <guid>http://oinopa.com/archives/2010/10/26/an-xp-question/</guid>
      <description>&lt;p&gt;I recently finished reading Kent Beck&amp;#8217;s &lt;em&gt;Extreme Programming Explained&lt;/em&gt; for the first time. It&amp;#8217;s really an amazing book, and if you haven&amp;#8217;t read it you should do so &lt;a href='http://www.amazon.com/gp/product/0321278658?ie=UTF8&amp;amp;tag=oinopont-20&amp;amp;link_code=as3&amp;amp;camp=211189&amp;amp;creative=373489&amp;amp;creativeASIN=0321278658'&gt;now&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But I was a bit confused by something, especially thinking about applying XP to a consultancy rather than a team embedded in an organization. Here are two quotes to consider &amp;#8212;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&amp;#8220;As soon as a story is written, try to estimate the development effort necessary to implement it.&amp;#8221;&lt;/p&gt;

&lt;p&gt;&amp;#8220;XP asks programmers to accept responsibility for estimating and completing their own work.&amp;#8221;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The question is this. &lt;strong&gt;If you are writing and estimating stories at the same time, and writing them each week, how do you determine a client&amp;#8217;s budget and time requirements?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m conflicted by this. Writing and estimating stories before work has started&amp;#8212;or even before you know who will be working on the project&amp;#8212;feels more like &amp;#8220;requirements gathering&amp;#8221; than something belonging to an agile team. And yet it gives you good ground for estimating time-to-launch and budget for a client: &amp;#8220;Historically we deliver an average of &lt;em&gt;x&lt;/em&gt; points per week, so unless we reduce the scope you&amp;#8217;re looking at an approximate launch date of &lt;em&gt;y&lt;/em&gt;.&amp;#8221;&lt;/p&gt;

&lt;p&gt;How have you solved this?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Reply to me on &lt;a href='http://twitter.com/bjschaefer'&gt;twitter&lt;/a&gt;, or &lt;a href='mailto:bj.schaefer@gmail.com'&gt;shoot me an email&lt;/a&gt;. If I get enough responses I&amp;#8217;ll put together a followup post.&lt;/em&gt;&lt;/p&gt;</description>
      
    </item>
    
    <item>
      <title>Laptop Driven Development</title>
      <link>http://oinopa.com/2010/10/24/laptop-driven-development.html</link>
      <pubDate>Sun Oct 24 00:00:00 -0700 2010</pubDate>
      <guid>http://oinopa.com/archives/2010/10/24/laptop-driven-development/</guid>
      <description>&lt;p&gt;Any time someone sees me working on my (1st generation) Macbook Air, I get asked, &amp;#8220;Do you do actual development on that?&amp;#8221; And the answer is, &amp;#8220;Absolutely!&amp;#8221; It&amp;#8217;s been my primary work / personal machine since I got it. I wanted to share my latest preferred workflow, for anyone else frustrated by developing on a laptop.&lt;/p&gt;

&lt;p&gt;For me, the key is getting as much out of my shell, and avoiding all possible context switches. Screen real-estate is sacred &amp;#8211; there&amp;#8217;s no room for two terminal sessions side-by-side, and even tabs take up precious space.&lt;/p&gt;

&lt;p&gt;For those who just want the answer: use &lt;code&gt;\C-z&lt;/code&gt; and set up bindings to jump back to where you were.&lt;/p&gt;

&lt;h2 id='setup'&gt;Setup&lt;/h2&gt;

&lt;p&gt;For me, the ideal setup requires just two additional lines in &lt;code&gt;~/.bash_profile&lt;/code&gt;:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='c'&gt;# ~/.bash_profile&lt;/span&gt;
&lt;span class='nb'&gt;export &lt;/span&gt;&lt;span class='nv'&gt;HISTIGNORE&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;fg*&amp;quot;&lt;/span&gt;
&lt;span class='nb'&gt;bind&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;&amp;quot;\C-f&amp;quot;: &amp;quot;fg %-\n&amp;quot;&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first line tells bash to omit any commands that start with &lt;code&gt;fg&lt;/code&gt; from the history. This will come in handy later. The second line sets up a readline binding to foreground the process. We&amp;#8217;re not using a simple &lt;code&gt;fg&lt;/code&gt; or &lt;code&gt;fg %&lt;/code&gt;, because we want to be able to swap back and forth between multiple processes, which is exactly what &lt;code&gt;fg %-&lt;/code&gt; gets us.&lt;/p&gt;

&lt;h2 id='use_case_1_switch_between_your_editor_and_a_shortlived_process'&gt;Use Case #1: switch between your editor and a short-lived process.&lt;/h2&gt;

&lt;p&gt;So you&amp;#8217;re writing an integration test with Capybara, and you can&amp;#8217;t remember what the api is to visit a particular page (I know, bear with me).&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/laptop-driven-development/editing-in-vim.png' alt='Editing integration test in vim' /&gt;&lt;/p&gt;

&lt;p&gt;Normally you might open a new tab, navigate to the project&amp;#8217;s directory, and then &lt;code&gt;bundle open capybara&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But we can do better than that! Let&amp;#8217;s send &lt;code&gt;C-z&lt;/code&gt; (or &lt;code&gt;:stop&lt;/code&gt;) to the current process.&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/laptop-driven-development/back-to-the-console.png' alt='Return to the console' /&gt;&lt;/p&gt;

&lt;p&gt;Okay. So now we can &lt;code&gt;bundle open capybara&lt;/code&gt;, poke around the source, and see, &amp;#8220;Oh, duh! It&amp;#8217;s &amp;#8216;visit&amp;#8217;!&amp;#8221; So we close it up, and then jump back into our code using the binding we set up before (&lt;code&gt;\C-f&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/laptop-driven-development/editing-in-vim.png' alt='Now were back where we were' /&gt;&lt;/p&gt;

&lt;p&gt;And we&amp;#8217;re right back where we started!&lt;/p&gt;

&lt;p&gt;This technique is also incredibly useful for running specs. &lt;code&gt;\C-z&lt;/code&gt; to get back to the console, run the spec, and then &lt;code&gt;\C-f&lt;/code&gt; to get back to your spec. If you&amp;#8217;ve forgotten what line a failure occured on, just do &lt;code&gt;:!&lt;/code&gt; to see the terminal&amp;#8217;s history. And since we ignored &lt;code&gt;fg&lt;/code&gt; commands, when we want to run the spec again, it&amp;#8217;s as simple as &lt;code&gt;\C-z&lt;/code&gt; &lt;code&gt;\C-p&lt;/code&gt; &lt;code&gt;&amp;lt;enter&amp;gt;&lt;/code&gt; (or &lt;code&gt;\C-j&lt;/code&gt; if you want to be fancy).&lt;/p&gt;

&lt;h2 id='use_case_2_switch_between_your_editor_and_another_process'&gt;Use Case #2: switch between your editor and another process.&lt;/h2&gt;

&lt;p&gt;The use case here might be for testing out some changes from an &lt;code&gt;irb&lt;/code&gt; or &lt;code&gt;rails
console&lt;/code&gt; session.&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/laptop-driven-development/irb-session.png' alt='An IRB session' /&gt;&lt;/p&gt;

&lt;p&gt;And&amp;#8230; what&amp;#8217;s that method again? Easy! &lt;code&gt;\C-z&lt;/code&gt; and &lt;code&gt;\C-f&lt;/code&gt; to hop back to vim.&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/laptop-driven-development/my-app-vim-session.png' alt='Back to VIM session' /&gt;&lt;/p&gt;

&lt;p&gt;Oh, yeah! &lt;code&gt;\C-z&lt;/code&gt; and &lt;code&gt;\C-f&lt;/code&gt; and you&amp;#8217;re back at your IRB prompt.&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/laptop-driven-development/irb-session2.png' alt='Back to the IRB session' /&gt;&lt;/p&gt;

&lt;p&gt;And that&amp;#8217;s it! I have found this to be a much more pleasant and productive way to work on a laptop than, say, switching between an external editor and the console, or even running commands in a separate tab from vim. And remember, with &lt;code&gt;vim-fugituve&lt;/code&gt;, you don&amp;#8217;t need to leave vim to commit and push your code!&lt;/p&gt;</description>
      
    </item>
    
    <item>
      <title>Introducing Safarium</title>
      <link>http://oinopa.com/2010/07/01/introducing-safarium.html</link>
      <pubDate>Thu Jul 01 00:00:00 -0700 2010</pubDate>
      <guid>http://oinopa.com/archives/2010/07/01/introducing-safarium/</guid>
      <description>&lt;p&gt;I recently switched to Chrome after using Safari for almost 3 years, and have been quite happy so far with the results: the memory footprint seems to be much better than Safari, everything feels incredibly snappy, and I&amp;#8217;m now completely addicted to the unified location bar. But there were a few things that drove me absolutely crazy, and apparently I was &lt;a href='http://www.google.com/search?q=chrome+tab+form+elements' target='_blank'&gt;not&lt;/a&gt; &lt;a href='http://www.google.com/search?q=chrome+command+enter' target='_blank'&gt;alone&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id='id1'&gt;&lt;code&gt;&amp;lt;TAB&amp;gt;&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;In Safari, when you hit &lt;code&gt;&amp;lt;TAB&amp;gt;&lt;/code&gt; it moves your cursor&amp;#8217;s focus between form elements, making it easy to navigate the page wthout using the mouse. In Chrome (and FireFox), however, &lt;code&gt;&amp;lt;TAB&amp;gt;&lt;/code&gt; instead cycles through all clickable elements on the page: links, form elements, etc. When you have been hitting &lt;code&gt;&amp;lt;TAB&amp;gt;&lt;/code&gt; for years to, say, skip directly to the search field on Amazon and then discover you need to hit &lt;code&gt;&amp;lt;TAB&amp;gt;&lt;/code&gt; 14 times in Chrome to reach the search box, this becomes a killer feature.&lt;/p&gt;

&lt;h2 id='id2'&gt;&lt;code&gt;&amp;lt;COMMAND-ENTER&amp;gt;&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;This is another feature of Safari that I used on a regular basis. When you are within a form and hit &lt;code&gt;&amp;lt;COMMAND-ENTER&amp;gt;&lt;/code&gt; in Safari, it will be submitted into a new background tab, allowing you to continue working in your active tab. This behavior exists elsewhere in Chrome: &lt;code&gt;&amp;lt;COMMAND-ENTER&amp;gt;&lt;/code&gt; in the location bar will open the page in a background tab, and you can even &lt;code&gt;&amp;lt;COMMAND-CLICK&amp;gt;&lt;/code&gt; a form&amp;#8217;s submit button to open a new tab! But when you &lt;code&gt;&amp;lt;COMMAND-ENTER&amp;gt;&lt;/code&gt; from a form element&amp;#8230; nothing happens.&lt;/p&gt;

&lt;h2 id='safarium_to_the_rescue'&gt;Safarium to the rescue!&lt;/h2&gt;

&lt;p&gt;After just a few days using Chrome, I became frustrated enough that I broke open the &lt;a href='http://code.google.com/chrome/extensions/index.html'&gt;Chrome Extensions documentation&lt;/a&gt; and put together my own extension: &lt;a href='http://github.com/bernerdschaefer/safarium'&gt;Safarium&lt;/a&gt;. Right now it provides the &lt;code&gt;&amp;lt;TAB&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;COMMAND-ENTER&amp;gt;&lt;/code&gt; features which I missed so much, and will provide more as seems necessary. It&amp;#8217;s not up on Chrome&amp;#8217;s extension site, yet, because it needs a logo and some other things, but you can use these intructions for now to get up and running:&lt;/p&gt;

&lt;p&gt;First &lt;a href='http://github.com/bernerdschaefer/safarium/zipball/master'&gt;download&lt;/a&gt; the extension&amp;#8217;s ZIP file. If it wasn&amp;#8217;t automatically extracted, make sure to do so now. Next, in Chrome, pull up your extensions list (Window &amp;#62; Extensions). There should be &amp;#8220;Developer Mode&amp;#8221; link on the right which when clicked will reveal a few buttons. Click &amp;#8216;Load unpacked extension&amp;#8230;&amp;#8217;, select the unpacked Safarium extension you downloaded, and that&amp;#8217;s it! Happy &lt;code&gt;&amp;lt;TAB&amp;gt;&lt;/code&gt;ing!&lt;/p&gt;</description>
      
    </item>
    
    <item>
      <title>FakeWeb for the Browser</title>
      <link>http://oinopa.com/2010/06/28/fakeweb-for-the-browser.html</link>
      <pubDate>Mon Jun 28 00:00:00 -0700 2010</pubDate>
      <guid>http://oinopa.com/archives/2010/06/28/fakeweb-for-the-browser/</guid>
      <description>&lt;p&gt;Before releasing &lt;a href='http://rubygems.org/gems/akephalos'&gt;akephalos-0.0.5&lt;/a&gt;, I spent some time attempting to resolve some random failures that were showing up in our real integration suites, but not in capybara or akephalos&amp;#8217; specs. Along the way, I realized that because akephalos (and the same goes for selenium) behaves just like a real browser, all of our pages with &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags for Facebook Connect, Google Analytics, and Google Maps were actually going out over the network to load the data. Even worse, the Google Maps code was being run on every page&amp;#8212;building tiles, placing markers, etc.&amp;#8212;which was killing the performace even though no scenarios actually tested the maps. Perhaps not surprisingly, after disabling these resource-intensive external javascripts, the random failures disappeared.&lt;/p&gt;

&lt;p&gt;I decided, then, that we needed something like FakeWeb except for resources requested by the browser itself, and after a bit of digging in the HTMLUnit docs, I was able to wire up what I needed to implement filters for akephalos.&lt;/p&gt;

&lt;p&gt;Akephalos Filters Configuring filters in akephalos should be familiar to anyone who has used FakeWeb or a similar library. The simplest filter requires only an &lt;abbr&gt;HTTP&lt;/abbr&gt; method (&lt;code&gt;:get&lt;/code&gt;, &lt;code&gt;:post&lt;/code&gt;, &lt;code&gt;:put&lt;/code&gt;, &lt;code&gt;:delete&lt;/code&gt;, &lt;code&gt;:any&lt;/code&gt;) and a string or regex to match against.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Akephalos&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;filter&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:get&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;http://www.google.com&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='no'&gt;Akephalos&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;filter&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:any&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='sr'&gt;%r{^http://(api\.)?twitter\.com/.*$}&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;By default, all filtered requests will return an empty body with a 200 status code. You can change this by passing additional options to your filter call.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Akephalos&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;filter&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:get&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;http://google.com/missing&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='ss'&gt;:status&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;404&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:body&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;... &amp;lt;h1&amp;gt;Not Found&amp;lt;/h1&amp;gt; ...&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

&lt;span class='no'&gt;Akephalos&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;filter&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:post&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;http://my-api.com/resource.xml&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='ss'&gt;:status&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;201&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:headers&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='s2'&gt;&amp;quot;Content-Type&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;application/xml&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='s2'&gt;&amp;quot;Location&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;http://my-api.com/resources/1.xml&amp;quot;&lt;/span&gt; &lt;span class='p'&gt;},&lt;/span&gt;
  &lt;span class='ss'&gt;:body&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;:id&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='p'&gt;}&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_xml&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And that&amp;#8217;s really all there is to it! It should be fairly trivial to set up filters for the external resources you need to fake. For reference, however, here&amp;#8217;s what we ended up using for our external sources.&lt;/p&gt;

&lt;h4 id='google_analytics'&gt;Google Analytics&lt;/h4&gt;

&lt;p&gt;Google Analytics code is passively applied based on HTML comments, so simply returning an empty response body is enough to disable it without errors.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Akephalos&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;filter&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:get&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;http://www.google-analytics.com/ga.js&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='ss'&gt;:headers&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Content-Type&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;application/javascript&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 id='facebook_connect'&gt;Facebook Connect&lt;/h4&gt;

&lt;p&gt;When you enable Facebook Connect on your page, the FeatureLoader is requested, and then additional resources are loaded when you call &lt;code&gt;FB_RequireFeatures&lt;/code&gt;. We can therefore return an empty function from our filter to disable all Facebook Connect code.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Akephalos&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;filter&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:get&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='ss'&gt;:headers&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Content-Type&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;application/javascript&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;
  &lt;span class='ss'&gt;:body&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;window.FB_RequireFeatures = function() {};&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 id='google_maps'&gt;Google Maps&lt;/h4&gt;

&lt;p&gt;Google Maps requires the most extensive amount of API definitions of the three, but these few lines cover everything we&amp;#8217;ve encountered so far.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Akephalos&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;filter&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:get&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;http://maps.google.com/maps/api/js?sensor=false&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='ss'&gt;:headers&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Content-Type&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;application/javascript&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;
  &lt;span class='ss'&gt;:body&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;window.google = {&lt;/span&gt;
&lt;span class='s2'&gt;              maps: {&lt;/span&gt;
&lt;span class='s2'&gt;                LatLng: function(){},&lt;/span&gt;
&lt;span class='s2'&gt;                Map: function(){},&lt;/span&gt;
&lt;span class='s2'&gt;                Marker: function(){},&lt;/span&gt;
&lt;span class='s2'&gt;                MapTypeId: {ROADMAP:1}&lt;/span&gt;
&lt;span class='s2'&gt;              }&lt;/span&gt;
&lt;span class='s2'&gt;            };&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
      
    </item>
    
  </channel>
</rss>

