<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
 
 <title>The So-Called Writings of Michael Gorsuch</title>
 
 <link href="http://gorsuch.github.com/" />
 <updated>2011-09-09T09:34:54-07:00</updated>
 <id>http://gorsuch.github.com/</id>
 <author>
   <name>Michael Gorsuch</name>
   <email>michael.gorsuch@gmail.com</email>
 </author>

 
 <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/gorsuch" /><feedburner:info uri="gorsuch" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
   <title>Sucker Punching the Algorithm with Redis</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/q-dsLwOM_7A/sucker-punching-the-algorithm.html" />
   <updated>2011-09-09T00:00:00-07:00</updated>
   <id>http://gorsuch.github.com/2011/09/09/sucker-punching-the-algorithm</id>
   <content type="html">&lt;p&gt;Last night I attended my first &lt;a href="http://ok-ruby.org/"&gt;OK.rb&lt;/a&gt; meeting.  I moved back to the &lt;span class="caps"&gt;OKC&lt;/span&gt; area from New York several months ago, but it took me some time to get things in order before I could start attending.  You know &amp;#8211; kids, man.&lt;/p&gt;
&lt;p&gt;Anyway, I have nothing but good things to say about this group.  It&amp;#8217;s full of smarts and enthusiasm &amp;#8211; the sort of thing any hacker-type should be surrounding himself with.  Even better, they work on actual code problems.  It&amp;#8217;s not just fanboy hour; they are actually examining problems and working them out together.  A great way to sharpen skills for sure.&lt;/p&gt;
&lt;p&gt;The problem presented was clear: &lt;em&gt;understand this code, then optimize it&lt;/em&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# code example provided by James Gray II (http://twitter.com/jeg2)&lt;/span&gt;
&lt;span class="no"&gt;DICTIONARY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/usr/share/dict/words&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_signature&lt;/span&gt;
    &lt;span class="n"&gt;downcase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;^a-z&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;scrambled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_signature&lt;/span&gt;
&lt;span class="nb"&gt;abort&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;USAGE:  &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vg"&gt;$PROGRAM_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; SCRAMBLED_WORD&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;scrambled&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty?&lt;/span&gt;

&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DICTIONARY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_signature&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;scrambled&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The code is clear enough: we&amp;#8217;re monkey patching the &lt;code&gt;String&lt;/code&gt; class, allowing us to generate a simple signature for a given word.  We convert the word to lowercase, rip out any non-alphabetic characters, and then place the characters in alphabetically order.&lt;/p&gt;
&lt;p&gt;An example of it in action via &lt;code&gt;irb&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;ruby-1.9.2-p180 :008 &amp;gt; &lt;span class="s2"&gt;&amp;quot;test&amp;quot;&lt;/span&gt;.to_signature
 &lt;span class="o"&gt;=&lt;/span&gt;&amp;gt; &lt;span class="s2"&gt;&amp;quot;estt&amp;quot;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It&amp;#8217;s worth noting that signatures are not necessarily unique:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;ruby-1.9.2-p180 :010 &amp;gt; &lt;span class="s2"&gt;&amp;quot;host&amp;quot;&lt;/span&gt;.to_signature
 &lt;span class="o"&gt;=&lt;/span&gt;&amp;gt; &lt;span class="s2"&gt;&amp;quot;host&amp;quot;&lt;/span&gt; 
ruby-1.9.2-p180 :011 &amp;gt; &lt;span class="s2"&gt;&amp;quot;shot&amp;quot;&lt;/span&gt;.to_signature
 &lt;span class="o"&gt;=&lt;/span&gt;&amp;gt; &lt;span class="s2"&gt;&amp;quot;host&amp;quot;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;So, our code takes a word as an input, obtains its signature, and then finds every word in the dictionary which has the same signature &amp;#8211; a very brute force approach, no?  This leads to the next part: optimization.&lt;/p&gt;
&lt;h3&gt;Measure First&lt;/h3&gt;
&lt;p&gt;The first step is to start measuring execution time.  I&amp;#8217;m paraphrasing, but our first move was to wrap our algorithm in a method and include some simple debug timing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;DICTIONARY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/usr/share/dict/words&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_signature&lt;/span&gt;
    &lt;span class="n"&gt;downcase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;^a-z&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;descramble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scrambled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DICTIONARY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_signature&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;scrambled&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;scrambled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_signature&lt;/span&gt;
&lt;span class="nb"&gt;abort&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;USAGE:  &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vg"&gt;$PROGRAM_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; SCRAMBLED_WORD&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;scrambled&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty?&lt;/span&gt;

&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
&lt;span class="n"&gt;descramble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scrambled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;An initial run looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ruby descramble.rb &lt;span class="nb"&gt;test&lt;/span&gt;
sett
stet
&lt;span class="nb"&gt;test&lt;/span&gt;
2.703979
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Alright.  Our algorithm takes 2.7 seconds to brute force the word list.&lt;/p&gt;
&lt;h3&gt;First Improvements&lt;/h3&gt;
&lt;p&gt;The first improvement that came to the group mind was to see if the first word of each dictionary word was even included in our signature.  If it is not, there is no need to run the expensive &lt;code&gt;to_signature&lt;/code&gt; method.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;descramble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DICTIONARY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;scrambled&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;include?&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_signature&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;sig&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;A clear win:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ruby descramble.rb &lt;span class="nb"&gt;test&lt;/span&gt;
sett
stet
&lt;span class="nb"&gt;test&lt;/span&gt;
0.594181
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Nice!  That&amp;#8217;s about 4.5 times faster than the original.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Redis Makes it Fast!&lt;/h3&gt;
&lt;p&gt;As the session wore on, we were able to work out some seeming improvements, but nothing that made a huge difference.  The only other thing we discussed would be to pre-index the word file, but that seemed to be &lt;em&gt;cheating&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In the real world, though, I don&amp;#8217;t think that there really is such a thing.  We want to make this thing &lt;em&gt;fast&lt;/em&gt;, and the only way we&amp;#8217;re really going to do that is to index the file before hand.  Upon returning home, I quickly whipped up a solution using &lt;a href="http://redis.io"&gt;Redis&lt;/a&gt; as my data store.&lt;/p&gt;
&lt;p&gt;First, we pre-load the data:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# cheat.rb - preload our word data into redis sets, using the signature as the key&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;redis&amp;#39;&lt;/span&gt;

&lt;span class="no"&gt;DICTIONARY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/usr/share/dict/words&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_signature&lt;/span&gt;
    &lt;span class="n"&gt;downcase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;^a-z&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# our interface to the local redis instance&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
&lt;span class="c1"&gt;# go ahead and clear out anything that we might have in here&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flushall&lt;/span&gt;

&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DICTIONARY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sadd&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;That takes about 17 seconds to run, but we only have to do it &lt;em&gt;once&lt;/em&gt;.  After that, the indexed data persists and we can query it as needed.&lt;/p&gt;
&lt;p&gt;Our app changes to rely upon Redis:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;redis&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_signature&lt;/span&gt;
    &lt;span class="n"&gt;downcase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;^a-z&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;descramble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;smembers&lt;/span&gt; &lt;span class="n"&gt;sig&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;scrambled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_signature&lt;/span&gt;
&lt;span class="nb"&gt;abort&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;USAGE:  &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vg"&gt;$PROGRAM_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; SCRAMBLED_WORD&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;scrambled&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty?&lt;/span&gt;

