<?xml version='1.0' encoding='utf-8' ?>
<feed xmlns='http://www.w3.org/2005/Atom'>
  <id>http://turriate.com</id>
  <title>can't stop the rot</title>
  <updated>2011-02-07T19:06:00-08:00</updated>
  <link href='http://turriate.com' rel='alternative' />
  <link href='http://turriate.com/articles/feed' rel='self' />
  <author>
    <name>Sandro Turriate</name>
    <uri>http://turriate.com</uri>
  </author>
  <entry>
    <id>tag:turriate.com,2011-02-07:/articles/2011/feb/how-to-generate-signed-rails-session-cookie/</id>
    <title type='html'>How to generate a signed Rails session cookie</title>
    <published>2011-02-07T19:06:00-08:00</published>
    <link href='http://turriate.com/articles/2011/feb/how-to-generate-signed-rails-session-cookie/' rel='alternative' />
    <summary type='html'>Bypass the sign-in process by sending a signed session cookie in the HTTP headers.</summary>
    <content type='html'>
      &lt;p&gt;
        In my &lt;a href=&quot;/articles/2011/feb/testing-twitter-oauth-with-real-data/&quot;&gt;last article&lt;/a&gt; I wrote about testing the Twitter sign-in process by visiting Twitter, signing in to a real account, authorizing the application and returning to the callback url. We followed the exact path that the user follows to ensure that sign-in integrates flawlessly with Twitter. As we begin testing the rest of our application we'll need users to sign in but they don't need to follow the end-to-end sign-in path.
      &lt;/p&gt;
      &lt;p&gt;
        Most authentication systems provide a way of bypassing the sign-in process under the test environment but I had trouble coming up with a good solution for OmniAuth (pre 0.2.0). Passing an acceptable hash of attributes to the OmniAuth callback url was the initial idea but before leaving the middleware, OmniAuth makes a GET request to Twitter verifying account credentials. Sure, I could use &lt;a href=&quot;http://github.com/sandro/ephemeral_response&quot;&gt;EphemeralResponse&lt;/a&gt; to cache this request but with sign-in thoroughly tested elsewhere, the rest of my suite should sign in as simply as possible.
      &lt;/p&gt;
      &lt;p&gt;
        I can't easily go through OmniAuth without faking the Twitter interaction or monkeypatching the library so I decided to bypass OmniAuth altogether. When OmniAuth is successful, the user is signed in via their session. To mimic this behavior, all we have to do is to send a legitimately signed session cookie to the server. Unfortunately, generating a signed session cookie is not a straightforward process but after reading through a fair amount of source I wrote a class to generate them:
      &lt;/p&gt;
      &lt;div class='gist'&gt;
        &lt;div class='gist-file'&gt;
          &lt;div class='gist-data gist-syntax'&gt;
            &lt;div class='gist-highlight'&gt;
              &lt;pre&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SessionCookieGenerator&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;kp&quot;&gt;extend&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Forwardable&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;def_delegators&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:session_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:[]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:[]=&lt;/span&gt;&amp;#x000A;&amp;#x000A;  &lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:session_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:session_key&lt;/span&gt;&amp;#x000A;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;vi&quot;&gt;@session_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session_options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;vi&quot;&gt;@session_hash&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;session_id&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SecureRandom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;vi&quot;&gt;@cookie_jar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionDispatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Cookies&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CookieJar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secret_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Capybara&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default_host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session_key&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;signed_session_value&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;;&amp;quot;&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;signed_session_value&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;vi&quot;&gt;@cookie_jar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;signed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session_hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;no&quot;&gt;Rack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Utils&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;escape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@cookie_jar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      
      &lt;p&gt;
        Initialize a new object, set a session key and value, then call #to_s to retrieve the signed session cookie string.
      &lt;/p&gt;
      &lt;p&gt;
        Here's how you use it:
      &lt;/p&gt;
      &lt;div class='gist'&gt;
        &lt;div class='gist-file'&gt;
          &lt;div class='gist-data gist-syntax'&gt;
            &lt;div class='gist-highlight'&gt;
              &lt;pre&gt;&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^I am signed in as (.+)$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;vi&quot;&gt;@current_user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_by_nickname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;session_generator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SessionCookieGenerator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;session_generator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@current_user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_cookie&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session_generator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_s&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      
      &lt;p&gt;
        &lt;small&gt;
          &lt;em&gt;The above snippet assumes that a user is signed in via session[:user_id].&lt;/em&gt;
        &lt;/small&gt;
      &lt;/p&gt;
      &lt;p&gt;
        Now you can sign in without going through the sign-in form; just make sure at least one test actually goes through the full sign-in process.
      &lt;/p&gt;
    </content>
  </entry>
  <entry>
    <id>tag:turriate.com,2011-02-02:/articles/2011/feb/testing-twitter-oauth-with-real-data/</id>
    <title type='html'>Testing Twitter OAuth with real data</title>
    <published>2011-02-02T03:48:00-08:00</published>
    <link href='http://turriate.com/articles/2011/feb/testing-twitter-oauth-with-real-data/' rel='alternative' />
    <summary type='html'>Ensure that your application&amp;rsquo;s Twitter integration works by having your test suite actually sign in every so often.</summary>
    <content type='html'>
      &lt;p&gt;
        The most popular way to test Twitter OAuth is by having your test suite run against fake Twitter responses provided by gems like &lt;a href=&quot;https://github.com/chrisk/fakeweb&quot;&gt;FakeWeb&lt;/a&gt;. The benefits of this approach are fast tests which never leave your network and predictable tests - once they're passing they'll never fail. Anyone will tell you that I love a fast test suite, which is why I built &lt;a href=&quot;http://github.com/sandro/specjour&quot;&gt;specjour&lt;/a&gt; but in the real world, Twitter is not very predictable. For my own sanity, my integration tests must reflect reality as close as possible by following the same path the user follows. So if the user needs to be signed in to Twitter in order to register for my application, my test suite needs to reflect that.
      &lt;/p&gt;
      &lt;h3&gt;Enter EphemeralResponse&lt;/h3&gt;
      &lt;p&gt;
        I built &lt;a href=&quot;http://github.com/sandro/ephemeral_response&quot;&gt;ephemeral_response&lt;/a&gt; to satisfy my need for fast tests that reflect reality. EphemeralResponse captures the response of the first request and returns that response for all subsequent requests until the cached response expires, 24 hours later by default. This means that you'll test against an external service with real data once a day. There are a handful of gems that do this web caching dance but ephermal_response has zero dependencies and is the simplest to set up, just call
        &lt;code&gt;EphemeralResponse.activate&lt;/code&gt;&lt;span&gt;:&lt;/span&gt;&lt;/p&gt;
      &lt;div class='gist'&gt;
        &lt;div class='gist-file'&gt;
          &lt;div class='gist-data gist-syntax'&gt;
            &lt;div class='gist-highlight'&gt;
              &lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;benchmark&amp;#39;&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;ephemeral_response&amp;#39;&lt;/span&gt;&amp;#x000A;&amp;#x000A;&lt;span class=&quot;no&quot;&gt;EphemeralResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activate&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# begin capturing responses&lt;/span&gt;&amp;#x000A;&amp;#x000A;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;realtime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Net&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;example.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&amp;#x000A;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;44242906570435&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;# First request hits the server&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;0006&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;89029693603516&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Second request uses the cached response&lt;/span&gt;&lt;/pre&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      
      &lt;h3&gt;Using Capybara to test Twitter OAuth&lt;/h3&gt;
      &lt;p&gt;
        Our test depends on OmniAuth, Cucumber, Capybara, the capybara-mechanize driver, and ephemeral_response. We have to make real requests to Twitter which means we can't use capybara's rack-test driver. Thankfully, jeroenvandijk created &lt;a href=&quot;https://github.com/jeroenvandijk/capybara-mechanize&quot;&gt;capybara-mechanize&lt;/a&gt; which allows us to make external web requests while saving and sending cookies along the way.
      &lt;/p&gt;
      &lt;p&gt;
        First, we modify the
        &lt;code&gt;remote?&lt;/code&gt;
        method in the capybara-mechanize driver so that we don't have to continually switch Capybara.app_host. While we're here, let's also set Capybara.default_host.
      &lt;/p&gt;
      &lt;div class='gist'&gt;
        &lt;div class='gist-file'&gt;
          &lt;div class='gist-data gist-syntax'&gt;
            &lt;div class='gist-highlight'&gt;
              &lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;# features/support/capybara-mechanize.rb&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;no&quot;&gt;Capybara&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default_host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;example.com&amp;#39;&lt;/span&gt;&amp;#x000A;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Capybara&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Driver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Mechanize&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;original_remote?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remote?&lt;/span&gt;&amp;#x000A;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;remote?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relative?&lt;/span&gt;&amp;#x000A;      &lt;span class=&quot;n&quot;&gt;last_request_remote?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remote_response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Location&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&amp;#x000A;      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Capybara&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Capybara&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default_host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      
      &lt;p&gt;
        Now let's set up EphemeralResponse
      &lt;/p&gt;
      &lt;div class='gist'&gt;
        &lt;div class='gist-file'&gt;
          &lt;div class='gist-data gist-syntax'&gt;
            &lt;div class='gist-highlight'&gt;
              &lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;# features/support/ephemeral_response.rb&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;no&quot;&gt;EphemeralResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activate&lt;/span&gt;&lt;/pre&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      
      &lt;p&gt;
        Finally, the messy Cucumber step to sign in with Twitter. Refactor as necessary:
      &lt;/p&gt;
      &lt;div class='gist'&gt;
        &lt;div class='gist-file'&gt;
          &lt;div class='gist-data gist-syntax'&gt;
            &lt;div class='gist-highlight'&gt;
              &lt;pre&gt;&lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^I sign in$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;no&quot;&gt;Capybara&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_driver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:mechanize&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;c1&quot;&gt;# Sign in to Twitter&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;visit&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;http://twitter.com/sessions/new&amp;#39;&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;fill_in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Username or email&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:with&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;my_app_test_account&amp;#39;&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;fill_in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Password&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:with&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;my_app_test_account_password&amp;#39;&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;click_button&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Sign In&amp;#39;&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;has_content?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Sign out&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Sign in failed! Manually sign in to reset the captcha&amp;quot;&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&amp;#x000A;  &lt;span class=&quot;c1&quot;&gt;# Sign in to app&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/auth/twitter&amp;#39;&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;follow_redirect!&lt;/span&gt;&amp;#x000A;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;redirect?&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;follow_redirect!&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;c1&quot;&gt;# Authorize the app on twitter&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;n&quot;&gt;click_button&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Allow&amp;quot;&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;n&quot;&gt;click_link&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;click here&amp;#39;&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;follow_redirect!&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;have_content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Sign out&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      
      &lt;p&gt;
        That should be it. Run the step and check your
        &lt;code&gt;spec/fixtures/ephemeral_response&lt;/code&gt;
        directory to see the cached web responses. Try disconnecting from the network and re-running the test, it should still pass.
      &lt;/p&gt;
      &lt;p&gt;
        Cool, we've covered the registration/sign in portion of the site by actually connecting to Twitter. Next time I'll show off my hypocrisy by &lt;a href=&quot;/articles/2011/feb/how-to-generate-signed-rails-session-cookie/&quot;&gt;faking Rails sessions&lt;/a&gt; so we can skip the entire OAuth/ephemeral_response dance when signing in throughout the rest of the test suite.
      &lt;/p&gt;
    </content>
  </entry>
  <entry>
    <id>tag:turriate.com,2010-12-30:/articles/2010/dec/jungle-is-greener-on-other-side/</id>
    <title type='html'>The jungle is greener on the other side</title>
    <published>2010-12-30T18:54:00-08:00</published>
    <link href='http://turriate.com/articles/2010/dec/jungle-is-greener-on-other-side/' rel='alternative' />
    <summary type='html'>Montañita wouldn&amp;rsquo;t have me over the Christmas holiday so I moved up the road to Ayampe.</summary>
    <content type='html'>
      &lt;h3&gt;Lingering&lt;/h3&gt;
      &lt;p&gt;
        Christmas and New Years have been the biggest obstacles of this trip. Where will I spend those two holidays and will I find an adequate room for a decent price? My reservation with my hostel in Montañita ended on the 23rd, just before prices increase. This left me with the option of either finding another hostel in town, spending Christmas in a known area with bus loads of people, or trying my luck in another city. I heard the nearby village Ayampe, my next destination, is fairly empty which could make Christmas a lonely occasion. I visited five different hostels in Montañita looking for a room but they were all either booked or otherwise undesirable. Time to move on.
      &lt;/p&gt;
      &lt;p&gt;
        Leaving Montañita was much easier than I anticipated. I knew of the bus stop in town but had no idea of the schedule, nor which bus to take or where to purchase tickets. There is no bus terminal. The residents told me buses come every half hour and that I need to take the green bus, not the blue bus. I checked out of my hostel and within minutes on my walk into town I saw an aqua colored bus approaching. Hopefully this was the &quot;green&quot; bus. I hailed it down, it slowed and I hopped in (literally). I'm sure I received the gringo tax as I told the fare collector &quot;Ayampe&quot; and forked over my two dollars. The surf had been flat for the past week so catching the bus and riding up the coastline through bits of lush, green jungle was the most exiting thing I had done all week. Getting out of Montañita felt good. Change feels good.
      &lt;/p&gt;
      &lt;p&gt;
        &lt;a href=&quot;http://www.flickr.com/photos/untorn/5291883494/&quot; title=&quot;Montañita Sunset by untorn, on Flickr&quot;&gt;&lt;img src=&quot;http://farm6.static.flickr.com/5207/5291883494_9b9be906b9.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;Montañita Sunset&quot; /&gt;&lt;/a&gt;
      &lt;/p&gt;
      &lt;h3&gt;Up the road to Ayampe&lt;/h3&gt;
      &lt;p&gt;
        Yet again, my bus stop was a dirt road off the side of the highway but I mostly expected that and shrugged it off as I set out to find &lt;a href=&quot;http://surflabuenavida.com/&quot;&gt;Bungalows La Buena Vida&lt;/a&gt;; a hostel I read about on &lt;a href=&quot;http://www.wannasurf.com/spot/South_America/Ecuador/ayampe/index.html&quot;&gt;wannasurf&lt;/a&gt;. I saw a sign for it from the bus as we approached Ayampe. It was perched on a hill above the village but I was left off at the base so I decided to walk to the ocean and through the streets before heading up to the hostel.
      &lt;/p&gt;
      &lt;p&gt;
        The beach was mostly empty and covered in stones. This beach was not meant for sunbathers, volleyball players, kite-flyers, nor any recreationalists; the beach belongs to the fishermen, and to them it is simply a transition to the focal point of Ayampe, the sea. I passed one tiny minimart with caged-window service, one restaurant which looked abandoned at lunch time, and half a dozen hostels on my walk. How is it that hostels are the biggest business here? I assume Ayampe attracts people who want to escape. People who want to do their vacation justice by really vacating.
      &lt;/p&gt;
      &lt;p&gt;
        &lt;a href=&quot;http://www.flickr.com/photos/untorn/5287312226/&quot; title=&quot;Ayampe by untorn, on Flickr&quot;&gt;&lt;img src=&quot;http://farm6.static.flickr.com/5204/5287312226_32d16eefa1.jpg&quot; width=&quot;500&quot; height=&quot;374&quot; alt=&quot;Ayampe&quot; /&gt;&lt;/a&gt;
      &lt;/p&gt;
      &lt;p&gt;
        During my walk I ascended a dirt path that I hoped would lead to the hostel but it turned out being a residential driveway with no outlet. With my thirty pound pack and the unnecessary ascent, it didn't take long before I started sweating. It happened gradually enough that I could have stopped walking to control it but I thought &quot;why not sweat?&quot; and invited it along. Out it poured, making me feel nice and cool but I shocked the owner of the hostel upon arrival who was quick to offer me a drink and encouraged me to drop my pack.
      &lt;/p&gt;
      &lt;p&gt;
        The first thing I noticed when walking into La Buena Vida was a disparate set of young, muscular dudes eating breakfast at around noon, glued to the surf film playing on the television:
        &lt;strong&gt;surfers!&lt;/strong&gt;
        It's not that I didn't see and meet plenty of surfers in Montañita, I did&amp;hellip; but there was something different about this group. They were independents. Lone travelers like myself, some of them traveling through Central and South America for months, with no itinerary and no known return date, just looking for surf. These guys were real surfers and they were all concentrated at this one hostel, or so I thought. Actually, they were staying in different hostels throughout the area but came to La Buena Vida to indulge in the all-day breakfast menu, endless surf videos, and good company provided by business owners Keith and Marilyn Keller.
      &lt;/p&gt;
      &lt;p&gt;
        I was able to secure a room for three nights, Christmas tax included of course, but this place was worth it. La Buena Vida was a definite upgrade when compared to my last hostel. I was extremely happy to have consistent water pressure, dependable hot water, shelving for clothes, a quiet yet powerful, multi-variable speed fan, airtight screens over the windows, fast wifi, an all day snack bar with T.V. and owners who fully participate in the running of their business by cleaning up behind their employees when something was missed or serving drinks during the nightly sports game. In Montañita, I didn't spent much time in my hostel, but the environment created at La Buena Vida made you not want to leave, even better, people flocked here from other hostels just to hang out.
      &lt;/p&gt;
      &lt;p&gt;
        &lt;a href=&quot;http://www.flickr.com/photos/untorn/5291266881/&quot; title=&quot;Bungalows La Buena Vida from the road by untorn, on Flickr&quot;&gt;&lt;img src=&quot;http://farm6.static.flickr.com/5082/5291266881_45963363d9.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;Bungalows La Buena Vida from the road&quot; /&gt;&lt;/a&gt;
      &lt;/p&gt;
      &lt;p&gt;
        Although we were in the middle of a flat spell there was a small bump in the swell the day I arrived so I rented a board and went surfing with Keith for about two hours. I caught some fun lefts and had an epiphany: some waves are meant to be surfed, want to be surfed. It felt good to finally have sore shoulders.
      &lt;/p&gt;
      &lt;p&gt;
        The jungle really does look greener in Ayampe. Sure, there's more of it and it's closer - right on top of you even. When looking back from the ocean, it's all you see and it makes you wonder how you'll ever find your way back. Ayampe is greener both literally, and figuratively. Everything was much more to my liking here. It encapsulates what I envision a surf trip to be. Good, vigorous surfing, fresh fish, and lazy afternoons relaxing in hammocks watching the sun set. This place felt so &quot;right&quot; that I may visit it once more before going home.
      &lt;/p&gt;
      &lt;p&gt;
        La Buena Vida was an ideal place to spend Christmas. The small population in Ayampe means that anyone who hangs around for a short period automatically becomes family. You may not interact with everyone but even strangers are familiar. The owners prepared a wonderful, intimate Christmas dinner for their guests featuring a delicious turkey, Christmas carols and later, a walk into town to &lt;a href=&quot;http://www.youtube.com/watch?v=dDQUt3Nasbs&amp;hd=1&quot;&gt;celebrate&lt;/a&gt; with the residents.
      &lt;/p&gt;
      &lt;p&gt;
        &lt;img height='333' src='http://sphotos.ak.fbcdn.net/hphotos-ak-snc4/hs1398.snc4/165095_1457869178823_1596202111_30996635_6773144_n.jpg' width='500' /&gt;
        &lt;br /&gt;
        &lt;small&gt;
          &lt;em&gt;
            Photo of fellow guest Michael, owners Marilyn and Keith Keller, their son Tyler, and myself.
          &lt;/em&gt;
        &lt;/small&gt;
      &lt;/p&gt;
      &lt;p&gt;
        The surf is supposed to be small for another few days so I'm heading to the nearby city Puerto Lopez for a day of scuba diving.
      &lt;/p&gt;
    </content>
  </entry>
  <entry>
    <id>tag:turriate.com,2010-12-16:/articles/2010/dec/finally-reached-ecuadors-coast/</id>
    <title type='html'>Finally reached Ecuador&amp;rsquo;s coast</title>
    <published>2010-12-16T15:19:00-08:00</published>
    <link href='http://turriate.com/articles/2010/dec/finally-reached-ecuadors-coast/' rel='alternative' />
    <summary type='html'>After a week delay in Cuenca, I've finally reached the first destination of my surf trip - Montañita, Ecuador.</summary>
    <content type='html'>
      &lt;h3&gt;Cuenca&lt;/h3&gt;
      &lt;p&gt;
        Soon after I purchased my plane ticket to Ecuador, my parents followed suit. They've been researching the country for retirement opportunities, specifically, the city of Cuenca. Ironically, because of work constraints, they would only be in Ecuador for 7 days so I decided to spend my first week joining them in Cuenca.
      &lt;/p&gt;
      &lt;p&gt;
        Cuenca is the cleanest South American city I've visited, yet it is still distinctly South American. My dad and I had a hunch that this would be the case but my mom insisted that &quot;unless you go, you'll never know.&quot; She had hoped to find an affordable American suburbia next to a quiet city that could cater to all of her needs. During our stay we discovered that the nicer homes were often found on dirt roads, neighbored by small plots of crops or abandoned shacks; and the city, while sometimes quaint, was bustling with human and automotive traffic. Cuenca just won't work for them.
      &lt;/p&gt;
      &lt;table style=&quot;width:auto;&quot;&gt;&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;http://picasaweb.google.com/lh/photo/ib0zezdQ20moCmF6a_6k0g?feat=embedwebsite&quot;&gt;&lt;img src=&quot;http://lh4.ggpht.com/_gXFke_KBjpw/TP7MpGk9kfI/AAAAAAAAGcY/kqJpciQPn5A/s500/IMG_0420.JPG&quot; height=&quot;375&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;font-family:arial,sans-serif; font-size:11px; text-align:right&quot;&gt;From &lt;a href=&quot;http://picasaweb.google.com/ewokie.t/Ecuador2010?feat=embedwebsite&quot;&gt;Ecuador 2010&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
      &lt;p&gt;
        We ended our stay in Cuenca with a trip to the nearby Cajas national park. We spent a few hours in the area hiking around one of its 200+ lakes, visiting an area where the Virgin Mary was spotted, and eating the local trout in a classy hotel/lodge. We moved quite slowly on our hike as every step yielded another discovery of alien fauna. I was incredibly surprised at how observant and well entertained my parents were by their surroundings. Though we didn't cover much ground, we saw plenty.
      &lt;/p&gt;
      &lt;p&gt;
        This was my second time in or around a cloud forest and it really is something special. It slows you down and shuts you up. It reminds you to be still and yield. You're swallowed up by its quiet, cool, foggy yet
        &lt;strong&gt;green&lt;/strong&gt;
        atmosphere. The peace found in the cloud forest is wholly different than the peace found after a long surf session or an undistracted savasana, or the perfect refactoring. The trip to the cloud forest was exactly the change we needed from the crowded and loud city of Cuenca. It was the highlight of my parent's trip.
      &lt;/p&gt;
      &lt;h3&gt;Guayaquil&lt;/h3&gt;
      &lt;p&gt;
        I left for Guayaquil on the 8th of December to begin my trip to Montañita. I was once again able to gaze at the cloud forest on my way to the city. The ride was quite comfortable, though it got chilly as we reached the summit, requiring a light sweater. As we began descending I started noticing palm trees growing on the mountainside as the clouds dissipated. Then I could smell it&amp;hellip; the smell of humidity! I tossed my sweater and for a moment I thought I saw the coastline which brought me way more joy than expected.  It wasn't long before I could feel the humidity, which makes my dry skin happy, and then I was drowning in it. Welcome to Guayaquil.
      &lt;/p&gt;
      &lt;a href=&quot;http://www.flickr.com/photos/untorn/5266478757/&quot; title=&quot;La Rotunda on Malecón 2000 in Guayaquil by untorn, on Flickr&quot;&gt;&lt;img src=&quot;http://farm6.static.flickr.com/5161/5266478757_9994c73d25.jpg&quot; width=&quot;500&quot; height=&quot;374&quot; alt=&quot;La Rotunda on Malecón 2000 in Guayaquil&quot; /&gt;&lt;/a&gt;
      &lt;p&gt;
        Guayaquil reminds me of Lima with its crowded, gritty feel. Approaching the Guayas river on the Malecón 2000 presents an interesting illusion. The river has enough current and debris that it actually makes you feel like you're on a boat and the whole city is traveling up the river. There's so much human traffic on the large pedestrian streets (9th of October and 10th of August) leading from the Malecón to the central park that navigating without running into anyone is like a fun game. I had an excellent tomatillo milk shake while strolling through the city. The tomatillos here are not the same as the Mexican green husked tomatillo. It has a sweet, earthy and bitter flavor that I've never tasted before which bears further research. Walking toward Iguana Park I thought I'd be lucky if I could spot a lizard in a tree and that's exactly what happened upon arrival. Lucky me. Then, as I entered the park I was shocked to see one iguana after another, whole groups of iguanas in fact, with no fear of the humans walking past. What fun!
      &lt;/p&gt;
      &lt;iframe title=&quot;YouTube video player&quot; class=&quot;youtube-player&quot; type=&quot;text/html&quot; width=&quot;560&quot; height=&quot;345&quot; src=&quot;http://www.youtube.com/embed/qN7oocMvEWM?rel=0&amp;amp;hd=1&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
      &lt;p&gt;
        A few weeks ago I read about a traveler on the coast waking up multiple times in the middle of the night taking cold showers just to stay cool. I thought for sure that was an exaggeration but there I was, showering off before bed without reclothing. Yep, I was hanging out naked, considering whether I really wanted to catch the hot 1 p.m. bus the following day... Nope! I walked through most of the city in one afternoon and was ready to get moving. Instead, I grabbed the 5:30 a.m. bus to Montañita the next morning.
      &lt;/p&gt;
      &lt;h3&gt;Montañita&lt;/h3&gt;
      &lt;p&gt;
        The bus driver called out &quot;Montañita&quot; as he pulled over on the highway. No bus terminal? I was left off on the side of the road. There were only a few small tiendas opposite me and some local handing me business cards for his hostel. He had some food item on his lip and I could smell the alcohol around him. I wanted to ditch him so I instinctively began walking away from the little bit of visible civilization. Turns out, I was walking in the right direction. He helped me out, telling me to cross the nearby bridge, walk down to the beach and head north for a mile to get to my hostel. A mile seemed a bit far but I enjoy walking on the beach and didn't have any better alternatives so I followed his directions. The beach immediately felt familiar and though there wasn't much surf, I was glad to finally see the ocean.
      &lt;/p&gt;
      &lt;p&gt;
        &lt;a href=&quot;http://www.flickr.com/photos/untorn/5263721105/&quot; title=&quot;Beach behind me, jungle in front of me by untorn, on Flickr&quot;&gt;&lt;img src=&quot;http://farm6.static.flickr.com/5162/5263721105_c64a200172.jpg&quot; width=&quot;500&quot; height=&quot;374&quot; alt=&quot;Beach behind me, jungle in front of me&quot; /&gt;&lt;/a&gt;
      &lt;/p&gt;
      &lt;p&gt;
        I got settled in my hostel and because the surf was small, I immediately went for a barefoot &lt;a href=&quot;http://runkeeper.com/user/sandrot/activity/21062273&quot;&gt;run&lt;/a&gt;. It was, as I now know, uncharacteristically hot and sunny that afternoon. It was my first time running in three weeks and it showed. I ended the run panting, with newly blistered toes. A dip in the ocean temporarily relieved the pain. I bodysurfed the first wave that approached me with ease; kudos Pacific Ocean. I often forget just how good the ocean feels. Within the first couple of hours in Montañita I felt like I had succeeded in extending my Floridian summer. A quick outdoor shower, air drying and relaxing shirtless made me feel totally at home in this foreign land. My only concern was how I would fill the long days ahead of me?
      &lt;/p&gt;
      &lt;p&gt;
        Generally, I've been filling my days with surfing, running, futbol, frisbee, reading, journaling, &lt;a href=&quot;http://twitter.com/#!/sandrot/status/14057187332587520&quot;&gt;fighting mosquitos&lt;/a&gt;, playing the hot water dance (AKA: trying not to get scalded in the shower), learning to enjoy cold showers, drinking my calories (love the fresh fruit juice here), eating mediocre food, preparing &lt;a href=&quot;http://www.flickr.com/photos/untorn/5248231010/in/set-72157625560205344/&quot;&gt;interesting salads&lt;/a&gt;, buying friendships with the locals (think conversation with ceviche guy, not prostitution), aimlessly walking around town, and taking a siesta or two.
      &lt;/p&gt;
      &lt;p&gt;
        &lt;a href=&quot;http://www.flickr.com/photos/untorn/5269226354/&quot; title=&quot;Swell by untorn, on Flickr&quot;&gt;&lt;img src=&quot;http://farm6.static.flickr.com/5006/5269226354_8508cde160.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;Swell&quot; /&gt;&lt;/a&gt;
      &lt;/p&gt;
      &lt;p&gt;
        Balsa boards are really popular among both the tourists and the locals. I'm renting a 8'2&quot; balsa long board which I actually find more maneuverable than my POS at home. It recovers well when I misplace my balance, kinda like it knows the wave better than I do. There's beach break and a point break and my hostel is located just outside of the point break. There's a decent wave at the point and apparently, it's building. Usually, the wave is mushy and forgiving. Most days the locals have to really work it just to get a short ride. The conditions are reminiscent of my Florida beach break in that there's nothing to fear, the waves are tame. It's supposed to get really good but also really crowded in January/February. It's already crowded enough with half a dozen to a dozen surfers all paddling for the same wave at the point. I feel especially self-conscious on my long balsa board because if I screw up within the tight group and send my board flying, someone's liable to get a concussion. Surfing has been fun but the water is a little chilly and the constantly overcast skies don't help. The water definitely feels like the low 70's if not high 60's.
      &lt;/p&gt;
      &lt;p&gt;
        The locals are warm and friendly. They've learned the art of speaking Spanish slowly and rephrasing with patience which is wonderful because my Spanish isn't very good. They have a special greeting which comes with its own
        &lt;strong&gt;HANDSHAKE!&lt;/strong&gt;
        Everyone says &quot;Todo Bien?&quot; (All good?) with a slow-moving open-hand slap, which looks like it should be a handshake, followed by a fist bump. It's nice to greet and be greeted like a local.
      &lt;/p&gt;
      &lt;p&gt;
        Every conversation I have down here, with a local or otherwise, always gravitates toward the chicas quite suddenly. Conversations typically unravel with &quot;when did you arrive&quot;, &quot;how long are you staying&quot;, &quot;where are you from&quot;, and &quot;where's your chica&quot;? &quot;Oh, don't have one? Go pick one (two or three) up at the Hola Ola Cafe or at the Christmas Festival, or at The Natural Bamboo bar, or at the nearby yoga class.&quot; To these guys and gals, it's like stopping by the pet store on your way home to grab a pet fish for some light entertainment. It's all so natural and common that I really wish I could identify with the sentiment. I mostly just chuckle and change the subject to the local fruits. The machismo is strong here.
      &lt;/p&gt;
      &lt;p&gt;
        I'm not in love with my accommodations but like most establishments in Montañita, it's run by a young surfer crew. This crew is especially green though. They'll get their schooling in the coming months as the high season really kicks into gear. I think a mentor, someone who really understands service, could go a long way for them but I doubt they'd accept one; they're independent souls, learning on their own.
      &lt;/p&gt;
      &lt;p&gt;
        &lt;a href=&quot;http://www.flickr.com/photos/untorn/5269226032/&quot; title=&quot;My room is the bottom left by untorn, on Flickr&quot;&gt;&lt;img src=&quot;http://farm6.static.flickr.com/5284/5269226032_e1e4b590ed.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;My room is the bottom left&quot; /&gt;&lt;/a&gt;
      &lt;/p&gt;
      &lt;p&gt;
        I'll be in Montañita for another week, then I'm headed up the coast, stopping at various small towns along the way to Canoa. The extremely close town of Ayampe is the first (and only) on my list. Not sure how the timing will work out but I'd like to get a few days in Canoa before leaving for Quito, then home.
      &lt;/p&gt;
    </content>
  </entry>
  <entry>
    <id>tag:turriate.com,2010-11-30:/articles/2010/nov/heres-to-taking-the-next-ten-months-off/</id>
    <title type='html'>Here’s to taking the next 10 months off</title>
    <published>2010-11-30T13:49:00-08:00</published>
    <link href='http://turriate.com/articles/2010/nov/heres-to-taking-the-next-ten-months-off/' rel='alternative' />
    <summary type='html'>My life as a Rocketeer has ended and my life as an active, traveling hacker has begun.</summary>
    <content type='html'>
      &lt;p&gt;
        For the last two years and eight months I've been working for
        &lt;a href=&quot;http://hashrocket.com&quot;&gt;Hashrocket&lt;/a&gt;
        as a Ruby on Rails consultant. I'm honored to have worked with the most intelligent, funny, compassionate, and generous people I've ever known. We created a family at Hashrocket which made leaving fairly difficult but after almost 3 years my passion has started to wane and without passion, my job would have just become work.
      &lt;/p&gt;
      &lt;p&gt;
        &lt;a href=&quot;http://www.flickr.com/photos/allanbranch/2256113939/&quot; title=&quot;Hashrocket by LessAllan, on Flickr&quot;&gt;&lt;img src=&quot;http://farm3.static.flickr.com/2382/2256113939_612957b1c2.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Hashrocket&quot; /&gt;&lt;/a&gt;
      &lt;/p&gt;
      &lt;p&gt;
        I resigned on November 5th to pursue a more active lifestyle and to hack on whatever excites me. I'll still be contributing to my open source projects including &lt;a href=&quot;http://github.com/sandro/specjour&quot;&gt;Specjour&lt;/a&gt;, &lt;a href=&quot;http://github.com/sandro/ephemeral_response&quot;&gt;Ephemeral Response&lt;/a&gt; and &lt;a href=&quot;http://github.com/jnunemaker/httparty&quot;&gt;HTTParty&lt;/a&gt;.
      &lt;/p&gt;
      &lt;p&gt;&lt;/p&gt;
      &lt;h3&gt;Saw a few places&lt;/h3&gt;
      &lt;p&gt;
        I've been to a handful of places over the past few years. Most were Hashrocket related. Let me recount some experiences for each.
        &lt;ul&gt;
          &lt;li&gt;Orlando, FL&lt;/li&gt;
          &lt;li&gt;Portland, OR&lt;/li&gt;
          &lt;li&gt;Sunnyvale, CA&lt;/li&gt;
          &lt;li&gt;Lima, Peru&lt;/li&gt;
          &lt;li&gt;Norcross/Duluth, GA&lt;/li&gt;
          &lt;li&gt;Southern/Western Europe&lt;/li&gt;
          &lt;li&gt;Nashville, TN&lt;/li&gt;
          &lt;li&gt;Keystone, CO&lt;/li&gt;
          &lt;li&gt;Baltimore, MD&lt;/li&gt;
          &lt;li&gt;New Orleans, LA&lt;/li&gt;
        &lt;/ul&gt;
        &lt;iframe width=&quot;500&quot; height=&quot;350&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; src=&quot;http://maps.google.com/maps/ms?ie=UTF8&amp;amp;hl=en&amp;amp;msa=0&amp;amp;msid=103372833485500058354.0004963fa61d75bd64d24&amp;amp;ll=25.482951,-52.382812&amp;amp;spn=98.795826,175.78125&amp;amp;z=2&amp;amp;output=embed&quot;&gt;&lt;/iframe&gt;
      &lt;/p&gt;
      &lt;h3&gt;2008&lt;/h3&gt;
      &lt;p&gt;
        In February, on my second day on the job, the then-infant Hashrocket crew made its way down to Orlando for Acts as Conference. What an excellent way to start my new job. A few months later in May, Hashrocket sponsored a trip to Portland, OR for Railsconf. I'd say at least 12 of us went, stomping about in our &quot;Moon Man&quot; or &quot;Ghetto Blaster&quot; T-shirts. In September, I hacked on an ActiveRecord adapter for YQL at the Yahoo Hack Day in Sunnyvale, CA with my old friend &lt;a href=&quot;http://risingcode.com/&quot;&gt;Jon Bardin&lt;/a&gt;. That November, my dad and I buried my grandfather, &lt;a href=&quot;http://troy.turriate.com/sastre.html&quot;&gt;Oscar Turriate&lt;/a&gt;, next to his first wife, Gliceria Loayza, in Lima, Peru. He was an excellent man. Strong, consistent, and quiet; worthy of all the respect and love he'd gained in life.
      &lt;/p&gt;
      &lt;p&gt;
        &lt;a href=&quot;http://www.flickr.com/photos/untorn/5219887601/&quot; title=&quot;IMG_0992.JPG by untorn, on Flickr&quot;&gt;&lt;img src=&quot;http://farm5.static.flickr.com/4091/5219887601_84bb8459c3.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;IMG_0992.JPG&quot; /&gt;&lt;/a&gt;
      &lt;/p&gt;
      &lt;h3&gt;2009&lt;/h3&gt;
      &lt;p&gt;
        In February I drove &lt;a href=&quot;http://twitter.com/l4rk&quot;&gt;@l4rk&lt;/a&gt; down to Acts of Conference in Orlando, FL while going over &lt;a href=&quot;http://aac2009.confreaks.com/06-feb-2009-13-30-testing-as-communication-jon-larkowski.html&quot;&gt;his talk&lt;/a&gt; en route. In April, a handful of us did on-site pair programming with with the &lt;a href=&quot;http://hashrocket.com/work/view/primedia-inc/&quot;&gt;Primedia&lt;/a&gt; team in Norcross, GA. In May, &lt;a href=&quot;http://twitter.com/l4rk&quot;&gt;@l4rk&lt;/a&gt;, &lt;a href=&quot;http://twitter.com/cgrusden&quot;&gt;@cgrusden&lt;/a&gt; and I returned to Orlando to help &lt;a href=&quot;http://twitter.com/cory_foy&quot;&gt;@cory_foy&lt;/a&gt; troubleshoot Rails installations on Windows during the &lt;a href=&quot;http://vimeo.com/5714765&quot;&gt;Day of Ruby&lt;/a&gt;. Later in May I &lt;a href=&quot;http://www.flickr.com/photos/untorn/sets/72157625296991909/&quot;&gt;cruised&lt;/a&gt; around Southern and Western Europe with my folks. In August, &lt;a href=&quot;javascript:void(0);&quot;&gt;@l4rk&lt;/a&gt; and I picked up Snow Leopard discs in the middle of the Ruby Hoedown in Nashville, TN. I finished the year off snowboarding through the Christmas holiday in Keystone, CO.
      &lt;/p&gt;
      &lt;p&gt;
        &lt;a href=&quot;http://www.flickr.com/photos/untorn/5220487458/&quot; title=&quot;Untitled by untorn, on Flickr&quot;&gt;&lt;img src=&quot;http://farm5.static.flickr.com/4085/5220487458_d5e7394532.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
      &lt;/p&gt;
      &lt;h3&gt;2010&lt;/h3&gt;
      &lt;p&gt;
        In June, some alcohol-craving Rubyconf attendees and I shared moonshine with
        &lt;a href=&quot;http://twitter.com/techpickles&quot;&gt;@techpickles&lt;/a&gt;
        late one night in Baltimore, MD. That August, my dad and I took our bi-annual &lt;a href=&quot;http://www.flickr.com/photos/untorn/sets/72157625296903823/&quot;&gt;trip to Peru&lt;/a&gt; to hang out with the family and see la
        &lt;a href=&quot;http://en.wikipedia.org/wiki/Cordillera_Blanca&quot;&gt;Cordillera Blanca&lt;/a&gt;. This mountain range rekindled my extreme desire to learn how to rock climb and to live a simpler life among the hills. Then in late September I ate my first Korean BBQ dinner in Duluth, GA with
        &lt;a href=&quot;http://twitter.com/leshill&quot;&gt;@leshill&lt;/a&gt; and &lt;a href=&quot;http://twitter.com/bestplayer&quot;&gt;@bestplayer&lt;/a&gt;. It took a few years but in November, I finally made it to my first RubyConf in New Orleans, LA. I consecutively drank and smoked more than ever before at that conference. By the end of the week, walking into my first session at noon with a drink in my hand seemed absolutely normal. I also ran my first &lt;a href=&quot;http://plixi.com/p/56467285&quot;&gt;10K&lt;/a&gt; through &lt;a href=&quot;http://rubyconf5k.com/&quot;&gt;rubyconf5k&lt;/a&gt; and watched
        &lt;a href=&quot;http://twitter.com/voxdolo&quot;&gt;@voxdolo&lt;/a&gt; and &lt;a href=&quot;http://twitter.com/rbxbx&quot;&gt;@rbxbx&lt;/a&gt; get some stylish
        &lt;a href=&quot;http://www.youtube.com/watch?v=XBdIQJ0B5SI&quot;&gt;tattoos&lt;/a&gt;.
      &lt;/p&gt;
      &lt;p&gt;
        &lt;a href=&quot;http://www.flickr.com/photos/untorn/5188938402/&quot; title=&quot;Cordillera Blanca y Cordillera Negra by untorn, on Flickr&quot;&gt;&lt;img src=&quot;http://farm5.static.flickr.com/4084/5188938402_3fcc97a51d.jpg&quot; width=&quot;500&quot; height=&quot;374&quot; alt=&quot;Cordillera Blanca y Cordillera Negra&quot; /&gt;&lt;/a&gt;
      &lt;/p&gt;
      &lt;h3&gt;Looking ahead&lt;/h3&gt;
      &lt;p&gt;
        I'll be leaving for Ecuador tomorrow on December 1st. I'm going down there for a month-long surf trip. Towards the end of my last trip to Peru I got an itch to explore the Northern beaches but we ran out of time. Of course, Peru's North is Ecuador's South, so I'll try to scratch my itch in Ecuador. Also, Ecuador is on the equator which makes it ideal for &quot;skinning it&quot; in December. I don't have any accommodations planned but the goal is to see most of the coast. I'll just keep moving north and south until I find a place that feels right.
      &lt;/p&gt;
      &lt;p&gt;
        I return to Jacksonville in January, staying with my parents for two weeks. Then I'm off to Breckenridge, CO for a month of snowboarding, and possibly some indoor swimming/rock climbing.
      &lt;/p&gt;
      &lt;p&gt;
        I'll be back in Jacksonville in late February with plans to move to Boulder, CO by April. I'll use my time in Boulder to learn how to rock climb and to train for my first triathlon. I'm not sure how it happened but all of a sudden, I'm interested in exercise and the challenge of a triathlon seems like a good fit for me. If I can make it to August, then that'll be ten months without a proper job. We'll see.
      &lt;/p&gt;
    </content>
  </entry>
  <entry>
    <id>tag:turriate.com,2010-06-27:/articles/2010/jun/silencing-forked-processes/</id>
    <title type='html'>Silencing a Forked Process</title>
    <published>2010-06-27T13:57:00-07:00</published>
    <link href='http://turriate.com/articles/2010/jun/silencing-forked-processes/' rel='alternative' />
    <summary type='html'>How to provide the concurrency of a forked process without spamming the parent process' stdout.</summary>
    <content type='html'>
      &lt;p&gt;
        I'm adding a feature to
        &lt;a href=&quot;http://github.com/sandro/specjour&quot;&gt;specjour&lt;/a&gt;
        which starts a manager in a subprocess when Bonjour fails to find any
        listening managers. This will enable the dream of specjour running the full
        suite with just one command instead of two. The first problem I ran into while
        working on this feature was that all of the debugging information that a
        manager prints, like the current test it's running, was being printed
        in-between the progress dots. The forked process was sharing the parent
        process' stdout.  While this makes sense, it's undesirable.
      &lt;/p&gt;
      &lt;h6&gt;A forked process sharing stdout&lt;/h6&gt;
      &lt;div class='gist'&gt;
        &lt;div class='gist-file'&gt;
          &lt;div class='gist-data gist-syntax'&gt;
            &lt;div class='gist-highlight'&gt;
              &lt;pre&gt;&lt;span class=&quot;vg&quot;&gt;$stdout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sync&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;nb&quot;&gt;fork&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;running test&amp;quot;&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;nb&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;nb&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;/pre&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      
      &lt;p&gt;
        A forked manager is essentially the same as running a manager in a terminal
        that gets minimized or backgrounded. All the user cares about are those little
        green dots so when a manager is forked, all of its output must be
        suppressed.
      &lt;/p&gt;
      &lt;p&gt;
        My first attempt was to use IO.pipe to hand off a nice clean IO object to the
        forked process.  This worked some of the time but larger test suites would just
        hang. I finally learned that that a pipe's buffer is not infinite and over
        time, if you don't read it, your write will just sit there blocking, waiting
        to be read before continuing with to write.
      &lt;/p&gt;
      &lt;h6&gt;Overloading the buffer of a pipe&lt;/h6&gt;
      &lt;div class='gist'&gt;
        &lt;div class='gist-file'&gt;
          &lt;div class='gist-data gist-syntax'&gt;
            &lt;div class='gist-highlight'&gt;
              &lt;pre&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipe&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;nb&quot;&gt;fork&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;vg&quot;&gt;$stdout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;28_000&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# change this number to 29_000 to see it hang&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;no&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waitall&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close_write&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;/pre&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      
      &lt;p&gt;
        IO.pipe was the right idea but a little off. The underlying principle was to
        point $stdout to another IO object. Unfortunately, our pipe was space
        constrained. What we really need is a super sized, dumb IO object.  Thanks to
        &lt;a href=&quot;http://shayarnett.com/&quot;&gt;Shay Arnett&lt;/a&gt;
        for reminding me of StringIO! Let's try it out.
      &lt;/p&gt;
      &lt;h6&gt;Replacing stdout with a StringIO&lt;/h6&gt;
      &lt;div class='gist'&gt;
        &lt;div class='gist-file'&gt;
          &lt;div class='gist-data gist-syntax'&gt;
            &lt;div class='gist-highlight'&gt;
              &lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;stringio&amp;#39;&lt;/span&gt;&amp;#x000A;&amp;#x000A;&lt;span class=&quot;nb&quot;&gt;fork&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;vg&quot;&gt;$stdout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StringIO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# comment this out to see the subprocess use the parent&amp;#39;s stdout&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;29_000&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;no&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waitall&lt;/span&gt;&lt;/pre&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      
      &lt;p&gt;Excellent, it didn't hang! Now we have a silent forked process. Let's wrap it up:&lt;/p&gt;
      &lt;h6&gt;QuietFork&lt;/h6&gt;
      &lt;div class='gist'&gt;
        &lt;div class='gist-file'&gt;
          &lt;div class='gist-data gist-syntax'&gt;
            &lt;div class='gist-highlight'&gt;
              &lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;stringio&amp;#39;&lt;/span&gt;&amp;#x000A;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;QuietFork&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;kp&quot;&gt;extend&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:pid&lt;/span&gt;&amp;#x000A;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fork&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;vi&quot;&gt;@pid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Kernel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fork&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&amp;#x000A;      &lt;span class=&quot;vg&quot;&gt;$stdout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StringIO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&amp;#x000A;      &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&amp;#x000A;    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&amp;#x000A;&lt;span class=&quot;no&quot;&gt;QuietFork&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fork&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&amp;#x000A;  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;29_000&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&amp;#x000A;&lt;span class=&quot;no&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waitall&lt;/span&gt;&lt;/pre&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      
      &lt;p&gt;
        Check out
        &lt;a href=&quot;https://github.com/sandro/specjour/blob/v0.3.1/lib/specjour/manager.rb#L16&quot;&gt;specjour&lt;/a&gt;
        to see how I actually use this thing.
      &lt;/p&gt;
    </content>
  </entry>
  <entry>
    <id>tag:turriate.com,2010-05-16:/articles/2010/may/moving-off-webby-on-nanoc/</id>
    <title type='html'>Moving off Webby to Nanoc</title>
    <published>2010-05-16T17:57:00-07:00</published>
    <link href='http://turriate.com/articles/2010/may/moving-off-webby-on-nanoc/' rel='alternative' />
    <summary type='html'>After a long survey of Ruby's static website generators I've finally settled on Nanoc.</summary>
    <content type='html'>
      &lt;p&gt;Yay! A year and a half later later and I'm finally writing my second article. That's exciting even though moving to &lt;a href=&quot;http://nanoc.stoneship.org&quot;&gt;Nanoc&lt;/a&gt; wasn't. I put my site on &lt;a href=&quot;http://webby.rubyforge.org&quot;&gt;webby&lt;/a&gt; over a year ago because that's what everyone else was doing. Unfortunately, I never felt like I fully understood webby which become yet another excuse not to write. It took researching a long list of static website generators and actually testing a few out to finally feel like I understood webby. If I didn't fight as hard as I did to make Nanoc work for me, I'd probably still be using webby.&lt;/p&gt;
      
      &lt;p&gt;Here's the list of generators I investigated:&lt;/p&gt;
      
      &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; - haml unsupported&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.staticmatic.net&quot;&gt;StaticMatic&lt;/a&gt; - can't write articles in Markdown&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://cloudhead.io/toto&quot;&gt;toto&lt;/a&gt; - haml unsupported, rack required&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://tinytree.info/&quot;&gt;bonsai&lt;/a&gt; - tilt is extremely appealing, can't load custom helpers, no generic partial helper&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://awardwinningfjords.com/2009/10/22/middleman.html&quot;&gt;middleman&lt;/a&gt; - really cool helpers/extensions, no method for retrieving a list of articles&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://webgen.rubyforge.org/&quot;&gt;webgen&lt;/a&gt; - worth a second look&lt;/li&gt;
      &lt;/ul&gt;
      
      
      &lt;p&gt;Ideally I want to write all of my content in Markdown but have the freedom to easily change page layouts in an ad-hoc fashion. Autocompilation is a must. All html and css will be written in haml and sass. I should have the ability to write custom Ruby helpers. Partials should be supported. Feed generation and sitemaps would be great.&lt;/p&gt;
      
      &lt;h3&gt;Bonsai&lt;/h3&gt;
      
      &lt;p&gt;I love Bonsai's idea of &quot;Magic Variables,&quot; which represent subdirectories within the current directory. Bonsai is hierarchy-friendly providing many methods for traversing up and down the tree, encouraging subdividing your application, which I like. What I don't like is having all of my content live inside of yaml file nor do I like manually maintaining a clean-url folder structure, i.e. &lt;code&gt;2009/jan/my-post/index&lt;/code&gt;. Bonsai uses &lt;a href=&quot;http://github.com/rtomayko/tilt&quot;&gt;tilt&lt;/a&gt; to promote agnostic template generation which I think is an awesome move, I wish all static generators would do this. Ultimately, I moved away from bonsai when I found that haml partials were unsupported and that I couldn't easily inject my own libraries into the runtime.&lt;/p&gt;
      
      &lt;h3&gt;Nanoc&lt;/h3&gt;
      
      &lt;p&gt;I hate Rules. Nanoc ships with a Rules file in your project's root directory. You use this file to tell Nanoc that all files living in the sass directory should be compiled with sass and all files in the blog directory should be compiled with Markdown. It's ridiculous, where the hell is tilt when you need it, file extensions exist for a reason you know? Besides having too-strict a routing engine, Nanoc is new enough (to me) to hold my interest and has enough features to enable me to write articles.&lt;/p&gt;
      
      &lt;p&gt;I'd like to take another look at webgen and toto, I think they both might have some things to offer. Each of these engines offer unique extras like syntax highlighting, feed generation, site maps, asset compression, etc. which I'd love to see wrapped up into one awesome, framework-agnostic library. Also, I'd like to come up with a workflow so I can better understand exactly what I need. Until then, I guess Nanoc will have to do.&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <id>tag:turriate.com,2009-01-28:/articles/2009/jan/bookmarklets-not-chiclets/</id>
    <title type='html'>Bookmarklets not Chiclets</title>
    <published>2009-01-28T01:40:00-08:00</published>
    <link href='http://turriate.com/articles/2009/jan/bookmarklets-not-chiclets/' rel='alternative' />
    <summary type='html'>Chatting about bookmarklets with Refresh Jacksonville - Pecha Kucha Style.</summary>
    <content type='html'>
      &lt;p&gt;I like javascript and I like hacking; sometimes I like to hack other people's work and I do so with bookmarklets.  Bookmarklets allow you to make modifications to websites via javascript.  Bookmarklets encourage the same philosophy as &lt;a href=&quot;http://www.greasespot.net&quot;&gt;greasemonkey&lt;/a&gt; greasemonkey except you and your users don't need to install greasemonkey to modify web sites, bookmarklets are natively supported by the browser.&lt;/p&gt;
      
      &lt;p&gt;I like Bookmarklets so much that I gave a talk about them at &lt;a href=&quot;http://www.refreshjacksonville.org/announcements/january-29th-meeting&quot;&gt;Refresh Jacksonville&lt;/a&gt;.&lt;/p&gt;
      
      &lt;p&gt;Here are some of the resources I mentioned in my talk:&lt;/p&gt;
      
      &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://gist.github.com/54389&quot;&gt;Bookmarklet Template&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://flxhr.flensed.com&quot;&gt;flXHR for secure cross-site scripting&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://turriate.com/media/githubber&quot;&gt;Githubber&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://gist.github.com/54387&quot;&gt;Load jQuery from google with callback after jQuery loads&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://gist.github.com/42119&quot;&gt;One line function to download jQuery from google&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
      
      
      &lt;p&gt;And here's the video of the talk:&lt;/p&gt;
      
      &lt;object width=&quot;500&quot; height=&quot;281&quot;&gt;&lt;param name=&quot;allowfullscreen&quot; value=&quot;true&quot; /&gt;&lt;param name=&quot;allowscriptaccess&quot; value=&quot;always&quot; /&gt;&lt;param name=&quot;movie&quot; value=&quot;http://vimeo.com/moogaloop.swf?clip_id=3459673&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=1&amp;amp;color=00ADEF&amp;amp;fullscreen=1&quot; /&gt;&lt;embed src=&quot;http://vimeo.com/moogaloop.swf?clip_id=3459673&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=1&amp;amp;color=00ADEF&amp;amp;fullscreen=1&quot; type=&quot;application/x-shockwave-flash&quot; allowfullscreen=&quot;true&quot; allowscriptaccess=&quot;always&quot; width=&quot;500&quot; height=&quot;281&quot;&gt;&lt;/embed&gt;&lt;/object&gt;
      
      
      &lt;p&gt;&lt;a href=&quot;/media/bookmarklets-not-chiclets.pdf&quot;&gt;Download the presentation&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
</feed>
