<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>techno weenie</title>
  <id>tag:techno-weenie.net,2010:mephisto/</id>
  <generator uri="http://github.com/mojombo/jekyll" version="0.6.2">Jekyll</generator>
  <link href="http://techno-weenie.net/feed/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="http://techno-weenie.net/" rel="alternate" type="text/html"/>
  <updated>2010-08-02T23:58:18-07:00</updated>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:8:3:protobuf-for-node</id>
    <published>2010-08-03T00:00:00-07:00</published>
    <updated>2010-08-03T00:00:00-07:00</updated>
    <link href="/2010/8/3/protobuf-for-node" rel="alternate" type="text/html"/>
    <title>Protocol Buffers with Riak for Node.js</title>
<content type="html">
&lt;p&gt;I've been playing around with &lt;a href=&quot;https://wiki.basho.com/display/RIAK/The+Riak+Fast+Track&quot;&gt;Riak&lt;/a&gt; a bit lately.  It's a simple key/value store with S3-style buckets and one-way links between keys.  It also has clustering built in, and lets you run map/reduce against a set of data pretty easily.  All this, over a simple HTTP API.&lt;/p&gt;

&lt;p&gt;It's a great way to start playing with Riak, but I found it to be pretty slow.  With Riak, there are two more options: use the Erlang client, or write a Protocol Buffer adapter.  I'd never done anything with &lt;a href=&quot;http://code.google.com/p/protobuf/&quot;&gt;Protocol Buffers&lt;/a&gt;, so I figured this was good opportunity.&lt;/p&gt;

&lt;h2&gt;Riak PBC Client&lt;/h2&gt;

&lt;p&gt;Armed with &lt;a href=&quot;http://code.google.com/p/protobuf-for-node/&quot;&gt;Node.js Protocol Buffer&lt;/a&gt; serializing and parsing abilities, I took a look at the &lt;a href=&quot;https://wiki.basho.com/display/RIAK/PBC+API&quot;&gt;Riak PBC API&lt;/a&gt;.  It has a very simple API:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;00 00 00 07 09 0A 01 62 12 01 6B
|----Len---|MC|----Message-----|
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each message starts with 4 bytes for the message length, a single byte for the message code, and then the message.&lt;/p&gt;