&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
&lt;span class="n"&gt;descramble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scrambled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The performance improvement is obvious:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ruby descramble.rb &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt;
stet
sett
0.001002
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;0.001002 seconds!  That&amp;#8217;s nearly 2,700 times as fast as the original piece of code.  Ah, the spoils of cheating!&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Of course, Redis is not the only choice here, but given the scope of the problem, it seemed like a fair pick.  It is also such a &lt;em&gt;cheap&lt;/em&gt; tool, meaning that there isn&amp;#8217;t much to getting it running and putting it to quick use.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/q-dsLwOM_7A" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2011/09/09/sucker-punching-the-algorithm.html</feedburner:origLink></entry>
 
 <entry>
   <title>Automate Your Development Environments.  Please.</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/h6gxyoIMcTg/automated-development-environments.html" />
   <updated>2011-05-06T00:00:00-07:00</updated>
   <id>http://gorsuch.github.com/2011/05/06/automated-development-environments</id>
   <content type="html">&lt;p&gt;&lt;em&gt;[note: much of this is inspired from what I&amp;#8217;ve learned from my peers at &lt;a href="http://heroku.com"&gt;Heroku&lt;/a&gt;.  If this sounds like your kind of place, &lt;a href="http://jobs.heroku.com/"&gt;we are hiring&lt;/a&gt;.]&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Your team&amp;#8217;s development environments should be managed by automated processes: you should be able to fabricate a clone of production in a manner of minutes. If your team has to invest days in the setup of an environment only to have everyone drift apart a few weeks later, you are probably doing it wrong.&lt;/p&gt;
&lt;p&gt;Give strong consideration to employeeing the likes of &lt;a href="http://vagrantup.com/docs/getting-started/why.html"&gt;Vagrant&lt;/a&gt; (or something similar) in your environment.  If you rely on &lt;span class="caps"&gt;AWS&lt;/span&gt;, put something like &lt;a href="http://aws.amazon.com/cloudformation/"&gt;CloudFormation&lt;/a&gt; to use.&lt;/p&gt;
&lt;p&gt;Your production, staging, and development environments should be managed by something like &lt;a href="http://wiki.opscode.com/display/chef/Home"&gt;Chef&lt;/a&gt; or &lt;a href="http://www.puppetlabs.com/"&gt;Puppet&lt;/a&gt; (or something else, but whatever you do, don&amp;#8217;t roll your own).  No one-off machines.  As much as I love the variety and differences among my peers, I find it intolerable in systems.  And so should you.&lt;/p&gt;
&lt;p&gt;If you do this, your development team will be able to walk in each morning and boot a complete development cloud.  As each system boots, it will execute the same build procedures as used in production, thus allowing those procedures to be put to the test several times a day.  Problems and bad practices will make themselves known quickly.  Your development environment will be regularly refreshed, preventing the dreaded drift that plagues so many.  Even better, you will be working on an environment that mirrors production, thus eliminating the very cause of most &amp;#8220;cowboy&amp;#8221; behavior.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/h6gxyoIMcTg" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2011/05/06/automated-development-environments.html</feedburner:origLink></entry>
 
 <entry>
   <title>Fixing the Author on a Git Commit</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/2XYEai6qdXc/fixing-author-on-a-commit.html" />
   <updated>2011-03-31T00:00:00-07:00</updated>
   <id>http://gorsuch.github.com/2011/03/31/fixing-author-on-a-commit</id>
   <content type="html">&lt;p&gt;Ugh.  So you just made a commit, take a look at the revlog and realize that you did it as root.  How embarrassing.  Assuming you haven&amp;#8217;t pushed, however, you can fix it without much trouble.&lt;/p&gt;
&lt;p&gt;Try this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git commit --amend --author &lt;span class="s2"&gt;&amp;quot;Your Name &amp;lt;you@domain.com&amp;gt;&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/2XYEai6qdXc" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2011/03/31/fixing-author-on-a-commit.html</feedburner:origLink></entry>
 
 <entry>
   <title>Squashing Git Commits</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/_5Gv-OR1f1M/squashing-git-commits.html" />
   <updated>2011-03-30T00:00:00-07:00</updated>
   <id>http://gorsuch.github.com/2011/03/30/squashing-git-commits</id>
   <content type="html">&lt;p&gt;All too often while modifying some &lt;a href="http://www.opscode.com/chef/"&gt;chef recipe&lt;/a&gt;, I make commit after commit after commit.  Many times my local branch&amp;#8217;s commit messages look a little like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;commit 473c878c8bf249a474da9faf57333288aaa0d22f
Author: Michael Gorsuch &amp;lt;michael.gorsuch@gmail.com&amp;gt;
Date:   Mon Mar 21 16:53:10 2011 -0500

    recipes&lt;span class="o"&gt;[&lt;/span&gt;apache2&lt;span class="o"&gt;]&lt;/span&gt;: crap. typo.

commit 27bda2b580c8fb697615fca84933d2f89d26c70b
Author: Michael Gorsuch &amp;lt;michael.gorsuch@gmail.com&amp;gt;
Date:   Mon Mar 21 08:58:40 2011 -0500

    recipes&lt;span class="o"&gt;[&lt;/span&gt;apache2&lt;span class="o"&gt;]&lt;/span&gt;: added ability to generate sites based on attributes

...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I don&amp;#8217;t like merging that sort of mess back in to the main branch, as it can cause some headaches when you have a large team that would rather be doing other things than watching you backtrack all over the codebase.&lt;/p&gt;
&lt;p&gt;A colleague asked me to check into git&amp;#8217;s rebase functionality.  What I found was scary but ever so useful.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s squash these commits together, making it look as if the typo had never occurred.&lt;/p&gt;
&lt;p&gt;Use the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git rebase -i HEAD~2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;-i&lt;/code&gt; tells git to use interactive mode, i.e. launch $&lt;span class="caps"&gt;EDITOR&lt;/span&gt;, and &lt;code&gt;HEAD~2&lt;/code&gt; is more or less saying get the past two commits.&lt;/p&gt;
&lt;p&gt;When I run this, &lt;span class="caps"&gt;VIM&lt;/span&gt; gives me the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;pick 0259fff recipes&lt;span class="o"&gt;[&lt;/span&gt;apache2&lt;span class="o"&gt;]&lt;/span&gt;: added ability to generate sites based on attributes
pick ad68659 recipes&lt;span class="o"&gt;[&lt;/span&gt;apache2&lt;span class="o"&gt;]&lt;/span&gt;: crap. typo.

&lt;span class="c"&gt;# Rebase be9f5a0..ad68659 onto be9f5a0&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Commands:&lt;/span&gt;
&lt;span class="c"&gt;#  p, pick = use commit&lt;/span&gt;
&lt;span class="c"&gt;#  r, reword = use commit, but edit the commit message&lt;/span&gt;
&lt;span class="c"&gt;#  e, edit = use commit, but stop for amending&lt;/span&gt;
&lt;span class="c"&gt;#  s, squash = use commit, but meld into previous commit&lt;/span&gt;
&lt;span class="c"&gt;#  f, fixup = like &amp;quot;squash&amp;quot;, but discard this commit&amp;#39;s log message&lt;/span&gt;
&lt;span class="c"&gt;#  x, exec = run command (the rest of the line) using shell&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# If you remove a line here THAT COMMIT WILL BE LOST.&lt;/span&gt;
&lt;span class="c"&gt;# However, if you remove everything, the rebase will be aborted.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Let&amp;#8217;s go ahead and do a &lt;code&gt;fixup&lt;/code&gt; here, pretending that this change never happened.  Just change the top two lines to look like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;pick 0259fff recipes&lt;span class="o"&gt;[&lt;/span&gt;apache2&lt;span class="o"&gt;]&lt;/span&gt;: added ability to generate sites based on attributes
f ad68659 recipes&lt;span class="o"&gt;[&lt;/span&gt;apache2&lt;span class="o"&gt;]&lt;/span&gt;: crap. typo.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now if you exit out of vim, your change will take place.  Look at the commit log now:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;commit 8654333d73b46f93ae111b40986dff8329b813a2
Author: Michael Gorsuch &amp;lt;michael.gorsuch@gmail.com&amp;gt;
Date:   Mon Mar 21 08:58:40 2011 -0500

    recipes&lt;span class="o"&gt;[&lt;/span&gt;apache2&lt;span class="o"&gt;]&lt;/span&gt;: added ability to generate sites based on attributes
...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Fixed and you look more perfect than ever.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/_5Gv-OR1f1M" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2011/03/30/squashing-git-commits.html</feedburner:origLink></entry>
 
 <entry>
   <title>Build Debian Packages Easily with FPM</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/obf_PMZKajI/simple-package-management-with-fpm.html" />
   <updated>2011-03-22T00:00:00-07:00</updated>
   <id>http://gorsuch.github.com/2011/03/22/simple-package-management-with-fpm</id>
   <content type="html">&lt;p&gt;Today I discovered that the version of &lt;a href="http://collectd.org/"&gt;collectd&lt;/a&gt; that ships with Ubuntu Lucid has trouble with &lt;span class="caps"&gt;HTTP&lt;/span&gt; basic authentication.  To verify, I grabbed the latest tarball, which is several revisions beyond the Ubuntu package&amp;#8230; and it worked.  So what do I do now?  Debian packages are fast, easy to distribute, and work well with &lt;a href="http://www.opscode.com/chef/"&gt;Chef&lt;/a&gt;&amp;#8230; but they are a pain in the ass to build for people who don&amp;#8217;t do it regularly.&lt;/p&gt;
&lt;p&gt;Enter &lt;a href="https://github.com/jordansissel/fpm"&gt;&lt;span class="caps"&gt;FPM&lt;/span&gt;&lt;/a&gt;, or &amp;#8220;Effing Package Management&amp;#8221;.  &lt;a href="http://www.semicomplete.com"&gt;Jordan Sissel&lt;/a&gt; built this so he didn&amp;#8217;t have to fight with the package process any longer.  Kudos to him, because this freed up my afternoon.&lt;/p&gt;
&lt;p&gt;Just so one more example gets out there, here is how you can use it.&lt;/p&gt;
&lt;p&gt;First off, grab the tarball of the software you want to convert into a package.  I am going with collectd here, but anything that follows the traditional &lt;code&gt;make install&lt;/code&gt; route ought to work.  Go ahead and run &lt;code&gt;./configure&lt;/code&gt; and &lt;code&gt;make install&lt;/code&gt;.  I&amp;#8217;ll wait.&lt;/p&gt;
&lt;p&gt;As it stands now, collectd installs in &lt;code&gt;/opt/collectd&lt;/code&gt; out of the box.  Assuming that is how we want to keep it for production, we can go ahead and build our custom package.&lt;/p&gt;
&lt;p&gt;Now install fpm:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gem install fpm
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Build your deb with the following commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;fpm -n collectd -v 4.9.4-custom-1 -t deb -s dir /opt/collectd
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;A little explanation will go a long way here. &lt;code&gt;-n&lt;/code&gt; gives the new package a name.  &lt;code&gt;-v&lt;/code&gt; allows us to state the version.  I appended &lt;code&gt;-custom-1&lt;/code&gt; as that should help me distinguish from the vendor.  &lt;code&gt;-t&lt;/code&gt; lets me specify the package type, such as rpm or deb.  &lt;code&gt;-s dir&lt;/code&gt; states that I am building my package from the contents of a directory.  Finally, the last item tells fpm which directory to build from.&lt;/p&gt;
&lt;p&gt;Clear?&lt;/p&gt;
&lt;p&gt;Upon completion, you should have a fresh deb in your directory.  I installed mine like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo dpkg -i collectd_4.9.4-custom-1_i686.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Big thanks to &lt;a href="http://www.semicomplete.com/"&gt;Jordan&lt;/a&gt; for making life better for all of us.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/obf_PMZKajI" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2011/03/22/simple-package-management-with-fpm.html</feedburner:origLink></entry>
 
 <entry>
   <title>A Taste of Cuba</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/TgmYjFOAyAw/a-taste-of-cuba.html" />
   <updated>2011-03-22T00:00:00-07:00</updated>
   <id>http://gorsuch.github.com/2011/03/22/a-taste-of-cuba</id>
   <content type="html">&lt;p&gt;Late last week I ran across a neat little web framework called &lt;a href="http://cuba.is/"&gt;Cuba&lt;/a&gt;.  I write small tools, so most of my work can easily fit into something like this.  Something that it has that I&amp;#8217;ve been dying for: the ability to match urls components only once.  See my snippit below for an example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;require &lt;span class="s1"&gt;&amp;#39;cuba&amp;#39;&lt;/span&gt;

Cuba.define &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;  &lt;/span&gt;on &lt;span class="s2"&gt;&amp;quot;api/v1&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;    &lt;/span&gt;on &lt;span class="s2"&gt;&amp;quot;rooms/:id&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; |id|
      @room &lt;span class="o"&gt;=&lt;/span&gt; Room&lt;span class="o"&gt;[&lt;/span&gt;id&lt;span class="o"&gt;]&lt;/span&gt; 

      &lt;span class="c"&gt;# below could be hit like so:&lt;/span&gt;
      &lt;span class="c"&gt;# /api/v1/rooms/123/messages&lt;/span&gt;
      on &lt;span class="s2"&gt;&amp;quot;messages&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;        &lt;/span&gt;res.write &lt;span class="s2"&gt;&amp;quot;#{@room.name} has #{@room.messages.size} messages.&amp;quot;&lt;/span&gt;
      end
    end 
  end

  on &lt;span class="s2"&gt;&amp;quot;api/v2&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c"&gt;# ...&lt;/span&gt;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;#8217;ll be applying this to a real-life production problem this week.  For more information, see this &lt;a href="http://files.soveran.com/cuba/#0"&gt;great presentation&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/TgmYjFOAyAw" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2011/03/22/a-taste-of-cuba.html</feedburner:origLink></entry>
 
 <entry>
   <title>Rolling Your Own Self-Signed SSL Certs</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/Tj1jf2Puceo/rolling-your-own-ssl-certs.html" />
   <updated>2011-03-20T00:00:00-07:00</updated>
   <id>http://gorsuch.github.com/2011/03/20/rolling-your-own-ssl-certs</id>
   <content type="html">&lt;p&gt;This isn&amp;#8217;t rocket science, but I have to do this so rarely that I end up having to Google it due to lack of familiarity.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s say you want to build your own self-signed certs.  Here is how you go about doing that.&lt;/p&gt;
&lt;p&gt;First we build the private key:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;openssl genrsa -des3 -out server.key 2048
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This above action will require you to set a password on the key.  For most use cases (say, sticking it on nginx), you&amp;#8217;ll need the password removed.  Do so like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cp server.key server.key.org
&lt;span class="nv"&gt;$ &lt;/span&gt;openssl rsa -in server.key.org -out server.key
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Next, create the &lt;span class="caps"&gt;CSR&lt;/span&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;openssl req -new -key server.key -out server.csr
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Finally, build the cert from the &lt;span class="caps"&gt;CSR&lt;/span&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There you go.  Easy peasy.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/Tj1jf2Puceo" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2011/03/20/rolling-your-own-ssl-certs.html</feedburner:origLink></entry>
 
 <entry>
   <title>Falling in Love With My Work Again</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/nEAHnK80MHM/falling-in-love-again.html" />
   <updated>2011-03-11T00:00:00-08:00</updated>
   <id>http://gorsuch.github.com/2011/03/11/falling-in-love-again</id>
   <content type="html">&lt;p&gt;In October of last year I made a &lt;a href="/2010/11/12/returning-to-ruby.html"&gt;small note&lt;/a&gt; that I had re-introduced myself to the Ruby ecosystem.  It was a willed attempt to get some deeper parts of my mind working again after several years of working predominantly with tools that just weren&amp;#8217;t doing it for me.  Language and platform agnosticism has its place and benefits, but it was time for me to get back to digging a little more joy out of my work.&lt;/p&gt;
&lt;p&gt;Once that decision was made, things began falling into place.  I &lt;a href="/2010/12/03/introducing-logtrend.html"&gt;crafted a few tools&lt;/a&gt; and got my socks blown off by learning a little about &lt;a href="/2011/01/05/diving-into-rspec.html"&gt;behavior driven development&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was then fortunate enough to pick up a job at &lt;a href="http://heroku.com"&gt;Heroku&lt;/a&gt;, a shop that has changed the world of application delivery for the better.  And it will continue to blow your mind.  I promise.  I&amp;#8217;m focusing on ops engineering, helping craft an environment that lives entirely in &lt;a href="http://aws.amazon.com"&gt;&lt;span class="caps"&gt;AWS&lt;/span&gt;&lt;/a&gt;, implementing smart configuration management practices using tools like &lt;a href="/2011/02/27/introduction-to-stem.html"&gt;stem&lt;/a&gt; and &lt;a href="http://www.opscode.com/chef/"&gt;chef&lt;/a&gt;, and building sharp, small-scoped components wherever possible.  It&amp;#8217;s challenging and I think that it will continue to push me hard.&lt;/p&gt;
&lt;p&gt;The ops team is entirely distributed, so that&amp;#8217;s a sweet perk.  Because of that, my family and I can now relocate back to our home town of Oklahoma City.  I&amp;#8217;m really looking forward to connecting with the budding tech scene there.  &lt;a href="http://okccoco.com/"&gt;People are making things&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I am writing this on the airplane as I fly back from my first on-site visit with the Heroku team.  I now know that I made the right choice to come aboard as I am now fully convinced that everyone in that building is smarter than I am.  Which is great.  I have something to aim for.&lt;/p&gt;
&lt;p&gt;So things are good.  Hope they are for you, too.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/nEAHnK80MHM" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2011/03/11/falling-in-love-again.html</feedburner:origLink></entry>
 
 <entry>
   <title>An Introduction to Stem - How to Easily Fabricate EC2 Instances</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/LV41QOxexEw/introduction-to-stem.html" />
   <updated>2011-02-27T16:30:00-08:00</updated>
   <id>http://gorsuch.github.com/2011/02/27/introduction-to-stem</id>
   <content type="html">&lt;p&gt;When I began working with the &lt;a href="http://heroku.com"&gt;Heroku&lt;/a&gt; team, one of the first tools I had to learn was &lt;a href="http://github.com/pvh/stem"&gt;Stem&lt;/a&gt;.  Originally developed by &lt;a href="https://github.com/pvh"&gt;Peter van Hardenberg&lt;/a&gt; and based upon &lt;a href="https://github.com/orionz/judo"&gt;Judo&lt;/a&gt;, Stem was built to ease the the pain that comes about when building custom Amazon EC2 instances.&lt;/p&gt;
&lt;h2&gt;Our Simple Example&lt;/h2&gt;
&lt;p&gt;Assuming that you are like me in that you have both a short attention span and little time on your hands, we&amp;#8217;ll keep things simple.  In subsequent posts I&amp;#8217;ll go into more detail and better practices, but for now we&amp;#8217;ll focus on creating a simple &amp;#8216;base&amp;#8217; &lt;span class="caps"&gt;AMI&lt;/span&gt; that we can fabricate to our heart&amp;#8217;s content.&lt;/p&gt;
&lt;p&gt;Once complete, we should be able to launch 100 of these suckers if we had the means to pay for them.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;First, install stem:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo gem install stem
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Then add your &lt;span class="caps"&gt;AWS&lt;/span&gt; credentials to your environment (I add mine to my &lt;code&gt;~/.profile&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;XXXXXX
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;XXXXXX
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Don&amp;#8217;t forget to do this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;. ~/.profile
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Next&amp;#8230;&lt;/p&gt;
&lt;h2&gt;Start Building!&lt;/h2&gt;
&lt;p&gt;Run the following to setup the basic directory structure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mkdir -p stems/bare stems/base
&lt;span class="nv"&gt;$ &lt;/span&gt;touch stems/bare/bare.json
&lt;span class="nv"&gt;$ &lt;/span&gt;touch stems/base/base.json
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We&amp;#8217;ll start by building out a &amp;#8216;bare&amp;#8217; instance, which will just be a standard public &lt;span class="caps"&gt;AMI&lt;/span&gt;.  Once we get this guy up and running, we&amp;#8217;ll make a few changes and then build a new &lt;span class="caps"&gt;AMI&lt;/span&gt; out of it for future use.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s take a look at our &lt;code&gt;bare.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="o"&gt;{&lt;/span&gt; 
  &lt;span class="s2"&gt;&amp;quot;ami&amp;quot;&lt;/span&gt;                       : &lt;span class="s2"&gt;&amp;quot;ami-4a0df923&amp;quot;&lt;/span&gt;,
  &lt;span class="s2"&gt;&amp;quot;instance_type&amp;quot;&lt;/span&gt;             : &lt;span class="s2"&gt;&amp;quot;t1.micro&amp;quot;&lt;/span&gt;,
  &lt;span class="s2"&gt;&amp;quot;key_name&amp;quot;&lt;/span&gt;                  : &lt;span class="s2"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There are three parameters here, all of which are fairly self-explanatory.  &lt;code&gt;ami&lt;/code&gt; refers to the &lt;span class="caps"&gt;AMI&lt;/span&gt; we want to launch, being an &lt;a href="http://releases.ubuntu.com/lucid/"&gt;Ubuntu Lucid&lt;/a&gt; instance in this case. &lt;code&gt;instance_type&lt;/code&gt; tells us what sort of system.  In our case we&amp;#8217;re going to use a &lt;code&gt;t1.micro&lt;/code&gt;, which is the cheapest instance size available.  No need to break the bank here.  &lt;code&gt;key_name&lt;/code&gt; defines which &lt;span class="caps"&gt;SSH&lt;/span&gt; key we&amp;#8217;re going to use.  This name is managed in the &lt;a href="http://aws.amazon.com"&gt;&lt;span class="caps"&gt;AWS&lt;/span&gt; Console&lt;/a&gt;, so substitute as you need to.&lt;/p&gt;
&lt;p&gt;Now that all of this is understood, let us go ahead and launch our bare instance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;stem launch bare/bare.json
New instance ID: i-XXXXXX
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;That returned line, &lt;code&gt;New instance ID: i-XXXXXX&lt;/code&gt;, tell us the instance id that we&amp;#8217;ll work with.  Now do this to get a list of running instances:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;stem list
------------------------------------------
Instances
------------------------------------------
i-17e5d17b      50.16.66.78     running         default              ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Great.  We have a running system and an IP to connect to.  Just do this to get going:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh ubuntu@50.16.66.78
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You should get a shell.  Now let us make a minor customization to our instance.  You can obviously do something more elaborate, but this will serve as a demonstration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;FOO &amp;gt; BAR
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now we have a file named &amp;#8216;&lt;span class="caps"&gt;FOO&lt;/span&gt;&amp;#8217; in the home directory of the ubuntu user.  Good enough for me.  Let&amp;#8217;s build a custom &lt;span class="caps"&gt;AMI&lt;/span&gt; off of this.&lt;/p&gt;
&lt;p&gt;First, halt the instance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo halt
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Run &lt;code&gt;stem list&lt;/code&gt; a couple of times to ensure that the instance has entered the &lt;code&gt;stopped&lt;/code&gt; state:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;stem list
------------------------------------------
Instances
------------------------------------------
i-17e5d17b      50.16.66.78     stopped         default              ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Capture it like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;stem create base i-17e5d17b
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After a few minutes (it could take longer depending on how &lt;span class="caps"&gt;AWS&lt;/span&gt; is behaving), you should be able to run &lt;code&gt;stem list&lt;/code&gt; and see something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;stem list
------------------------------------------
Instances
------------------------------------------
i-17e5d17b      50.16.66.78     stopped         default              ...
------------------------------------------
AMIs
------------------------------------------
base  ami-487d8e21
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Woo!  You now have a &lt;code&gt;base&lt;/code&gt; &lt;span class="caps"&gt;AMI&lt;/span&gt; ready with your customizations to be deployed as often as you like.  You could do this via the &lt;span class="caps"&gt;AWS&lt;/span&gt; Console&amp;#8230; or you could just create a new Stem definition to make this possible.  We&amp;#8217;re going to go the Stem route.&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;stems/base/base.json&lt;/code&gt; with the following info:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="o"&gt;{&lt;/span&gt; 
  &lt;span class="s2"&gt;&amp;quot;ami&amp;quot;&lt;/span&gt;                       : &lt;span class="s2"&gt;&amp;quot;ami-487d8e21&amp;quot;&lt;/span&gt;,
  &lt;span class="s2"&gt;&amp;quot;instance_type&amp;quot;&lt;/span&gt;             : &lt;span class="s2"&gt;&amp;quot;t1.micro&amp;quot;&lt;/span&gt;,
  &lt;span class="s2"&gt;&amp;quot;key_name&amp;quot;&lt;/span&gt;                  : &lt;span class="s2"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, the only difference between this definition and the &lt;code&gt;bare&lt;/code&gt; one is the &lt;span class="caps"&gt;AMI&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;You can now launch it like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;stem launch stems/base/base.json
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you give it a second to boot and then &lt;span class="caps"&gt;SSH&lt;/span&gt; into the box, you&amp;#8217;ll see that our &lt;code&gt;BAR&lt;/code&gt; file is in place.&lt;/p&gt;
&lt;p&gt;Pretty cool, eh?  Once you are done playing around, make sure you go back and run &lt;code&gt;stem destroy&lt;/code&gt; against each running instance so you don&amp;#8217;t rack up unexpected charges.&lt;/p&gt;
&lt;p&gt;In future articles I&amp;#8217;ll talk more about better ways to bootstrap these instances, configuration management, and using the stem api instead of the command line tool.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/LV41QOxexEw" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2011/02/27/introduction-to-stem.html</feedburner:origLink></entry>
 
 <entry>
   <title>Hi Heroku!</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/3qGZrA_P8AI/hi-heroku.html" />
   <updated>2011-02-27T00:00:00-08:00</updated>
   <id>http://gorsuch.github.com/2011/02/27/hi-heroku</id>
   <content type="html">&lt;p&gt;Last week I joined the ops team at &lt;a href="http://heroku.com"&gt;Heroku&lt;/a&gt;.  It&amp;#8217;s an exciting opportunity and has already paved over my old thinking on operations.  I have a lot to learn.&lt;/p&gt;
&lt;p&gt;I keep telling myself that I need to port this over to a Heroku app or change the domain or something&amp;#8230; but we&amp;#8217;ll see.  I have enough to think about for the time being.&lt;/p&gt;
&lt;p&gt;Cheers!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/3qGZrA_P8AI" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2011/02/27/hi-heroku.html</feedburner:origLink></entry>
 
 <entry>
   <title>Diving into RSpec</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/BK5rL-50HQY/diving-into-rspec.html" />
   <updated>2011-01-05T17:30:00-08:00</updated>
   <id>http://gorsuch.github.com/2011/01/05/diving-into-rspec</id>
   <content type="html">&lt;p&gt;For Christmas, my wife bought me a copy of &lt;em&gt;&lt;a href="http://www.pragprog.com/titles/achbd/the-rspec-book"&gt;The RSpec Book&lt;/a&gt;&lt;/em&gt; by The Pragmatic Programmers.  It has changed my entire approach to development.&lt;/p&gt;
&lt;p&gt;Over the past several years, I&amp;#8217;ve grown apart from &lt;span class="caps"&gt;TDD&lt;/span&gt;.  I began using &lt;a href="http://guides.rubyonrails.org/testing.html"&gt;Test::Unit&lt;/a&gt; with my early Rails projects, but always found that things became too brittle.  My tests would often dive way too deep into the implementation of my classes and then break when I would refactor.  After much frustration, I would eventually abandon automated testing altogether.  I highly doubt the fault lies with the library &amp;#8212; I didn&amp;#8217;t really grasp the philosophy and therefore sucked when trying to apply it.&lt;/p&gt;
&lt;p&gt;Everything changed with &lt;em&gt;The RSpec Book&lt;/em&gt;.  I now have a better grasp (maybe): focus on defining and testing behavior rather than the internals.  After working through all the examples in the first five chapters this evening, I decided to apply RSpec to &lt;a href="http://imranontech.com/2007/01/24/using-fizzbuzz-to-find-developers-who-grok-coding/"&gt;Imran&amp;#8217;s FizzBuzz problem&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If we decide that a resonable way to solve this is to create a class named &lt;code&gt;FizzBuzz&lt;/code&gt; and a &lt;code&gt;calculate&lt;/code&gt; method to do the work, we can start out with a bare spec:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;FizzBuzz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;#calculate&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;returns &amp;#39;Fizz&amp;#39; for all multiples of 3&amp;quot;&lt;/span&gt; 
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;returns &amp;#39;Buzz&amp;#39; for all multiples of 5&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;returns &amp;#39;FizzBuzz&amp;#39; for all multiples of 3 and 5&amp;quot;&lt;/span&gt; 
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;returns the passed number if not a multiple of 3 or 5&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This is all very readable, and encapsulates the problem quite well.  I can marvel at this for hours&amp;#8230;&lt;/p&gt;
&lt;p&gt;And when I&amp;#8217;m done, I can type &lt;code&gt;rspec &amp;lt;filename&amp;gt;&lt;/code&gt; and see something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;Pending:
  FizzBuzz#calculate returns &lt;span class="s1"&gt;&amp;#39;Fizz&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;all multiples of 3
    &lt;span class="c"&gt;# Not Yet Implemented&lt;/span&gt;
    &lt;span class="c"&gt;# ./fizzbuzz_spec.rb:16&lt;/span&gt;
  FizzBuzz#calculate returns &lt;span class="s1"&gt;&amp;#39;Buzz&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;all multiples of 5
    &lt;span class="c"&gt;# Not Yet Implemented&lt;/span&gt;
    &lt;span class="c"&gt;# ./fizzbuzz_spec.rb:17&lt;/span&gt;
  FizzBuzz#calculate returns &lt;span class="s1"&gt;&amp;#39;FizzBuzz&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;all multiples of 3 and 5
    &lt;span class="c"&gt;# Not Yet Implemented&lt;/span&gt;
    &lt;span class="c"&gt;# ./fizzbuzz_spec.rb:18&lt;/span&gt;
  FizzBuzz#calculate returns the passed number &lt;span class="k"&gt;if &lt;/span&gt;not a multiple of 3 or 5
    &lt;span class="c"&gt;# Not Yet Implemented&lt;/span&gt;
    &lt;span class="c"&gt;# ./fizzbuzz_spec.rb:19&lt;/span&gt;

Finished in 0.00061 seconds
4 examples, 0 failures, 4 pending
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Amazingly clear, right?  I have four examples, and all of them are pending further definition.  So I don&amp;#8217;t bother you with all of the boring details, here is the complete implementation.  Since this is so simple of a problem, I decided to combine the spec and class definition in the same file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FizzBuzz&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="s2"&gt;&amp;quot;FizzBuzz&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="s2"&gt;&amp;quot;Fizz&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="s2"&gt;&amp;quot;Buzz&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;FizzBuzz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;#calculate&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:fb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;FizzBuzz&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;returns &amp;#39;Fizz&amp;#39; for all multiples of 3&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Fizz&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;returns &amp;#39;Buzz&amp;#39; for all multiples of 5&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Buzz&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;returns &amp;#39;FizzBuzz&amp;#39; for all multiples of 3 and 5&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;FizzBuzz&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;returns the passed number if not a multiple of 3 or 5&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;77&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When running this completed spec, we see that all is good:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;....

Finished in 0.00162 seconds
4 examples, 0 failures
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Awesome, right?  Learn more about RSpec &lt;a href="http://rspec.info/"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/BK5rL-50HQY" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2011/01/05/diving-into-rspec.html</feedburner:origLink></entry>
 
 <entry>
   <title>Small Notes on New Year Resolutions</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/v8hd104ww5E/getting-new-year-resolutions-right.html" />
   <updated>2011-01-05T00:00:00-08:00</updated>
   <id>http://gorsuch.github.com/2011/01/05/getting-new-year-resolutions-right</id>
   <content type="html">&lt;p&gt;What you need to know:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Everyone has them.  Everyone blogs about them.  Everyone breaks them.&lt;/p&gt;
&lt;p&gt;&amp;#8212;Me&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;How to fix it:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you have plans for the New Year, keep quiet about them.  For one reason or another, when we chat about our resolutions or plans, we tend to not accomplish them.&lt;/p&gt;
&lt;p&gt;Rather than write a blog post about your intentions, carefully write them down on a piece of paper.  Get specific and cut out the dead wood.  If you have a list of ten items, reduce it to a list of five.&lt;/p&gt;
&lt;p&gt;While you may be able to make progress on your entire list, pick one to really focus on.  Consider this another step towards making sure that you actually want what you are asking for.  Upon waking, review that item.  Upon getting ready for bed, review that item.  If that particular goal seems too big, break it up into chunks.  It&amp;#8217;s like selling software &amp;#8211; you don&amp;#8217;t launch and then hope for a million loyal customers.  You launch and work towards getting your first customer.  Then you get your next ten, fifty, and so on.&lt;/p&gt;
&lt;p&gt;Good luck!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/v8hd104ww5E" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2011/01/05/getting-new-year-resolutions-right.html</feedburner:origLink></entry>
 
 <entry>
   <title>Introducing logtrend, or HTTP Request Trending With EventMachine</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/ZkiTRQ8NisY/introducing-logtrend.html" />
   <updated>2010-12-03T00:00:00-08:00</updated>
   <id>http://gorsuch.github.com/2010/12/03/introducing-logtrend</id>
   <content type="html">&lt;p&gt;I have this problem of not wanting to demonstrate code unless it is prettied up.  It probably stems from inexperience and the fact that &lt;a href="http://fogcreek.com"&gt;I work with people much smarter than myself&lt;/a&gt;.  I told myself that its time to start showing off as soon as something seems useful, so here you go.  In this era of social coding, the sooner something is released, the sooner the feedback comes.&lt;/p&gt;
&lt;p&gt;The following code demonstrates a simple way to trend the volume of &lt;span class="caps"&gt;HTTP&lt;/span&gt; requests in your environment.&lt;/p&gt;
&lt;h2&gt;Our Problem&lt;/h2&gt;
&lt;p&gt;I assume you run a web app of reasonable volume, and stream your access logs to disk.  If you&amp;#8217;ve ever been fire fighting and scanning logs looking for long running or more-frequent-than-usual requests, you know it is hard and often futile work.&lt;/p&gt;
&lt;h2&gt;One Possible Solution&lt;/h2&gt;
&lt;p&gt;Here is &lt;a href="http://github.com/gorsuch/logtrend"&gt;logtrend&lt;/a&gt;, my contribution to putting these days behind us.&lt;/p&gt;
&lt;p&gt;The idea is simple, give logtrend a log to follow, define categories for different types of events, and let it automatically build graphs as you define them.  You can then check up on your graphs when you things start to behave funny and see if traffic patterns have changed.  For example, perhaps you have a customer abusing your &lt;span class="caps"&gt;API&lt;/span&gt; or hammering on an entry point that is especially expensive.&lt;/p&gt;
&lt;p&gt;This can probably be better demonstrated via the following code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;logtrend&amp;#39;&lt;/span&gt;

&lt;span class="c1"&gt;# Invoke this to begin trending your data...&lt;/span&gt;
&lt;span class="no"&gt;LogTrend&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/var/log/httpd-access&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;  
  &lt;span class="c1"&gt;# Set new locations for our graphs and rrds.  defaults to &amp;#39;.&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rrd_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/tmp/rrd&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graphs_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/tmp/graphs&amp;#39;&lt;/span&gt;
  
  &lt;span class="c1"&gt;# Add some things to trend.  An RRD is built for each one of these items.&lt;/span&gt;
  &lt;span class="c1"&gt;# Each time we read a line from the log file, we pass it to the block.&lt;/span&gt;
  &lt;span class="c1"&gt;# If your block returns true, we count that as a hit.&lt;/span&gt;
  &lt;span class="c1"&gt;# Every minute, the RRD is updated with the hits for the previous period.&lt;/span&gt;
  &lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_trend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="sr"&gt; /.*/&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_trend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:fbod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="sr"&gt; /fogbugz.com/&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_trend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:kod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="sr"&gt; /kilnhg.com/&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_trend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:long&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="c1"&gt;# Let us pretend that request time is in seconds&lt;/span&gt;
    &lt;span class="c1"&gt;# and is the last item on the log line&lt;/span&gt;
    &lt;span class="n"&gt;request_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_i&lt;/span&gt;
    &lt;span class="n"&gt;request_time&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  
  &lt;span class="c1"&gt;# Build a graph displaying some of the items we are trending&lt;/span&gt;
  &lt;span class="c1"&gt;# Label it as :requests_per_minute&lt;/span&gt;
  &lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:requests_per_minute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_point&lt;/span&gt; &lt;span class="ss"&gt;:area&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;#333333&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_point&lt;/span&gt; &lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:fbod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;#0066cc&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_point&lt;/span&gt; &lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:kod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;#993333&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Build a second graph for our long running queries&lt;/span&gt;
  &lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:long_requests&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_point&lt;/span&gt; &lt;span class="ss"&gt;:area&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;#000000&amp;#39;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once started, logtrend will watch the logfile in real time, outputting graphs every minute.&lt;/p&gt;
&lt;p&gt;Here is a small sample of our first graph (numbers and other sensitive data have been edited out):&lt;/p&gt;
&lt;p&gt;&lt;img src="http://michaelgorsuch.com/images/graph_sample.png" /&gt;&lt;/p&gt;
&lt;p&gt;The black background represents the total requests for the given service, and the yellow line represents all &lt;span class="caps"&gt;API&lt;/span&gt; requests coming in.  Notice the spike?  Fortunately it did not cause any service issues, but had it, we would have had actionable intelligence.&lt;/p&gt;
&lt;h2&gt;Further Notes&lt;/h2&gt;
&lt;p&gt;Installation should be a matter of &lt;code&gt;sudo gem install logtrend&lt;/code&gt;.  Feel free to fork and patch as you see fit.  There are still some rough edges, but I have been using it in production for over a week without any issue.&lt;/p&gt;
&lt;p&gt;The system is currently retaining a year&amp;#8217;s worth of data, but only rendering graphs for the past 24 hours.  Expect this to be improved in an upcoming release.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://github.com/gorsuch/logtrend"&gt;logtrend&lt;/a&gt; is built with the intention of staying simple, so it probably won&amp;#8217;t ever be a good fit for trending more complicated data than already mentioned.&lt;/p&gt;
&lt;h2&gt;Credit&lt;/h2&gt;
&lt;p&gt;As always, this was built on the shoulders of giants.  Abstraction upon abstraction made a difficult problem very easy to solve.  The most obvious contributions come from the flexibility of Ruby + the awesomeness of &lt;a href="http://rubyeventmachine.com/"&gt;EventMachine&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Jordan Sissel&amp;#8217;s &lt;a href="https://github.com/jordansissel/eventmachine-tail"&gt;eventmachine-tail&lt;/a&gt; made watching the files a cinch, and Thiago Morello&amp;#8217;s &lt;a href="https://github.com/morellon/rrd-ffi"&gt;rrd-ffi&lt;/a&gt; made graph generation and data retention pain free.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/ZkiTRQ8NisY" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2010/12/03/introducing-logtrend.html</feedburner:origLink></entry>
 
 <entry>
   <title>Bringing Your Rails App to the Google Apps Marketplace</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/xdQdakC0ww0/rails-google-apps-marketplace.html" />
   <updated>2010-11-14T10:00:00-08:00</updated>
   <id>http://gorsuch.github.com/2010/11/14/rails-google-apps-marketplace</id>
   <content type="html">&lt;p&gt;It&amp;#8217;s good times.  Between amazing frameworks like &lt;a href="http://rubyonrails.org"&gt;Ruby on Rails&lt;/a&gt; and cloud application platforms like &lt;a href="http://heroku.com"&gt;Heroku&lt;/a&gt;, lots of heavy lifting has been abstracted away and the friction for bringing your product to market has been seriously reduced. The phrase &amp;#8216;standing on the shoulders of giants&amp;#8217; sounds more and more applicable as each year passes.&lt;/p&gt;
&lt;p&gt;To make things even sweeter, Google comes along and creates the &lt;a href="http://www.google.com/enterprise/marketplace/"&gt;Google Apps Marketplace&lt;/a&gt;.  The short and easy: Google has millions of customers using Google Apps.  You can write applications that integrate with various Google Apps services, and can take advantage of their single sign-on infrastructure.  Your app can appear on the Google Apps nav bar, allowing them to switch to your product just as easily as they switch over to Google Docs or Calendar.  It looks to be a market begging for lots of attention.&lt;/p&gt;
&lt;p&gt;Okay.  That&amp;#8217;s out the way.  Now how hard is it to write an app to integrate with Google Apps?  Turns out to be a lot easier than I thought.  I spent two hours yesterday morning and came up with a reasonable example.  You can find the live version of the app &lt;a href="http://styledbits.com"&gt;here&lt;/a&gt;, and grab the full source &lt;a href="http://github.com/gorsuch/styledbits"&gt;via GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The problem to solve was simple: how do I share code snippets with coworkers?  I want a &lt;em&gt;private&lt;/em&gt; &lt;a href="http://pastebin.com"&gt;Pastebin&lt;/a&gt; or &lt;a href="http://gist.github.com"&gt;Gist&lt;/a&gt; service, but I don&amp;#8217;t want to install anything on my own servers.  Enter &lt;a href="http://styledbits.com"&gt;Styled Bits&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The app layout is very simple: I have three main models: &lt;a href="https://github.com/gorsuch/styledbits/blob/master/app/models/account.rb"&gt;Account&lt;/a&gt;, &lt;a href="https://github.com/gorsuch/styledbits/blob/master/app/models/user.rb"&gt;User&lt;/a&gt;, and &lt;a href="https://github.com/gorsuch/styledbits/blob/master/app/models/post.rb"&gt;Post&lt;/a&gt;.  Account represents an entire Google Apps domain, User represents a member of said domain, and Post is a code snippet.&lt;/p&gt;
&lt;p&gt;There are two major controllers, &lt;a href="https://github.com/gorsuch/styledbits/blob/master/app/controllers/main_controller.rb"&gt;Main&lt;/a&gt; and &lt;a href="https://github.com/gorsuch/styledbits/blob/master/app/controllers/posts_controller.rb"&gt;Posts&lt;/a&gt;.  Main handles user login and Posts displays the code snippets.&lt;/p&gt;
&lt;p&gt;All of this is pretty standard stuff, the only thing that stands out is how the Main controller actually handles authentication.  If you &lt;a href="https://github.com/gorsuch/styledbits/blob/master/app/controllers/main_controller.rb"&gt;take a peak&lt;/a&gt; at the &lt;code&gt;index&lt;/code&gt; method and related view, you will see that we display a form asking for your Google Apps domain.  You can see it live by visiting the front page of &lt;a href="https://github.com/gorsuch/styledbits/blob/master/app/controllers/main_controller.rb"&gt;Styled Bits&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After taking your domain name, we redirect you to the &lt;code&gt;login&lt;/code&gt; method, where we do something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;
  &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;https://www.google.com/accounts/o8/site-xrds?hd=&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:domain&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;authenticate_with_open_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
	&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:required&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://axschema.org/contact/email&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; 
	&lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;identity_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;successful?&lt;/span&gt;
      &lt;span class="c1"&gt;# Succesfully logged in&lt;/span&gt;
	    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	    &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_or_create_by_domain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:domain&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_or_create_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
	    &lt;span class="n"&gt;redirect_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;posts_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
	    &lt;span class="c1"&gt;# Failed to login&lt;/span&gt;
	    &lt;span class="n"&gt;flash&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:notice&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Could not log you in.&amp;quot;&lt;/span&gt;
	    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, the simple method &lt;code&gt;authenticate_with_open_id&lt;/code&gt; takes care of everything.  Don&amp;#8217;t you love blocks?  Don&amp;#8217;t you?  That&amp;#8217;s all it takes.  To get this handy method, just add the following to your project&amp;#8217;s &lt;a href="https://github.com/gorsuch/styledbits/blob/master/Gemfile"&gt;Gemfile&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ruby-openid&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rack-openid&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ruby-openid-apps-discovery&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Add the following to the end of your &lt;code&gt;environment.rb&lt;/code&gt; to take care of some weirdness with Google OpenID discovery:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;gapps_openid&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After that, all you need to do is to install the &lt;a href="https://github.com/rails/open_id_authentication"&gt;open_id_authentication&lt;/a&gt; plugin:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;rails plugin install https://github.com/rails/open_id_authentication.git
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With all of that out of the way, you are more or less on your way.  &lt;a href="http://developer.googleapps.com/marketplace/getting-started"&gt;Head over here&lt;/a&gt; and follow the instructions to become an official vendor and make your app installable.  But even without this, Google Apps users will be able to login without issue.&lt;/p&gt;
&lt;p&gt;I know that this article is far from perfect, but I hope it will at least inspire others to integrate their apps or create unique solutions for the millions of Google Apps users.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/xdQdakC0ww0" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2010/11/14/rails-google-apps-marketplace.html</feedburner:origLink></entry>
 
 <entry>
   <title>On Kiln and GitHub</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/XZYpUQ9Sc4o/kiln-github.html" />
   <updated>2010-11-14T00:00:00-08:00</updated>
   <id>http://gorsuch.github.com/2010/11/14/kiln-github</id>
   <content type="html">&lt;p&gt;I work for &lt;a href="http://fogcreek.com"&gt;Fog Creek Software&lt;/a&gt; and help maintain the infrastructure underlying &lt;a href="http://fogbugz.com"&gt;FogBugz On Demand&lt;/a&gt; and &lt;a href="http://kilnhg.com"&gt;Kiln&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://kilnhg.com"&gt;&lt;img src="http://michaelgorsuch.com/images/dodo.png" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Eyebrows sometimes raise when those in the know discover that I am &lt;a href="http://github.com/gorsuch"&gt;publishing small amounts of code to GitHub&lt;/a&gt;.  My imagination hears whispers of treason, and the outcome is this short article.&lt;/p&gt;
&lt;p&gt;In my line of work one can either be irrationally loyal to a given tool, or one can choose the weapon that best suits the environment to be conquered.  If I was going to be working with a group of developers on a serious project, I would go with &lt;a href="http://kilnhg.com"&gt;Kiln&lt;/a&gt;.  Mercurial is easy to use and works perfectly across all major platforms.  Kiln emphasizes its best features and gives you a fantastic code review tool as well as integration into one of the &lt;a href="http://fogbugz.com"&gt;best bug tracking platforms available&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://github.com"&gt;&lt;img src="http://michaelgorsuch.com/images/github.png" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However, for one who wishes to publish code for the sake of sharing with the world, &lt;a href="http://github.com"&gt;GitHub&lt;/a&gt; is the obvious choice.  While I think &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt; is an all around pain in the butt when compared to &lt;a href="http://mercurial.selenic.com/"&gt;Mercurial&lt;/a&gt;, it is clear that the open source community has chosen its platform.  Kiln is not built to do this sort of thing, and &lt;a href="http://bitbucket.org"&gt;Bitbucket&lt;/a&gt; does not have the traction.  When I&amp;#8217;m looking for code, I more often than not end up on GitHub.&lt;/p&gt;
&lt;p&gt;In a perfect world I could do all of my development with Kiln tools and then easily push my open source projects to GitHub as appropriate.  Someday, maybe.  For now, I use different tools as needed.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/XZYpUQ9Sc4o" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2010/11/14/kiln-github.html</feedburner:origLink></entry>
 
 <entry>
   <title>Returning to the Ruby Ecosystem</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/cVhgGVx42wg/returning-to-ruby.html" />
   <updated>2010-11-12T00:00:00-08:00</updated>
   <id>http://gorsuch.github.com/2010/11/12/returning-to-ruby</id>
   <content type="html">&lt;p&gt;For the past year, I have forced myself to work with Python and C#.&lt;/p&gt;
&lt;p&gt;Last night I sat down at my computer and typed the following:&lt;/p&gt;
&lt;pre&gt;gem install rails&lt;/pre&gt;
&lt;p&gt;After a minute or so, &lt;a href="http://rubyonrails.org"&gt;Rails 3&lt;/a&gt; loaded onto my system and I began to remember why I love Ruby so much.  After four hours of prototyping, I was able to deploy my code to the real world like so:&lt;/p&gt;
&lt;pre&gt;git push heroku&lt;/pre&gt;
&lt;p&gt;I don&amp;#8217;t want to go back.  Sure, I&amp;#8217;ll maintain my old code and use .&lt;span class="caps"&gt;NET&lt;/span&gt; where appropriate, but not on nights or weekends.&lt;/p&gt;
&lt;p&gt;To get up to date, I picked up &lt;a href="http://peepcode.com/products/meet-rails-3-i"&gt;Meet Rails 3&lt;/a&gt; from &lt;a href="http://peepcode.com"&gt;PeepCode&lt;/a&gt;, and grabbed the latest Rails &lt;a href="http://pragprog.com/titles/rails4/agile-web-development-with-rails"&gt;beta book&lt;/a&gt; from &lt;a href="http://pragprog.com/"&gt;The Pragmatic Bookshelf&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/cVhgGVx42wg" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2010/11/12/returning-to-ruby.html</feedburner:origLink></entry>
 
 <entry>
   <title>Serving WOT and SVG Files Via IIS</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/HXkJGbrL1QI/wot-svg-iis.html" />
   <updated>2010-11-10T17:30:00-08:00</updated>
   <id>http://gorsuch.github.com/2010/11/10/wot-svg-iis</id>
   <content type="html">&lt;p&gt;A small one for today.  As it turns out, &lt;span class="caps"&gt;IIS&lt;/span&gt; 6.0 only knows about &lt;span class="caps"&gt;EOT&lt;/span&gt; and &lt;span class="caps"&gt;TTF&lt;/span&gt; fonts.  In order for your fancypants designers to take advantage of &lt;span class="caps"&gt;WOFF&lt;/span&gt; or &lt;span class="caps"&gt;SVG&lt;/span&gt;, you have to add the following &lt;span class="caps"&gt;MIME&lt;/span&gt; type declarations via &lt;span class="caps"&gt;IIS&lt;/span&gt; Manager (under the &lt;span class="caps"&gt;HTTP&lt;/span&gt; Headers tab):&lt;/p&gt;
&lt;pre&gt;
.woff  application/x-woff
.svg   image/svg+xml
&lt;/pre&gt;
&lt;p&gt;After adding those definitions, all should be well.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/HXkJGbrL1QI" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2010/11/10/wot-svg-iis.html</feedburner:origLink></entry>
 
 <entry>
   <title>Solving libcurl's SSL Issues on OS X</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/a4enUi3lzq0/curl-and-ssl-on-osx.html" />
   <updated>2010-11-10T00:00:00-08:00</updated>
   <id>http://gorsuch.github.com/2010/11/10/curl-and-ssl-on-osx</id>
   <content type="html">&lt;p&gt;Hey!  A new blog post.&lt;/p&gt;
&lt;p&gt;After getting all excited about Jekyll in my &lt;a href="http://gorsuch.github.com/2010/11/09/hello-jekyll.html"&gt;earlier post&lt;/a&gt;, I came home to pull down my repository on my wife&amp;#8217;s Mac.  After gleefully typing the commands in, I received this:&lt;/p&gt;
&lt;pre&gt;Protocol https not supported or disabled in libcurl&lt;/pre&gt;
&lt;p&gt;Fortunately, the fix wasn&amp;#8217;t too hard to figure out.  I just needed to reinstall libcurl with &lt;span class="caps"&gt;SSL&lt;/span&gt; enabled.  The following command fixed that:&lt;/p&gt;
&lt;pre&gt;sudo port install curl +ssl&lt;/pre&gt;
&lt;p&gt;No more problems!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/a4enUi3lzq0" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2010/11/10/curl-and-ssl-on-osx.html</feedburner:origLink></entry>
 
 <entry>
   <title>Hello, Jekyll!</title>
   <link href="http://feedproxy.google.com/~r/gorsuch/~3/tQMWUZIItdw/hello-jekyll.html" />
   <updated>2010-11-09T00:00:00-08:00</updated>
   <id>http://gorsuch.github.com/2010/11/09/hello-jekyll</id>
   <content type="html">&lt;p&gt;After starting and stopping a dozen or so blogs over the past ten years, I finally decided to eliminate the fancy engine and go for a static layout.&lt;/p&gt;
&lt;p&gt;I was impressed after hearing about &lt;a href="https://github.com/mojombo/jekyll"&gt;Jekyll&lt;/a&gt; and reading &lt;a href="http://tom.preston-werner.com/2008/11/17/blogging-like-a-hacker.html"&gt;Tom Preston-Werner&amp;#8217;s post&lt;/a&gt;.  The entire system is simple: write up your article in the editor of your choice, add it to a Git repository and publish.  It&amp;#8217;s sort of like the &lt;a href="http://heroku.com"&gt;Heroku&lt;/a&gt; of static publishing.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m keeping the &amp;#8216;.github.com&amp;#8217; domain name for the time being, and will move to one of my true domains if the writing keeps up.&lt;/p&gt;
&lt;p&gt;If you are interested, you can see how easy it was to construct this site by viewing its &lt;a href="https://github.com/gorsuch/gorsuch.github.com"&gt;repository&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/gorsuch/~4/tQMWUZIItdw" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://gorsuch.github.com/2010/11/09/hello-jekyll.html</feedburner:origLink></entry>
 
 
</feed>