&lt;p&gt;The example above is how a simple request for a key might look.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// the Protocol Buffer schema.
message RpbGetReq {
    required bytes bucket = 1;
    required bytes key = 2;
    optional uint32 r = 3;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A Riak request looks something like this:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;Schema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;protobuf_for_node&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Schema&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;schema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;./riak.desc&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;GetReq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;RpbGetReq&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# &amp;lt;Buffer 0a 01 62 12 01 6b&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;GetReq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;serialize&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;bucket: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;k&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;len&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# account for riak code too&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 4 byte message length&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&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;nx&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&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;nx&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&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;nx&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;req&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&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;mi&quot;&gt;9&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;copy&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# copy serialized data to the buffer&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# req is now&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# &amp;lt;Buffer 00 00 00 07 09 0a 01 62 12 01 6b&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;That assembles the message.  Now, we just create a tcp connection to send it to Riak:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createConnection&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8087&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;connect&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;write&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Finally, something needs to listen for the data event for a response:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;len&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;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;8&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;nx&quot;&gt;chunk&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;o&quot;&gt;-&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# subtract 1 for the message code&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lookup_type_from_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;copy&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Pooling Connections&lt;/h2&gt;

&lt;p&gt;My &lt;a href=&quot;http://gist.github.com/488488#file_riak.coffee&quot;&gt;initial example&lt;/a&gt; started off pretty basic, but started to grow out of control.  I quickly realized that since the socket API was very synchronous, I needed to implement a connection pool so a Node.js process could have simultaneous conversations with Riak.  A basic example looks like this:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;riak&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;./protobuf&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)()&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createServer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# get a fresh connection off the pool&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;riak&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# make a connection, call the given callback when it returns.&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;PingReq&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;writeHead&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# release the connection back to the pool&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# SHORTCUT&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createServer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# automatically gets a fresh connection, sends a request, and releases&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# it back to the pool when done.&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;riak&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;PingReq&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;writeHead&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;nori + riak-js&lt;/h2&gt;

&lt;p&gt;Right now, this isn't in any released version of nori or riak-js.  The rough Protocol Buffers client is available in &lt;a href=&quot;http://github.com/technoweenie/riak-js/blob/coffee/src/protobuf.coffee&quot;&gt;the coffee branch of my riak-js fork&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When Frank released the sweet &lt;a href=&quot;http://riakjs.org/&quot;&gt;Riak-JS site&lt;/a&gt;, I took a hard look at what purpose nori was solving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I wanted to learn more about Riak (accomplished).&lt;/li&gt;
&lt;li&gt;I wanted to experiment with a new API style (very similar to Riak-js)&lt;/li&gt;
&lt;li&gt;I wanted a higher level Riak lib, more like an ORM.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The goals aligned pretty closely with riak-js, so there seemed no good reason to double our efforts.  I've decided to discontinue nori for the time being, and focus my Riak efforts in a refactoring of riak-js.  We want to have a single lib that lets you access Riak from jQuery (maybe), as well as Node.js over the HTTP and PBC APIs.&lt;/p&gt;

&lt;p&gt;So, what is the current progress of all this?  Here are some quick benchmarks from my iMac i7:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;coffeescript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# riak-js http API &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ab -n 5000 -c 20 &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 734.31 req/sec&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;sys&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;sys&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;http&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;riak-js&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createServer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;airlines&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;KLM&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;flight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;writeHead&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;flight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8124&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# riak-js PBC API&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ab -n 5000 -c 20&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 1682.01 req/sec&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;sys&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;sys&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;http&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;riak&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;./protobuf&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)()&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createServer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;riak&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;GetReq&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;bucket: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;airlines&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;KLM&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;flight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;writeHead&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;flight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8124&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;That's over a 2x speedup, not bad.&lt;/p&gt;

</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:7:13:in-process-node-queues</id>
    <published>2010-07-13T00:00:00-07:00</published>
    <updated>2010-07-13T00:00:00-07:00</updated>
    <link href="/2010/7/13/in-process-node-queues" rel="alternate" type="text/html"/>
    <title>In-Process Node.js Queues</title>
<content type="html">
&lt;p&gt;Node.js is great at handling lots of asynchronous connections, but sometimes I'd like to limit how many are in use.  One real world example is some kind of spider or feed reader.  If you have a list of 500 addresses to fetch, you don't want to fetch them all at once.  Maybe they're all on one server, or the requests return large files that need some post processing.&lt;/p&gt;

&lt;p&gt;A simple queue like Resque is great for this, but I wanted something even simpler.  Something that lived in the Node.js process, and could exit cleanly without any of that persistent mess left over.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/technoweenie/node-chain-gang&quot;&gt;Chain Gang&lt;/a&gt; is the result of my experimentation.  My idea is using the Node.js event system for pub/sub:&lt;/p&gt;

&lt;p&gt;First, I specify my unit of work.  In this case, I'm fetching a a web address, and calling worker.finish() after that's done.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nv&quot;&gt;sys: &lt;/span&gt;  &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;sys&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;http: &lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;http&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;client: &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createClient&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# start an active chain gang queue with 3 workers by default.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;chain: &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;chain-gang&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# downloads a web page, runs the callback when it&amp;#39;s done.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;get_path: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;req: &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;host: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;end&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# returns a chain gang job that downloads a web page and finishes the worker.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;job: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;worker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;get_path&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/$timeout/$name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;worker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now, I can add the callback, and queue the unit of work:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;coffeescript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# queues the job&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# queues the job with the unique name &amp;quot;foo_request&amp;quot;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;foo_request&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Assuming the chain gang queue is active, it should start executing the jobs immediately.&lt;/p&gt;

&lt;p&gt;There are two interesting behaviors that are possible now: Duplicate jobs are not run, and only a fixed number of jobs can run at any given time.  To highlight them, I have some sample files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/code/2010/7/chain-gang-sample/webserver.coffee&quot;&gt;webserver.coffee&lt;/a&gt; is a silly web server that waits for a specified amount of time before returning a request.  A URL like &quot;/3/foo&quot; will return in 3 seconds, for example.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/code/2010/7/chain-gang-sample/chain-with-dupes.coffee&quot;&gt;chain-with-dupes.coffee&lt;/a&gt; shows what happens when multiple jobs with the same name are queued.  In this contrived example, only the first, longer one is completed.  The rest are ignored.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/code/2010/7/chain-gang-sample/chain-with-uniques.coffee&quot;&gt;chain-with-uniques.coffee&lt;/a&gt; shows how Chain Gang handles more jobs than workers.  They just sit in an array until a free worker can take it.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;On a side note, this is my first lib using &lt;a href=&quot;http://npmjs.org/&quot;&gt;npm&lt;/a&gt; (Node.js package manager).  Type &lt;code&gt;npm install chain-gang&lt;/code&gt; to get rockin'.&lt;/p&gt;

</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:7:7:geektalk-interview</id>
    <published>2010-07-07T00:00:00-07:00</published>
    <updated>2010-07-07T00:00:00-07:00</updated>
    <link href="/2010/7/7/geektalk-interview" rel="alternate" type="text/html"/>
    <title>Geek Talk Interview</title>
<content type="html">
&lt;p&gt;So, I was interviewed by &lt;a href=&quot;http://thegeektalk.com/interviews/rick-olson/&quot;&gt;The Geek Talk&lt;/a&gt; recently.  Read on to learn the awful truth behind my early programming days :)&lt;/p&gt;

&lt;p&gt;Also, I'm moving to San Francisco this weekend.  I'm really looking forward to working side by side with my fellow GitHubbers.  Portland's a great place, and I have a feeling I'll be back.&lt;/p&gt;

</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:6:28:tee_and_child_processes</id>
    <published>2010-06-28T00:00:00-07:00</published>
    <updated>2010-06-28T00:00:00-07:00</updated>
    <link href="/2010/6/28/tee_and_child_processes" rel="alternate" type="text/html"/>
    <title>Tee and Child Processes</title>
<content type="html">
&lt;p&gt;My first node.js project at GitHub is a &lt;a href=&quot;http://github.com/blog/678-meet-nodeload-the-new-download-server&quot;&gt;replacement download server&lt;/a&gt;.  I wanted to remove the extra moving pieces required to get it to work.  One of the steps involves writing a file from the output of &lt;code&gt;git archive&lt;/code&gt;.  My initial attempt looked like this:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nv&quot;&gt;fs: &lt;/span&gt;     &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;fs&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;child: &lt;/span&gt;  &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;child_process&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;git: &lt;/span&gt;    &lt;span class=&quot;nx&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;spawn&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;git&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;archive&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;other options&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;stream: &lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createWriteStream&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;outputFilename&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# writes the file from git archive to the file stream&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# if the file stream isn&amp;#39;t flushed, pause git&amp;#39;s stdout&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# once the file stream is flushed, resume git&amp;#39;s stdout&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;drain&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;exit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;However, git archive's tar format does not come compressed.  That means I have to pipe the output to another ChildProcess object.  How do I do that without a lot of code duplication?  I put the common callbacks into defined functions:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nv&quot;&gt;fs: &lt;/span&gt;     &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;fs&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;child: &lt;/span&gt;  &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;child_process&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# writes data to the local file system.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;streamer: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# pipes the data to the gzip process.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;gzipper: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gzip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# closes the written file stream.  &lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;closer: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;git: &lt;/span&gt;    &lt;span class=&quot;nx&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;spawn&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;git&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;archive&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;other options&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;stream: &lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createWriteStream&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;outputFilename&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;drain&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# if this is a tarball, pipe `git archive` through `gzip -n`&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;outputFilename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\.tar\.gz$/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;gzip: &lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;spawn&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gzip&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;-n&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;-c&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;input: &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gzip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stdout&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;gzip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;streamer&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;gzip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt;        &lt;span class=&quot;s1&quot;&gt;&amp;#39;exit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;closer&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;gzip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt;  &lt;span class=&quot;s1&quot;&gt;&amp;#39;drain&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;exit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;gzip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;input: &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stdout&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;exit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;closer&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;gzip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;gzipper&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;streamer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;That's the code to write either &lt;code&gt;git archive --format=zip&lt;/code&gt; or &lt;code&gt;git archive --format=tar | gzip&lt;/code&gt; to a file.  It works, but the code is more complicated than I'd like.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/rtomayko&quot;&gt;Ryan&lt;/a&gt; suggested I use &lt;a href=&quot;http://man.cx/tee(1posix)&quot;&gt;tee&lt;/a&gt; for outputting the file, and /bin/sh to assemble the pipes.&lt;/p&gt;

&lt;p&gt;Now, the code is even simpler than my first attempt:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nv&quot;&gt;child: &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;child_process&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;cmd: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;git archive ...&amp;#39;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;outputFilename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\.tar\.gz$/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39; | gzip -n -c&amp;#39;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;arch: &lt;/span&gt;   &lt;span class=&quot;nx&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;spawn&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/bin/sh&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;-c&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;$cmd | tee $outputFilename&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addListener&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;exit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# do something&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:6:23:you-can-let-go-now</id>
    <published>2010-06-23T00:00:00-07:00</published>
    <updated>2010-06-23T00:00:00-07:00</updated>
    <link href="/2010/6/23/you-can-let-go-now" rel="alternate" type="text/html"/>
    <title>You can let go now</title>
<content type="html">
&lt;p&gt;If you're reading this, I've completed migrating my blog from &lt;a href=&quot;http://github.com/technoweenie/mephisto&quot;&gt;Mephisto&lt;/a&gt;
to &lt;a href=&quot;http://github.com/mojombo/jekyll&quot;&gt;Jekyll&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I had fun working on it, but clearly I failed on being able to foster a good community around it.  It was an unconventional Rails app in a time before things like Rack, Sinatra, MongoDB, restful controllers, etc.  It's nice to see similar ideas in newer projects though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://github.com/mojombo/jekyll&quot;&gt;Jekyll&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://get.harmonyapp.com/&quot;&gt;Harmony&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://github.com/creationix/wheat&quot;&gt;Wheat&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Thanks to everyone who &lt;a href=&quot;http://github.com/technoweenie/mephisto/contributors&quot;&gt;helped work on it&lt;/a&gt;, especially &lt;a href=&quot;http://alternateidea.com/&quot;&gt;Justin&lt;/a&gt; for the partnership on the design.  Mephisto was where our working relationship started, which eventually lead to Lighthouse and ENTP...&lt;/p&gt;

</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:5:17:railsconf-building-apis</id>
    <published>2010-05-17T00:00:00-07:00</published>
    <updated>2010-05-17T00:00:00-07:00</updated>
    <link href="/2010/5/17/railsconf-building-apis" rel="alternate" type="text/html"/>
    <title>Railsconf: Building APIs</title>
<content type="html">
&lt;p&gt;I'm going to be taking Chris' place in the &lt;a href=&quot;http://en.oreilly.com/rails2010/public/schedule/detail/14502&quot;&gt;Building an API&lt;/a&gt; panel at Railsconf in June.  I'll be speaking about the GitHub API (of course), as well as touching on my experiences building APIs for Lighthouse and Tender.&lt;/p&gt;

&lt;p&gt;Don't despair, Chris will still be doing his &lt;a href=&quot;http://en.oreilly.com/rails2010/public/schedule/detail/14595&quot;&gt;Redis, Rails, and  Reque&lt;/a&gt; talk.&lt;/p&gt;

</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:5:10:rhodes-riak-on-node-js</id>
    <published>2010-05-10T00:00:00-07:00</published>
    <updated>2010-05-10T00:00:00-07:00</updated>
    <link href="/2010/5/10/rhodes-riak-on-node-js" rel="alternate" type="text/html"/>
    <title>Nori: Node.js Riak wrapper</title>
<content type="html">
&lt;p&gt;&lt;a href=&quot;http://blog.basho.com/2010/04/14/practical-map-reduce:-forwarding-and-collecting/&quot;&gt;&lt;img src=&quot;/images/2010/05/fault-tolerance.png&quot; class=&quot;floater&quot; alt=&quot;Map Reduce?&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I took the &lt;a href=&quot;https://wiki.basho.com/display/RIAK/The+Riak+Fast+Track&quot;&gt;Riak Fast Track&lt;/a&gt; and really liked messing around with map reduce functions.  So, I wrote &lt;a href=&quot;http://github.com/technoweenie/nori&quot;&gt;nori&lt;/a&gt;, a node.js client.&lt;/p&gt;

&lt;p&gt;Riak is a key/value store inspired by the Dynamo whitepaper.  It has buckets, which contain resources identified by keys, with a REST API.  Therefore, it feels a lot like S3, with added &lt;a href=&quot;http://blog.basho.com/2010/04/14/practical-map-reduce:-forwarding-and-collecting/&quot;&gt;map reduce&lt;/a&gt; and &lt;a href=&quot;http://blog.basho.com/2010/03/25/schema-design-in-riak---relationships/&quot;&gt;link&lt;/a&gt; &lt;a href=&quot;http://blog.basho.com/2010/02/24/link-walking-by-example/&quot;&gt;walking&lt;/a&gt; powers.&lt;/p&gt;

&lt;p&gt;Riak is written in Erlang, but Basho decided to also support javascript for map reduce.  This makes node.js a natural fit for Riak.  Node.js is of course great at handling non-blocking HTTP requests, and &lt;code&gt;function.toString()&lt;/code&gt; lets us pass javascript functions through Nori.  This means it would be trivial to write local tests of your map reduce functions with local data (without having to go through Riak).  Look at how closely &lt;a href=&quot;http://github.com/technoweenie/nori/blob/master/examples/fast-track/mapred.js#L6-19&quot;&gt;my implementation&lt;/a&gt; matches &lt;a href=&quot;https://wiki.basho.com/display/RIAK/Loading+Data+and+Running+MapReduce+Queries&quot;&gt;the sample functions in the fast track&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Overall, the Fast Track was pretty good.  I would have liked some coverage of link walking, but at some point you have to cut the &quot;fast track&quot; short.  It was short enough to digest in a sitting (though, it did turn a chillaxin' Sunday afternoon into an epic node.js hackfest).&lt;/p&gt;

</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:5:10:no-i-did-not-create-a-mobile-phone-framework-too</id>
    <published>2010-05-10T00:00:00-07:00</published>
    <updated>2010-05-10T00:00:00-07:00</updated>
    <link href="/2010/5/10/no-i-did-not-create-a-mobile-phone-framework-too" rel="alternate" type="text/html"/>
    <title>No, I did not create a mobile phone framework too</title>
<content type="html">
&lt;p&gt;&lt;img src=&quot;http://img.skitch.com/20100510-p2x6aku7n2nsxhiquh9bj43meq.png&quot; class=&quot;floater&quot; alt=&quot;Where we're going, we don't need Rhodes&quot; style=&quot;padding:0 50px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Oh man, &lt;a href=&quot;http://twitter.com/xinuc/status/13714721161&quot;&gt;@xinuc&lt;/a&gt; is breaking my heart over here.  The name Rhodes is taken by &lt;a href=&quot;http://github.com/rhomobile/rhodes&quot;&gt;some javascript mobile phone framework&lt;/a&gt;.  Now, I need a new name.  I'm leaning towards Noh-Varr if no one else has any suggestions.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update:&lt;/em&gt; Okay, &lt;a href=&quot;http://twitter.com/jedschmidt/status/13716191470&quot;&gt;Jed rocks&lt;/a&gt;, the new project's name will be &lt;a href=&quot;http://en.wikipedia.org/wiki/Nori&quot;&gt;nori&lt;/a&gt;.&lt;/p&gt;

</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:5:4:escaping-your-test-suite-with-your-life</id>
    <published>2010-05-04T00:00:00-07:00</published>
    <updated>2010-05-04T00:00:00-07:00</updated>
    <link href="/2010/5/4/escaping-your-test-suite-with-your-life" rel="alternate" type="text/html"/>
    <title>Escaping your test suite with your life</title>
<content type="html">
&lt;p&gt;&lt;img src=&quot;http://img.skitch.com/20100504-cw3qshfnw9cpjrrtcbunu3mw93.png&quot; class=&quot;floater&quot; alt=&quot;Arnold in Running Man&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One testing feature that I really miss in test/unit, is &lt;a href=&quot;http://github.com/dchelimsky/rspec&quot;&gt;rspec&lt;/a&gt;'s &lt;code&gt;before(:all)&lt;/code&gt; blocks.  These are similar to your test/unit &lt;code&gt;setup&lt;/code&gt; methods, yet they only run once for the whole test suite.&lt;/p&gt;

&lt;p&gt;I was able to &lt;a href=&quot;http://github.com/jm/context/blob/master/lib/context/suite.rb#L29-34&quot;&gt;implement &lt;code&gt;before(:all)&lt;/code&gt; callbacks in context&lt;/a&gt;, but the code for that is a little gnarly.  Basically, I had to hack the &lt;code&gt;Test::Unit::TestSuite#run&lt;/code&gt; method to run some callbacks before and after the test run.  Downside: it doesn't work in minitest (it could, but &lt;a href=&quot;http://github.com/ruby/ruby/blob/ruby_1_9_1/lib/minitest/unit.rb#L405-426&quot;&gt;the code&lt;/a&gt; isn't very inviting).&lt;/p&gt;

&lt;p&gt;So, what if I could do something like this?&lt;/p&gt;

&lt;pre class=&quot;active4d&quot;&gt;&lt;code&gt;&lt;span class=&quot;Keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;TypeName&quot;&gt;MyTest&lt;span class=&quot;InheritedClass&quot;&gt; &lt;span class=&quot;InheritedClass&quot;&gt;&amp;lt;&lt;/span&gt; ActiveSupport::TestCase&lt;/span&gt;&lt;/span&gt;
  block &lt;span class=&quot;Operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;LibraryClassType&quot;&gt;RunningMan&lt;/span&gt;::&lt;span class=&quot;FunctionName&quot;&gt;Block&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;LineComment&quot;&gt;    &lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; something expensive&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;

  setup { block.&lt;span class=&quot;FunctionName&quot;&gt;run&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;self&lt;/span&gt;) }
&lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;a href=&quot;http://github.com/technoweenie/running_man&quot;&gt;Running Man&lt;/a&gt; gives you &lt;code&gt;before(:all)&lt;/code&gt; callbacks in test/unit and minitest (ruby 1.9).  Basically, you create a &lt;code&gt;RunningMan::Block&lt;/code&gt; and call &lt;code&gt;#run&lt;/code&gt; in a normal setup call in your tests.  The &lt;code&gt;RunningMan::Block&lt;/code&gt; calls the block just once, and fills your test case instances with the right instance variables on each test.  No muss, no ugly test/unit hacking.  (Ok, before you call me &lt;a href=&quot;http://github.com/technoweenie/running_man/blob/master/lib/running_man/block.rb#L96-110&quot;&gt;a liar&lt;/a&gt;, &lt;em&gt;that&lt;/em&gt; hack was to allow special teardown blocks on the last test.  That feature doesn't work in ruby 1.9, unfortunately.)&lt;/p&gt;

&lt;p&gt;This was extracted from GitHub and rewritten when I wanted to use it &lt;a href=&quot;http://github.com/technoweenie/seinfeld/blob/rewrite/test/user_test.rb&quot;&gt;in a small, non-Rails project&lt;/a&gt;.&lt;/p&gt;

&lt;pre class=&quot;active4d&quot;&gt;&lt;code&gt;&lt;span class=&quot;Keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;TypeName&quot;&gt;MyTest&lt;span class=&quot;InheritedClass&quot;&gt; &lt;span class=&quot;InheritedClass&quot;&gt;&amp;lt;&lt;/span&gt; ActiveSupport::TestCase&lt;/span&gt;&lt;/span&gt;
  fixtures &lt;span class=&quot;Keyword&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;post&lt;/span&gt; &lt;span class=&quot;Operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;LibraryClassType&quot;&gt;Post&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;make&lt;/span&gt; &lt;span class=&quot;LineComment&quot;&gt;&lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; &amp;lt;3 Machinist&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;

  test &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;check something on post&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do&lt;/span&gt;
    assert_equal &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;foo&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;post&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;title&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;

  test &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;delete post&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;post&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;destroy&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:4:5:will-the-ipad-kill-comic-books</id>
    <published>2010-04-05T00:00:00-07:00</published>
    <updated>2010-04-05T00:00:00-07:00</updated>
    <link href="/2010/4/5/will-the-ipad-kill-comic-books" rel="alternate" type="text/html"/>
    <title>Will the iPad kill comic books?</title>
<content type="html">
&lt;h3&gt;The Marvel App&lt;/h3&gt;

&lt;p&gt;Marvel has been dabbling in online comics for awhile.  They've had digital comic previews in an awkward flash interface for years, which has recently evolved into a monthly online service.  Lately, they've started producing &lt;a href=&quot;http://marvel.com/motion_comics/&quot;&gt;Motion Comics&lt;/a&gt;.  So, it's not surprising that they're the first big comic company with a &lt;a href=&quot;http://itunes.apple.com/us/app/marvel-comics/id350027738?mt=8&quot;&gt;serious iPad offering&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;The Good&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;http://img.skitch.com/20100405-b7i32asp2en5h417itr25pumih.jpg&quot; alt=&quot;Spider-Man&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Marvel was smart to partner with &lt;a href=&quot;http://www.comixology.com/&quot;&gt;Comixology&lt;/a&gt;.  Comics look &lt;em&gt;great&lt;/em&gt;.  I'd recommend picking up the free Spider-Man issues if you want to check it out.  You can read a whole page, or go panel to panel if you like.  There are a lot of advantages when you compare the iPad to a single comic.  You can't tear anything, you can read in the dark, you can zoom in, etc.  Also, the iPad holds all of your purchases (or at least, your most recent ones).  You won't be stuck with a closet full of &lt;a href=&quot;http://comicbooks.about.com/od/glossary/g/box.htm&quot;&gt;long boxes&lt;/a&gt; after several years.  Also, I hoped that this would be better for the environment, but &lt;a href=&quot;http://www.nytimes.com/interactive/2010/04/04/opinion/04opchart.html&quot;&gt;it's not quite as simple as that&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Navigating the store and buying issues is easy enough.  You can search by Series, Genre, Creator, and Storylines.  If you find something you like, you can have the app send you notifications when new titles are added.  Instant gratification comic book Wednesdays!&lt;/p&gt;

&lt;h3&gt;The Bad&lt;/h3&gt;

&lt;p&gt;There's the obvious downside:  These comics are protected and unshareable.  In fact, it's a little worse than other iTunes content because the files are hidden from you.  There's no way to export these comics for backup purposes, like you can with music or video.  However, I got a rapid response from their support center confirming that you are able to re-download anything you've purchased.&lt;/p&gt;

&lt;p&gt;If this is a deal killer for you, hope is not lost on the iPad.  I've found the &lt;a href=&quot;http://itunes.apple.com/us/app/comic-zeal-comic-reader-4/id363990983?mt=8&quot;&gt;Comic Zeal app&lt;/a&gt; to be decent for reading CBR files.  CBR files are basically scans of comics traded on Bit Torrent.  The quality varies, and the app isn't quite as smooth as Comixology's.  Personally, I only download what I already own, or seek out the graphic novels if I'm trying something new.&lt;/p&gt;

&lt;h3&gt;The Loser&lt;/h3&gt;

&lt;p&gt;Unfortunately, if digital comics become popular, then the comic shops will suffer.  I like visiting &lt;a href=&quot;http://www.floatingworldcomics.com/main/&quot;&gt;Floating World Comics&lt;/a&gt; every week or so.  Jason gives me personal recommendations (&lt;a href=&quot;http://www.amazon.com/100-Paul-Pope/dp/1401203493&quot;&gt;Paul Pope&lt;/a&gt;), advanced screenings to movies, etc.  Floating Worlds sometimes holds special &lt;a href=&quot;http://www.firstthursdayportland.com/&quot;&gt;First Thursday&lt;/a&gt; events, such as the where I picked up my &lt;a href=&quot;http://www.flickr.com/photos/technoweenie/3447609999/&quot;&gt;original David Mack page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, iTunes and Amazon have managed to kill off most of the indie book and music stores.  I fear the same fate awaits a lot of comic shops too.  What can I say though?  Comics on the iPad are cheaper, and more convenient to access and store digitally.  The same goes for books, movies, and music, even though I live close just a few blocks from a Powell's bookstore.&lt;/p&gt;

&lt;p&gt;You know what I'd love?  Hardcover graphic novels with DVD's full of the digital content.&lt;/p&gt;

&lt;h3&gt;The Verdict&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;http://img.skitch.com/20100405-djrthpb5i2spa2661jxtt49iuu.png&quot; alt=&quot;Civil War&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Comixology and Marvel have won me over.  The iPad will likely become my preferred medium for reading comics.  I'm hoping that they increase the catalog significantly, or at least start offering new titles.  For instance, they're relaunching their flagship Avengers titles in May, and it'd be nice to pick those up on the iPad.&lt;/p&gt;

&lt;p&gt;If you're interested in checking some comics out, I'd recommend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iron Man #1-6&lt;/li&gt;
&lt;li&gt;Amazing Spider-Man #546-548&lt;/li&gt;
&lt;li&gt;Astonishing X-Men&lt;/li&gt;
&lt;li&gt;New Avengers&lt;/li&gt;
&lt;li&gt;Wolverine #20-31 (Enemy of the State, Agent of SHIELD)&lt;/li&gt;
&lt;li&gt;Captain America (Secret agent/espionage stuff)&lt;/li&gt;
&lt;li&gt;Planet Hulk (Hulk plays gladiator while stranded on an alien planet)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Check out the &lt;a href=&quot;http://itunes.apple.com/us/app/comics/id303491945?mt=8&quot;&gt;Comixology app&lt;/a&gt; if superheroes aren't your thing.  I've been enjoying Madman so far.  Though, I'd wait a bit for Vertigo to come out with a competing service.&lt;/p&gt;

&lt;p&gt;I'm curious how others are enjoying the comic book experience, whether you're a comic nut like me, a former reader, or  you've only seen the movies.&lt;/p&gt;

</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:4:5:first-day-at-github</id>
    <published>2010-04-05T00:00:00-07:00</published>
    <updated>2010-04-05T00:00:00-07:00</updated>
    <link href="/2010/4/5/first-day-at-github" rel="alternate" type="text/html"/>
    <title>First day at GitHub</title>
<content type="html">
&lt;p&gt;Today's my first day at &lt;a href=&quot;http://github.com&quot;&gt;GitHub&lt;/a&gt;.  Leaving ENTP and ActiveReload was a hard decision, but I really felt like it was time to move on to something new.  I was very humbled by Chris' offer to join their growing team.  Apparently they need a drill sergeant to get the app ready for Rails 3.  My other recent interests (node.js, mongoDB, bourbon) also fit in well with the company.&lt;/p&gt;

&lt;p&gt;I really just got to Portland (2 years ago), but this is a good chance to make the trek to San Francisco.  I grew up in a small Kansas town, and have basically been skirting around the idea of going to SF for years.  I'll likely stay in Portland for most of the summer, and move in the fall.  I don't know yet -- I was actually going to do this last year and chickened out :)&lt;/p&gt;

&lt;p&gt;I feel very fortunate to be able to move from one kick ass company to another.  I'm really jazzed about what this year is going to be like at GitHub.&lt;/p&gt;

</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:3:26:my-lesson-in-bootstrapping</id>
    <published>2010-03-26T00:00:00-07:00</published>
    <updated>2010-03-26T00:00:00-07:00</updated>
    <link href="/2010/3/26/my-lesson-in-bootstrapping" rel="alternate" type="text/html"/>
    <title>My Lesson in Bootstrapping</title>
<content type="html">
&lt;p&gt;John Nunemaker wrote up &lt;a href=&quot;http://orderedlist.com/our-writing/blog/articles/lessons-learned-bootstrapping-harmony/&quot;&gt;his experiences in bootstrapping&lt;/a&gt; &lt;a href=&quot;http://get.harmonyapp.com/&quot;&gt;Harmony&lt;/a&gt; with Steve Smith.  He makes a lot of great points that have been working out for them.  I just have one quick thing to add to it all:&lt;/p&gt;

&lt;h3&gt;Embrace Open Source&lt;/h3&gt;

&lt;p&gt;I helped bootstrap both &lt;a href=&quot;http://lighthouseapp.com&quot;&gt;Lighthouse&lt;/a&gt; and &lt;a href=&quot;http://tenderapp.com&quot;&gt;Tender Support&lt;/a&gt;, and noticed big gains after supporting open source projects.  Obviously, these products are more developer focused than most, but we also take every chance to sponsor non profit organizations, educational use, etc.&lt;/p&gt;

&lt;p&gt;When Lighthouse launched, I was apprehensive about supporting them.   I worried that we'd get some project like Rails or Wordpress on there, melting my poor servers.  Really, we were extremely &lt;em&gt;fortunate&lt;/em&gt; to get some high profile OSS projects.  Our servers didn't melt (yes, we love &lt;a href=&quot;http://www.engineyard.com/&quot;&gt;Engine Yard&lt;/a&gt;, our hosting partner), and put our product in front of a lot more eyeballs.&lt;/p&gt;

&lt;p&gt;Write these opportunities off as advertising if it helps.&lt;/p&gt;

</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:3:26:leave-load_path-alone</id>
    <published>2010-03-26T00:00:00-07:00</published>
    <updated>2010-03-26T00:00:00-07:00</updated>
    <link href="/2010/3/26/leave-load_path-alone" rel="alternate" type="text/html"/>
    <title>Dealing with $LOAD_PATH properly</title>
<content type="html">
&lt;p&gt;I recently went through a process of consolidating a few backend miniapps that power some boring parts of Lighthouse and Tender.  I upgraded one app to Sinatra 1.0, and converted another from Rails to Sinatra.  The goal was to mount them in the same rack process, therefore simplifying the deployment process all around.  Doing this reinforced &lt;a href=&quot;http://twitter.com/rtomayko/status/1155906157&quot;&gt;Ryan's sage advice&lt;/a&gt; about &lt;a href=&quot;http://tomayko.com/writings/require-rubygems-antipattern&quot;&gt;requiring rubygems in your libraries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With libraries, it's cake.  Your &lt;a href=&quot;http://github.com/technoweenie/faraday/blob/9a8649b6d6be900eec81d38b3056901db6372f91/Rakefile#L13-14&quot;&gt;gem requirements&lt;/a&gt; are light.  No one is deploying your libraries as-is, so you can assume that any configuration is handled in their applications.  I'm still &lt;a href=&quot;http://github.com/technoweenie/faraday/blob/9a8649b6d6be900eec81d38b3056901db6372f91/test/helper.rb#L1-8&quot;&gt;struggling with tests a bit&lt;/a&gt;, however.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do not require rubygems (or rip, bundler, etc) in any files in &lt;code&gt;lib&lt;/code&gt; or &lt;code&gt;test&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Do not mess with the load path either.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Applications are a different matter.  These typically will be deployed, so some kind of configuration file is essential.  I try to provide examples so coworkers can get up and running really quickly.  My config files typically look something like this:&lt;/p&gt;

&lt;pre class=&quot;active4d&quot;&gt;&lt;code&gt;&lt;span class=&quot;LineComment&quot;&gt;&lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; config.rb&lt;/span&gt;
&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;$&lt;/span&gt;LOAD_PATH&lt;/span&gt; &lt;span class=&quot;Operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; ... &lt;span class=&quot;LineComment&quot;&gt;&lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; for setting up the Sinatra app's `lib` path and any &lt;/span&gt;
&lt;span class=&quot;LineComment&quot;&gt;                  &lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; vendored libraries&lt;/span&gt;

&lt;span class=&quot;Keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;rubygems&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;LineComment&quot;&gt;&lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; you can replace this with Bundler, Rip, etc&lt;/span&gt;
gem &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sinatra&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;~&amp;gt; 1.0.0&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
gem ...

&lt;span class=&quot;Keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;my-sinatra-app&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now, when I re-package these in a different setting (such as when I mash two Sinatra apps into the same Rack process), I have full control over the &lt;code&gt;$LOAD_PATH&lt;/code&gt; and the loaded gem versions.&lt;/p&gt;

&lt;p&gt;One pattern I've adopted for apps using Sequel is some kind of &lt;code&gt;#load&lt;/code&gt; method.  I had problems where my code was loading &lt;code&gt;Sequel::Model&lt;/code&gt; instances before the database configuration was setup.  Requiring these files first would access the non-existent database configuration and blow up.&lt;/p&gt;

&lt;pre class=&quot;active4d&quot;&gt;&lt;code&gt;&lt;span class=&quot;LineComment&quot;&gt;&lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; OLD&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;my-app&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;LineComment&quot;&gt;&lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; requires 'sequel' and 'my-app/foo_model'&lt;/span&gt;
&lt;span class=&quot;LibraryClassType&quot;&gt;Sequel&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;Operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;...&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;LineComment&quot;&gt;&lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; NEW&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;my-app&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;LibraryClassType&quot;&gt;MyApp&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;LibraryClassType&quot;&gt;Sequel&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;Operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;...&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;LineComment&quot;&gt;&lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; implementation&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;FunctionName&quot;&gt;self.load&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sequel&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;yield&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;my-app/foo_model&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;For what it's worth, I've started using &lt;code&gt;autoload&lt;/code&gt; more lately.  That negates the problem completely.&lt;/p&gt;

&lt;pre class=&quot;active4d&quot;&gt;&lt;code&gt;&lt;span class=&quot;LineComment&quot;&gt;&lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; my-app.rb&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sequel&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;TypeName&quot;&gt;MyApp&lt;/span&gt;
  autoload &lt;span class=&quot;UserDefinedConstant&quot;&gt;&lt;span class=&quot;UserDefinedConstant&quot;&gt;:&lt;/span&gt;FooModel&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;my-app/foo_model&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;LineComment&quot;&gt;&lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; config.rb&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;my-app&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;LibraryClassType&quot;&gt;Sequel&lt;/span&gt;.&lt;span class=&quot;FunctionName&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;Operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;...&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;LibraryClassType&quot;&gt;MyApp&lt;/span&gt;::&lt;span class=&quot;FunctionName&quot;&gt;FooModel&lt;/span&gt;.do_something
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This is Sinatra-specific, but always subclass from &lt;code&gt;Sinatra::Base&lt;/code&gt;.  I opt for the &lt;code&gt;classic Sinatra style&lt;/code&gt; a lot because it's so convenient.  But once I have something running and tested, I make it a full class.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using the classic style adds a lot of crufty methods to every object.  This can cause problems in mid to large projects.&lt;/li&gt;
&lt;li&gt;You can easily isolate and test these Sinatra classes with Rack Test.  &lt;a href=&quot;http://github.com/defunkt/resque/blob/master/lib/resque/server.rb#L1-7&quot;&gt;Resque's Server&lt;/a&gt; provides a good sample implementation &lt;a href=&quot;http://github.com/defunkt/resque/blob/master/lib/resque/server/test_helper.rb#L6-10&quot;&gt;with tests&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;


&lt;pre class=&quot;active4d&quot;&gt;&lt;code&gt;&lt;span class=&quot;Keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;TypeName&quot;&gt;MyAppTest&lt;span class=&quot;InheritedClass&quot;&gt; &lt;span class=&quot;InheritedClass&quot;&gt;&amp;lt;&lt;/span&gt; Test::Unit::TestCase&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;LibraryClassType&quot;&gt;Rack&lt;/span&gt;::&lt;span class=&quot;FunctionName&quot;&gt;Test&lt;/span&gt;::&lt;span class=&quot;FunctionName&quot;&gt;Methods&lt;/span&gt;

  &lt;span class=&quot;Keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;FunctionName&quot;&gt;app&lt;/span&gt;
    &lt;span class=&quot;LibraryClassType&quot;&gt;MyApp&lt;/span&gt;::&lt;span class=&quot;FunctionName&quot;&gt;Api&lt;/span&gt; &lt;span class=&quot;LineComment&quot;&gt;&lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; subclasses Sinatra::Base&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This problem also extends to libraries using Sinatra.  At first, I couldn't figure out why one of my older Sinatra apps still used the classic Sinatra DSL.  I got my answer when I converted it: &lt;a href=&quot;http://github.com/jamesgolick/classy_resources/blob/b822f02ed8101293a97bbb1c960fed8797346cbc/lib/classy_resources.rb#L121&quot;&gt;ClassyResources was including itself into main&lt;/a&gt;.  &lt;a href=&quot;http://twitter.com/technoweenie/status/10993934365&quot;&gt;I was not too pleased&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm assuming this code pre-dated &lt;a href=&quot;http://www.sinatrarb.com/extensions.html&quot;&gt;Sinatra's excellent extension API&lt;/a&gt;, so I spent an hour &lt;a href=&quot;http://github.com/technoweenie/classy_resources/commit/9ab32a4828c1496b9fe82c827780ac383aed6377&quot;&gt;registering the modules as proper Sinatra extensions&lt;/a&gt;.  I was glad I could focus my programmer rage into a good learning process.&lt;/p&gt;

&lt;p&gt;My &lt;a href=&quot;http://github.com/technoweenie/twitter-server/blob/master/lib/twitter_server.rb&quot;&gt;TwitterServer&lt;/a&gt; library serves as a good example of &lt;a href=&quot;http://github.com/technoweenie/twitter-server/blob/master/test/sinatra/account_test.rb#L3-20&quot;&gt;well-tested&lt;/a&gt; a Sinatra extension.&lt;/p&gt;

&lt;p&gt;Following these guidelines, I was able to load both of the Sinatra apps together with a simple 3-line rackup file.&lt;/p&gt;

&lt;pre class=&quot;active4d&quot;&gt;&lt;code&gt;&lt;span class=&quot;Keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;config&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
use &lt;span class=&quot;Variable&quot;&gt;MiniApp1&lt;/span&gt;
run &lt;span class=&quot;Variable&quot;&gt;MiniApp2&lt;/span&gt;

&lt;span class=&quot;LineComment&quot;&gt;&lt;span class=&quot;LineComment&quot;&gt;#&lt;/span&gt; thin -R config.ru start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;My only suggestion if you come across crappy libraries that muck with your &lt;code&gt;$LOAD_PATH&lt;/code&gt; is to fork away and push any patches upstream.  Sorry in advance if it's one of mine :)&lt;/p&gt;

&lt;p&gt;What good libraries out there handle this poorly?  Which ones are shining examples?  How do you handle similar issues?&lt;/p&gt;

</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:3:19:ruby-1-9-on-heroku</id>
    <published>2010-03-19T00:00:00-07:00</published>
    <updated>2010-03-19T00:00:00-07:00</updated>
    <link href="/2010/3/19/ruby-1-9-on-heroku" rel="alternate" type="text/html"/>
    <title>Ruby 1.9 on Heroku</title>
<content type="html">
&lt;p&gt;Heroku just pushed their &lt;a href=&quot;http://blog.heroku.com/archives/2010/3/5/public_beta_deployment_stacks/&quot;&gt;new deployment stacks to public beta&lt;/a&gt;.  You can now run your Heroku apps on Ruby 1.8.7 and 1.9 (as opposed to the old standard: 1.8.6).&lt;/p&gt;

&lt;p&gt;To test this out, I whipped up &lt;a href=&quot;http://ultraviolence.heroku.com/&quot;&gt;Ultraviolence&lt;/a&gt;, a quick wrapper around the &lt;a href=&quot;http://ultraviolet.rubyforge.org/&quot;&gt;Ultraviolet&lt;/a&gt; gem.  Ultraviolet will syntax highlight text, using parsed Textmate bundle files.  Any language that Textmate supports will work, using any theme that Textmate supports.&lt;/p&gt;

&lt;p&gt;There's also a web api if that's how you want to roll...&lt;/p&gt;

&lt;p&gt;I chose Ultraviolet because its installation in ruby 1.8.x was always a tricky issue.  Ruby needs the &lt;a href=&quot;http://www.geocities.jp/kosako3/oniguruma/&quot;&gt;Onigurama regex library&lt;/a&gt; to parse the Textmate bundles.  This means you have to install some software from a japanese geocities page and compile the onigurama gem.  Ruby 1.9 uses this regex library by default, so Ultraviolet is a snap to setup.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;http://heroku.com&quot;&gt;Heroku&lt;/a&gt; and &lt;a href=&quot;http://rvm.beginrescueend.com/&quot;&gt;RVM&lt;/a&gt;, it was pretty easy to get a ruby 1.9 app developed and deployed.&lt;/p&gt;

</content>
  </entry>

  <entry xml:base="http://techno-weenie.net/">
    <author>
      <name>rick</name>
    </author>
    <id>tag:techno-weenie.net,2010:2:25:my-talk-about-twitter-node-at-pdxjs</id>
    <published>2010-02-25T00:00:00-08:00</published>
    <updated>2010-02-25T00:00:00-08:00</updated>
    <link href="/2010/2/25/my-talk-about-twitter-node-at-pdxjs" rel="alternate" type="text/html"/>
    <title>My talk about Twitter-Node at PDXJS</title>
<content type="html">
&lt;p&gt;I was recently invited to talk about my &lt;a href=&quot;http://github.com/technoweenie/twitter-node&quot;&gt;Twitter Node&lt;/a&gt; project at last night's PDX Javascript Admirers meeting.  I was really nervous about giving my first talk in several years, but I did alright.  My slides are &lt;a href=&quot;http://pdxjs-twitter-node.heroku.com/&quot;&gt;up on Heroku&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The big win of the talk, however, was Scott's &lt;a href=&quot;http://github.com/schacon/showoff&quot;&gt;showoff&lt;/a&gt; app for composing presentations.&lt;/p&gt;

&lt;p&gt;Showoff is a sinatra app that builds a presentation from subdirectories of markdown files.  In the past, I'd spend a lot of time trying to make Keynote presentations look pretty, or fiddling with HTML for raw slides.  You know, I just don't care to do a lot of that stuff.  Showoff lets me focus on the content.&lt;/p&gt;

&lt;p&gt;One technique I really enjoyed, was showing a block of code in multiple slides and different comments.  The comments 'animate', pointing at whatever it is I was talking about.&lt;/p&gt;

&lt;p&gt;Also, the rapid Heroku deployment saved us a bit of hassle.  I didn't have the right micro adapter for my laptop, so I was able to run the presentation just fine from another laptop.&lt;/p&gt;

</content>
  </entry>

</feed>
