<?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>opensoul.org by Brandon Keepers</title>
  
  <link href="http://opensoul.org/blog/" />
  <id>http://opensoul.org/blog/</id>
  <updated>2012-05-16T08:08:32-04:00</updated>
 
  
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/opensoul" /><feedburner:info uri="opensoul" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><link rel="license" type="text/html" href="http://creativecommons.org/licenses/by-sa/2.0/" /><logo>http://creativecommons.org/images/public/somerights20.gif</logo><entry>
      <title>The Plight of Pinocchio: JavaScript's quest to become a real language</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/RKLxJ2nUUfM/" />
      <id>4fb397b5dabe9d642800d76a</id>
      <updated>2012-05-16T08:08:32-04:00</updated>
      <published>2012-05-16T08:00:00-04:00</published>
      <category term="talk" />
      <summary type="html">&lt;p&gt;JavaScript is no longer a toy language. Many of our applications can&amp;#8217;t function without it. If we are going to use JavaScript to do real things, we need to treat it like a real language, adopting the same practices we use with real languages. This framework agnostic talk takes a serious look at how we develop JavaScript applications.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;JavaScript is no longer a toy language. Many of our applications can&amp;#8217;t function without it. If we are going to use JavaScript to do real things, we need to treat it like a real language, adopting the same practices we use with real languages.&lt;/p&gt;
&lt;p&gt;This framework agnostic talk takes a serious look at how we develop JavaScript applications. Despite its prototypical nature, good object-oriented programming principles are still relevant. The design patterns that we&amp;#8217;ve grown to know and love work just as well in JavaScript as they do any other language. Test driven development forces us to write modular, decoupled code.&lt;/p&gt;
&lt;p&gt;Here are the slides for my &lt;a href="http://2012.jsday.it/talk/javascript-application-architecture/"&gt;JSDay talk&lt;/a&gt;.&lt;/p&gt;
&lt;script async class="speakerdeck-embed" data-id="4fa986b3a117fc00220012ed" data-ratio="1.3333333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/RKLxJ2nUUfM" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2012/05/16/the-plight-of-pinocchio/</feedburner:origLink></entry>
  
    <entry>
      <title>Walking</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/UxjPy3Z_RDM/" />
      <id>4f2c006fdabe9d765900d0a7</id>
      <updated>2012-02-03T10:51:17-05:00</updated>
      <published>2012-02-03T10:00:00-05:00</published>
      <category term="life" /><category term="observation" />
      <summary type="html">&lt;p&gt;I am inspired by progress. I love the continuous process of taking something that is awkward, uncomfortable or even painful and making it incrementally better.&lt;/p&gt;</summary>
      <content type="html">&lt;blockquote&gt;
&lt;p&gt;I had to answer the question &amp;#8220;What inspires you?&amp;#8221; for a TEDx application. Every answer to that question sounds cheesy. Here&amp;#8217;s my cheesy answer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I am inspired by walking; steady, continuous forward progress. Fast enough to get where I want to go in time, but not too fast.&lt;/p&gt;
&lt;p&gt;Maintaining a fast pace while walking requires enormous concentration on the act of walking. It is extremely difficult to focus on anything else and maintain a fast pace. I only walk fast when arriving at my destination is of the utmost of importance.&lt;/p&gt;
&lt;p&gt;Walking slow is often enjoyable, but neglects the fact that the point of walking is to go somewhere. I walk slow when I have a particularly difficult problem to solve, or I only want a slight change of scenery in hopes of inspiration.&lt;/p&gt;
&lt;p&gt;Walking naturally requires almost no concentration. It allows me to focus on what is important, adjust my path when necessary, and arrive at my destination in a reasonable time.&lt;/p&gt;
&lt;p&gt;I am inspired by progress. I love the continuous process of taking something that is awkward, uncomfortable or even painful and making it incrementally better.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/UxjPy3Z_RDM" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2012/02/03/walking/</feedburner:origLink></entry>
  
    <entry>
      <title>Getting Started with Sublime Text 2</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/v10JoqbBiLw/" />
      <id>4f076418dabe9d11e6003b67</id>
      <updated>2012-02-02T00:56:41-05:00</updated>
      <published>2012-01-12T07:00:00-05:00</published>
      <category term="editor" /><category term="popular" />
      <summary type="html">&lt;p&gt;I recently switched to &lt;a href="http://www.sublimetext.com/"&gt;Sublime Text 2&lt;/a&gt;. As it goes with many of the finer things in life, Sublime is an acquired taste. Here are a few tips that made it easier for me to get into it.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;I recently switched to &lt;a href="http://www.sublimetext.com/"&gt;Sublime Text 2&lt;/a&gt; from TextMate. I did not love it at first. As it goes with many of the finer things in life, Sublime is an acquired taste. It was not until I paired with &lt;a href="http://twitter.com/jnunemaker"&gt;John Nunemaker&lt;/a&gt; for a few days that I really started to see its power. I have been using it for several weeks now and have not been tempted to open TextMate for anything.&lt;/p&gt;
&lt;p&gt;Here are a few tips that made it easier for me to get into it. Thanks to &lt;a href="http://twitter.com/garand"&gt;Anthony Garand&lt;/a&gt; for providing feedback and contributing to some of these tips below.&lt;/p&gt;
&lt;h2&gt;Beat the ugly out of it&lt;/h2&gt;
&lt;p&gt;I cannot figure out a nice way to say this, so I will just say it: out of the box, Sublime is hideous. We are pompous programmers, so we cannot let that stand.&lt;/p&gt;
&lt;p&gt;&lt;ins&gt;Update: The new default theme that was was &lt;a href="http://www.sublimetext.com/blog/articles/sublime-text-2-build-2165"&gt;released today&lt;/a&gt; is actually &lt;strong&gt;much&lt;/strong&gt; better. I still prefer the light Soda theme linked to below, but it&amp;#8217;s no longer a necessity.&lt;/ins&gt;&lt;/p&gt;
&lt;p&gt;Start by installing the &lt;a href="https://github.com/buymeasoda/soda-theme/"&gt;Soda UI theme&lt;/a&gt;. This is not a theme in the syntax highlighting sense, but for the UI widgets in Sublime. Note that when plugins talk about the &lt;code&gt;Packages&lt;/code&gt; directory in Sublime, on OS X that is in &lt;code&gt;~/Library/Application Support/Sublime Text 2/Packages&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ahh, that is better.  But the icon is ugly too. There are several &lt;a href="http://dribbble.com/search?q=sublime+text&amp;amp;x=0&amp;amp;y=0"&gt;replacement options on dribbble&lt;/a&gt;. &lt;a href="http://dribbble.com/shots/311515-A-Sublime-Text-2-Icon-that-is-less-horrible"&gt;This one is my favorite&lt;/a&gt;, although there is still much room for improvement.&lt;/p&gt;
&lt;p&gt;The syntax highlighting theme that comes with Sublime is alright, but everyone has their favorite theme. Mine is &lt;a href="http://madeofcode.com/posts/29-photo-my-new-textmate-theme-8220-made-of-code-8221-mdash-download-9-feb-2010-update-t"&gt;Made of Code&lt;/a&gt;. You can use any TextMate theme by copying it to the &lt;code&gt;Packages&lt;/code&gt; directory and going to &amp;#8220;Preferences » Color Scheme&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Ok, now we can stand to look at it.&lt;/p&gt;
&lt;p&gt;&lt;img src="/assets/4f0df732dabe9d0233009fe8/content/screen_shot_20120111_at_3.50.09_pm.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;Opening a project&lt;/h2&gt;
&lt;p&gt;Sublime has support for project files, but I do not use them. I prefer to just open projects from the command line. Sublime comes with a command line interface, but you need to add it to your path.&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;sudo ln -s &amp;quot;/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl&amp;quot; /usr/bin/subl&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Now you can open a file or directory from the command line.&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;subl -n ~/blog&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I almost always edit from the current directory, so I set up a bash alias.&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;alias e=&amp;quot;subl -n .&amp;quot;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Ok, now we have Sublime looking pretty and our project open.&lt;/p&gt;
&lt;h2&gt;Customizing Settings&lt;/h2&gt;
&lt;p&gt;Sublime is extremely configurable, but the configuration is in a few different &lt;span class="caps"&gt;JSON&lt;/span&gt; files that you have to edit by hand. But once you get familiar with them, they are not that scary.&lt;/p&gt;
&lt;p&gt;On OS X, go to &amp;#8220;Sublime Text 2 » Preferences&amp;#8221;.&lt;/p&gt;
&lt;p&gt;&lt;img src="/assets/4f0de004dabe9d6e8f000ce3/content/screen_shot_20120111_at_2.15.34_pm.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;For each type of setting, Sublime has at least two files: defaults and custom user settings. You edit the &lt;strong&gt;User&lt;/strong&gt; versions to specify the things you are overriding.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;File Settings&lt;/strong&gt; affect the file that you are currently editing. This includes things like your color scheme, fonts…basically, anything related to editing files.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Global Settings&lt;/strong&gt; affect how Sublime itself behaves, including the UI theme, files to ignore, and window behavior.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Key bindings&lt;/strong&gt; are…well, key bindings. 614 lines of glorious shortcut keys.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Take time to browse through the defaults to see all the levers and dials you can tweak. Check out &lt;a href="https://gist.github.com/1596561"&gt;my settings&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Command palette&lt;/h2&gt;
&lt;p&gt;Think of the command palette as Alfred for your code editor. Quick access to pretty much anything. Pressing ⌘⇧P will bring up the command palette where you can then scroll through a few of the options available to you: snippets, convert case, sort lines, and toggle various interface options.&lt;/p&gt;
&lt;h2&gt;Package Control&lt;/h2&gt;
&lt;p&gt;If you feel like something is missing from Sublime, then there is probably a plugin for it. The most essential plugin is &lt;a href="http://wbond.net/sublime_packages/package_control"&gt;Package Control&lt;/a&gt;, which allows you to install pretty much anything.&lt;/p&gt;
&lt;p&gt;To use package control, press ⌘⇧P to bring up the command palette, begin to type &amp;#8220;install&amp;#8221;, and select &amp;#8220;&lt;strong&gt;Package Control: Install Package&lt;/strong&gt;&amp;#8221;. This will bring up a new palette to search for packages.  Find your packages and hit enter to install it. You can see in the status bar at the bottom that it is installing. Most packages will just work, but occasionally you might have to restart Sublime for the package to work.&lt;/p&gt;
&lt;p&gt;&lt;img src="/assets/4f0e0559dabe9d2e33005837/content/screen_shot_20120111_at_4.55.08_pm.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;⌘p is the new ⌘t&lt;/h2&gt;
&lt;p&gt;⌘p (which is also mapped to ⌘t) brings up the Go To File palette. In addition to giving you quick access to your files, it has several features to allow you quickly jump &lt;strong&gt;into&lt;/strong&gt; your files. After typing the file name, use &lt;strong&gt;@&lt;/strong&gt; in the palette to jump directly to a specific method. You can also use ⌘r to bring up the palette prefaced with &lt;strong&gt;@&lt;/strong&gt; to jump to a method in your current file.&lt;/p&gt;
&lt;p&gt;&lt;img src="/assets/4f0eda58dabe9d1cbb008bdd/content/screen_shot_20120112_at_8.03.54_am.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;You can also use &lt;strong&gt;:&lt;/strong&gt; (colon) in the palette to go to a specific line number, and ^g will open the paletted prefaced with colon to jump to a line in the current file.&lt;/p&gt;
&lt;h2&gt;Favorite Plugins&lt;/h2&gt;
&lt;p&gt;Here is a list of some of my favorite plugins. Most of these should be available to install through Package Control. Check them out and don&amp;#8217;t forget to browse around Packgae Control to see what else is available. Here is a &lt;a href="http://wbond.net/sublime_packages/community"&gt;complete list of packages available&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="https://github.com/kemayo/sublime-text-2-git"&gt;Git&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://github.com/weslly/Nettuts-Fetch"&gt;Fetch&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://wbond.net/sublime_packages/alignment"&gt;Alignment&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://github.com/jnordberg/sublime-colorpick"&gt;Color Pick&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://github.com/condemil/Gist"&gt;Gist&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://sass-lang.com"&gt;&lt;span class="caps"&gt;SCSS&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://github.com/jashkenas/coffee-script-tmbundle"&gt;CoffeeScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;One more thing&lt;/h2&gt;
&lt;p&gt;I highly recommend you go over and read the &lt;a href="http://net.tutsplus.com/tutorials/tools-and-tips/sublime-text-2-tips-and-tricks/"&gt;Sublime article on NetTuts+&lt;/a&gt; for many more tips and tricks to integrate into your workflow as you migrate to this new editor.&lt;/p&gt;
&lt;p&gt;I started tweeting tips from &lt;a href="http://twitter.com/sublimetips"&gt;@sublimetips&lt;/a&gt; on Twitter. Tweet your tips mentioning @sublimetips and I will likely retweet them.&lt;/p&gt;
&lt;p&gt;Happy Coding!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/v10JoqbBiLw" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2012/01/12/getting-started-with-sublime-text-2/</feedburner:origLink></entry>
  
    <entry>
      <title>The $40 Standup Desk</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/3bRlf4wPpjc/" />
      <id>4f04b311dabe9d6b760016a2</id>
      <updated>2012-03-02T17:35:31-05:00</updated>
      <published>2012-01-09T10:00:00-05:00</published>
      <category term="dyi" /><category term="popular" />
      <summary type="html">&lt;p&gt;I searched for weeks for the ideal standup desk. I could not find something that was either exactly what I wanted or reasonably priced, and I could not justify paying $800 or more for a desk that I did not love. So I built my own.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;&lt;img src="/assets/4f0743bedabe9d60180023e1/content/img_5073.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I spent weeks searching for the ideal standup desk. I shopped around online, visited every furniture store within 10 miles no fewer than 3 times, and almost ordered a &lt;a href="http://www.geekdesk.com/"&gt;Geek Desk&lt;/a&gt;. I could not find something that was exactly what I wanted and I could not justify paying $800 or more for a desk that I did not love.&lt;/p&gt;
&lt;p&gt;Ultimately, I decided I to do something cheap for now; I built my own. I had been thinking about doing it for a while when my coworker Steve Smith &lt;a href="http://ordlnk.com/331R1k441W2u1k433P3D"&gt;built his desk&lt;/a&gt;. After seeing his, I was convinced this was what I wanted to do.&lt;/p&gt;
&lt;p&gt;It turns out, the cheap option is pretty awesome.&lt;/p&gt;
&lt;h2&gt;Parts&lt;/h2&gt;
&lt;p class="right"&gt;&lt;img src="/assets/4f0743bcdabe9d60180023cd/medium/img_5072.jpg" alt="" /&gt;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Heavy-duty 19&amp;quot; shelf brackets&lt;/strong&gt; &amp;#8211; These should cost $8-$10 each at the local hardware store. A set of 2 brackets claims to be able to hold 1000 pounds if properly secured, so it should be more than enough, even for extra-curricular activities (good luck).&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Desktop surface&lt;/strong&gt; &amp;#8211; I found a local furniture outlet that sells assorted desktop surfaces for $3-$4 per foot. I picked up a 4&amp;#8217; desktop for $16.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Screws&lt;/strong&gt; &amp;#8211; I needed 6 larger screws (about 2 1/2&amp;quot;) to attach the brackets to the wall, and 6 smaller screws (less than the thickness of the desktop) to attach the desktop to the brackets.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Tools&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Drill&lt;/strong&gt; for creating pilot holes for the screws. If you have powerful drill/screwdriver, you may not need to drill holes.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Level&lt;/strong&gt;, because nobody wants a crooked work surface.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Tape measure&lt;/strong&gt; to get the height right and measure the distance between the studs.&lt;/li&gt;
	&lt;li&gt;A &lt;strong&gt;stud finder&lt;/strong&gt; or old man that knows how to knock on the wall and tell where the studs are. Since this is a desk and will be supporting a lot of weight, the brackets need to be screwed into the studs in the walls. If you do not have a stud finder or an old man, I provide some tips below in guessing where the studs are.&lt;/li&gt;
	&lt;li&gt;A &lt;strong&gt;Pencil&lt;/strong&gt;, which is ridiculously hard to find around my house in this day and age.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;Hanging a shelf is taught in Adulthood 101. We are hanging a really big shelf, but it is not much harder than that.&lt;/p&gt;
&lt;h3&gt;Finding the studs&lt;/h3&gt;
&lt;p&gt;Finding the studs was my biggest challenge. My house has this crazy cement board instead of typical drywall that is in most houses, so a stud finder does not work. Which means I had to guess. Studs are typically placed 16&amp;quot; apart (or 24&amp;quot; in some houses) on the center, meaning it is 16&amp;quot; from the center of one stud to the center of the other.&lt;/p&gt;
&lt;p&gt;Outlets are attached to studs when the house is built. Find an outlet on the wall and on either side of it is likely a stud. The first stud will &lt;em&gt;likely&lt;/em&gt; be in one of the corners. Measure from the corner of the wall to the outlet, and the distance between the corner to one side of the outlet should be about 3/4&amp;quot; off of a multiple of 16. If that is not the case, then try measuring from the other corner. If neither of them are, then just try drilling a hole with the smallest drillbit you can find on either side of the outlet to see if you can find the stud. Once you find one stud, it is pretty easy to find the rest.&lt;/p&gt;
&lt;p&gt;From there, measure 16&amp;quot; and try drilling another small hole. If you hit another stud, then your studs are 16&amp;quot; apart. If not, then try 24&amp;quot;. I ended up with about 6 tiny holes in my wall just trying to find the studs. They are easily filled and painted over.&lt;/p&gt;
&lt;h3&gt;Getting the height right&lt;/h3&gt;
&lt;p&gt;After I found the studs, I was ready to hang the brackets. But first I had to figure out how high to hang them. Which means I had to decide how high I wanted my desk to be. Every standup desk I have ever used felt too low, so I wanted to aim high. My first attempt I aimed too high.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://standupdesks.com/measure.shtml"&gt;standupdesk.com has some measuring tips&lt;/a&gt;. In the end, I found their suggestion of 1&amp;quot; below the elbow to be just right for me. My desk ended up at 45&amp;quot;.&lt;/p&gt;
&lt;p&gt;After hanging the brackets on the wall, place your desktop on them and try it out for a day. If this is your first experience with a stand up desk, your first day will probably feel uncomfortable in your legs and lower back. If it is uncomfortable in your shoulders and upper back, then the height is probably not right.&lt;/p&gt;
&lt;h3&gt;Hanging the desk&lt;/h3&gt;
&lt;p&gt;I wanted my desk to be at 45&amp;quot;, so I subtracted 1 1/2&amp;quot; for the thickness of my desktop, and another 1 1/2&amp;quot; for the distance from the top edge of the bracket to the first screw hole and I drilled my first pilot hole. I measured over in multiples of 16&amp;quot; to where I wanted the next bracket and marked it with a pencil, then used the level to make sure it is even with the first pilot hole.&lt;/p&gt;
&lt;p&gt;I attached the brackets with just one screw and laid my desktop to test the height. I worked at the desk for a day like this to make sure I liked the height. The next day, I drilled the remaining pilot holes and screwed the brackets into the desktop.&lt;/p&gt;
&lt;h2&gt;Happy ending&lt;/h2&gt;
&lt;p&gt;I am thrilled with how the desk turned out. It is extremely stable and takes up considerably less space than &lt;a href="http://cl.ly/1g3V320P2p12162G3c2V"&gt;my previous desk&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="/assets/4f0746dddabe9d682f002115/content/img_5070.jpg" alt="" /&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/3bRlf4wPpjc" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2012/01/09/the-40-standup-desk/</feedburner:origLink></entry>
  
    <entry>
      <title>Wasting Time</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/AXAMq1WoR0w/" />
      <id>4f0771aadabe9d371200338f</id>
      <updated>2012-01-06T17:30:19-05:00</updated>
      <published>2012-01-06T17:00:00-05:00</published>
      <category term="rant" />
      <summary type="html">&lt;p&gt;From now on, when I waste time, I&amp;#8217;m ging to try to do it by learning something new, even if it&amp;#8217;s something I won&amp;#8217;t ever use.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;I remember the day I bought &lt;a href="http://macromates.com/"&gt;TextMate&lt;/a&gt;. Like everyone, I loved TextMate. I loved it so much that I procrastinated learning anything else while I waited for TextMate Forever to be released. The new release that fixed everything was going to be out any day, so why should I waste time learning something new?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why should I waste time learning something new?&lt;/strong&gt; That question echoed in my head when I explained to a friend why I hadn&amp;#8217;t tried another editor yet. &lt;em&gt;Why should I &lt;strong&gt;waste&lt;/strong&gt; time &lt;strong&gt;learning&lt;/strong&gt; something new?&lt;/em&gt; Since when is learning something new a waste of time? I can think of many ways I have wasted time, and none of them involve learning something new.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;72 hours&lt;/strong&gt;. That&amp;#8217;s how long I wasted watching four and a half seasons of &lt;a href="http://en.wikipedia.org/wiki/24_(TV_series)"&gt;24&lt;/a&gt;. I will never get those hours back. Now that&amp;#8217;s a waste! It was not relaxing (it was actually quite stressful and anxiety inducing), it wasn&amp;#8217;t intellectually stimulating and I didn&amp;#8217;t learn anything. Fortunately I came to my senses and stopped watching it.&lt;/p&gt;
&lt;p&gt;From now on, when I waste time, I&amp;#8217;m going to try to do it by learning something new, even if it&amp;#8217;s something I won&amp;#8217;t ever use.&lt;/p&gt;
&lt;p&gt;On a related note, next week I will be blogging about my experience with learning &lt;a href="http://www.sublimetext.com/"&gt;Sublime Text 2&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/AXAMq1WoR0w" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2012/01/06/wasting-time/</feedburner:origLink></entry>
  
    <entry>
      <title>GitHub Acquires Ordered List</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/Bpe7v5-DjeA/" />
      <id>4edc3d06dabe9d211500f3ae</id>
      <updated>2011-12-05T13:02:46-05:00</updated>
      <published>2011-12-05T13:00:00-05:00</published>
      <category term="work" />
      <summary type="html">&lt;p&gt;I am thrilled to announce that I am now a GitHubber.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;I am thrilled to announce that I am now a GitHubber. Check out the official announcements on both the &lt;a href="https://github.com/blog/993-ordered-list-is-a-githubber"&gt;GitHub&lt;/a&gt; and &lt;a href="http://orderedlist.com/blog/articles/ordered-list-acquired-by-github/"&gt;Ordered List&lt;/a&gt; blogs.&lt;/p&gt;
&lt;p&gt;When I joined Ordered List, we had grand plans of building a great company on the back of some amazing products. GitHub is already doing that, so when the opportunity came to join forces with them, I had almost no hesitation.&lt;/p&gt;
&lt;p&gt;Personally, I look forward to bringing the &lt;a href="http://speakerdeck.com"&gt;products&lt;/a&gt; &lt;a href="http://get.gaug.es"&gt;we&lt;/a&gt; &lt;a href="http://get.harmonyapp.com"&gt;built&lt;/a&gt; and the experiences we gained building them to make GitHub even better. GitHub has an &lt;a href="https://github.com/about"&gt;amazing team&lt;/a&gt; and I cannot wait to inspire and be inspired by them.&lt;/p&gt;
&lt;p&gt;&lt;img src="/assets/4edc4921dabe9d21150166d1/olctocat.png" alt="" /&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/Bpe7v5-DjeA" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2011/12/05/github-acquires-ordered-list/</feedburner:origLink></entry>
  
    <entry>
      <title>Tunneling to Production</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/e0uup0d5v6w/" />
      <id>4ed7f94ddabe9d0fdc02074f</id>
      <updated>2011-12-01T17:17:29-05:00</updated>
      <published>2011-12-01T17:00:00-05:00</published>
      <category term="ssh" /><category term="tidbit" />
      <summary type="html">&lt;p&gt;A quick tidbit on how to use &lt;a href="https://github.com/jamis/net-ssh-gateway"&gt;net-ssh-gateway&lt;/a&gt; in your Ruby scripts to tunnel to a remote server.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;One of the great features of &lt;span class="caps"&gt;SSH&lt;/span&gt; is its ability to tunnel any port to a remote server. I&amp;#8217;ve used this &lt;span class="caps"&gt;SSH&lt;/span&gt; command often to connect to a production database:&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;$ ssh -N -L 27018:localhost:27017 example.com&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;That &lt;span class="caps"&gt;SSH&lt;/span&gt; command takes all traffic to port 27018 on your local host and tunnels it to 27017 on the remote server.&lt;/p&gt;
&lt;p&gt;If you rarely use this command, it&amp;#8217;s easy to forget it. So why not create the tunnel directly in the scripts that need it using &lt;a href="https://github.com/jamis/net-ssh-gateway"&gt;net-ssh-gateway&lt;/a&gt;? We&amp;#8217;ve been using this on &lt;a href="http://gaug.es"&gt;Gaug.es&lt;/a&gt; and it works great.&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;net/ssh/gateway&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;

gateway = &lt;span class="co"&gt;Net&lt;/span&gt;::&lt;span class="co"&gt;SSH&lt;/span&gt;::&lt;span class="co"&gt;Gateway&lt;/span&gt;.new(host, username)
gateway.open(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;127.0.0.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="i"&gt;27017&lt;/span&gt;, &lt;span class="i"&gt;27018&lt;/span&gt;)

db = &lt;span class="co"&gt;Mongo&lt;/span&gt;::&lt;span class="co"&gt;Connection&lt;/span&gt;.new(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;127.0.0.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="i"&gt;27018&lt;/span&gt;).db(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;myapp_production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="c"&gt;# …&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/e0uup0d5v6w" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2011/12/01/tunneling-to-production/</feedburner:origLink></entry>
  
    <entry>
      <title>HAML: the unforgivable sin</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/zD8LqFm8Ppw/" />
      <id>4ecf14a9dabe9d0f2a02a1ec</id>
      <updated>2011-12-09T10:16:19-05:00</updated>
      <published>2011-11-30T08:00:00-05:00</published>
      <category term="haml" /><category term="html" /><category term="opinion" /><category term="popular" />
      <summary type="html">&lt;p&gt;I have used &lt;a href="http://haml-lang.com/"&gt;&lt;span class="caps"&gt;HAML&lt;/span&gt;&lt;/a&gt; on several client projects, and every time it drives me nuts. While there are certainly some things that are nice about it, overall it is a net loss.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;The other day &lt;a href="http://twitter.com/#!/bkeepers/status/139360663851106305"&gt;I posted this on twitter&lt;/a&gt;: &amp;#8220;there are many mistakes I&amp;#8217;m willing to forgive, but using haml is not one of them&amp;#8221;. Many people replied asking me to explain why I don&amp;#8217;t like it, so here it goes…&lt;/p&gt;
&lt;p&gt;I have used &lt;a href="http://haml-lang.com/"&gt;&lt;span class="caps"&gt;HAML&lt;/span&gt;&lt;/a&gt; on several client projects, and every time it drives me nuts. While there are certainly some things that are nice about it, overall it is a net loss.&lt;/p&gt;
&lt;h2&gt;For abstraction&amp;#8217;s sake&lt;/h2&gt;
&lt;p&gt;Abstractions are a beautiful thing. The goal of an abstraction is to reduce or factor out details that you don&amp;#8217;t care about. By removing details, an abstraction allows you to focus on the problem at hand and not the underlying implementation.&lt;/p&gt;
&lt;p&gt;But an abstraction fails when it doesn&amp;#8217;t remove the details. &lt;span class="caps"&gt;HAML&lt;/span&gt; is purely an abstraction of syntax. I still have to think about tags, ids and classes while I&amp;#8217;m writing &lt;span class="caps"&gt;HAML&lt;/span&gt;. It simply provides a more concise syntax.&lt;/p&gt;
&lt;h2&gt;So you hate &lt;span class="caps"&gt;SASS&lt;/span&gt; and CoffeeScript too?&lt;/h2&gt;
&lt;p&gt;No. While &lt;span class="caps"&gt;SASS&lt;/span&gt; and CoffeeScript do abstract the syntax of &lt;span class="caps"&gt;CSS&lt;/span&gt; and JavaScript, they provide other great features.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;SASS&lt;/span&gt; (or even better, &lt;span class="caps"&gt;SCSS&lt;/span&gt;) is a great abstraction because it enables features that would be nearly impossible to maintain by hand. It makes it easier to break your styles up into small, reusable components, and encourages good design patterns.&lt;/p&gt;
&lt;p&gt;Likewise, CoffeeScript adds &amp;#8220;features&amp;#8221; on top of JavaScript that are extremely cumbersome to code by hand. CoffeeScript guards against common coding mistakes, like ensuring variables are properly declared within lexical scope.&lt;/p&gt;
&lt;h2&gt;Easy to write, hard to read&lt;/h2&gt;
&lt;p&gt;I agree that &lt;span class="caps"&gt;HAML&lt;/span&gt; is easier to write than &lt;span class="caps"&gt;HTML&lt;/span&gt;, simply because it involves less typing. But I feel it is infinitely harder to read. While the indentation makes it easy to see the nesting, the extremely overloaded synax requires careful attention to each line.&lt;/p&gt;
&lt;p&gt;If &lt;span class="caps"&gt;HAML&lt;/span&gt; provided some value other than saving a few keystrokes, I would likely become a believer. But it doesn&amp;#8217;t.&lt;/p&gt;
&lt;p&gt;Typing is not my bottleneck in coding. Thinking is. And forcing my brain to parse &lt;span class="caps"&gt;HAML&lt;/span&gt; when it is occupied by more important things is not conducive to productivity.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/zD8LqFm8Ppw" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2011/11/30/haml-the-unforgivable-sin/</feedburner:origLink></entry>
  
    <entry>
      <title>Startup Fatigue</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/-630e-8_Uow/" />
      <id>4ec49650dabe9d20f60013a9</id>
      <updated>2011-11-21T21:50:29-05:00</updated>
      <published>2011-11-21T13:00:00-05:00</published>
      <category term="michigan" /><category term="startups" />
      <summary type="html">&lt;p&gt;If you want to fund the next great software company, then take an idea—any idea because it doesn&amp;#8217;t really matter—and partner with someone that can execute.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;People in West Michigan are doing startups completely wrong. I&amp;#8217;m sure this is relevant elsewhere, but I can only speak for the context in which I live.&lt;/p&gt;
&lt;p&gt;The problem with the startup fad is that people like Rick Devos are throwing money at people that have only sold them on an idea and have no history of execution. I&amp;#8217;m sure the presenters at &lt;a href="http://5x5night.com/"&gt;5&amp;#215;5&lt;/a&gt; have a lot of interesting ideas. I&amp;#8217;ve heard many good ideas in the past year for business that I would gladly give my money to if they existed. But they don&amp;#8217;t, because nobody has been able to execute on the idea. What the startup culture doesn&amp;#8217;t understand is that ideas don&amp;#8217;t matter. &lt;strong&gt;Execution matters&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In his now classic post, &lt;a href="http://www.oreillynet.com/onlamp/blog/2005/08/ideas_are_just_a_multiplier_of.html"&gt;Derek Sivers succinctly articulates&lt;/a&gt; that ideas are just a multiplier of execution. Or, as &lt;a href="http://tech.fortune.cnn.com/2011/11/11/steve-jobs-the-parable-of-the-stones/"&gt;Steve Jobs put it&lt;/a&gt;, &amp;#8220;the problem with that is that there’s just a tremendous amount of craftsmanship in between a great idea and a great product.&amp;#8221;&lt;/p&gt;
&lt;p&gt;If you want to fund the next great software company, then take an idea—any idea because it doesn&amp;#8217;t really matter—and partner with someone that can execute. There are &lt;a href="http://www.mattblodgett.com/2007/03/software-consulting-companies-in-grand.html"&gt;several&lt;/a&gt; &lt;a href="http://www.atomicobject.com/"&gt;great&lt;/a&gt; &lt;a href="http://collectiveidea.com"&gt;software&lt;/a&gt; &lt;a href="http://mutuallyhuman.com"&gt;companies&lt;/a&gt; in the area that have a history of execution. Pick one and give them enough resources to deliver.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; A few people have taken the previous paragraph as me saying &amp;#8220;contract&amp;#8221; with one of these companies. I don&amp;#8217;t mean that at all. I literally mean &lt;strong&gt;partner&lt;/strong&gt; with them. Find a company that&amp;#8217;s interested in your idea, give them an equity stake, fund them, and help them find people that can execute in areas that they can&amp;#8217;t.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/-630e-8_Uow" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2011/11/21/startup-fatigue/</feedburner:origLink></entry>
  
    <entry>
      <title>Sinatra and SSL</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/zrLghaBeVOQ/" />
      <id>4ea72077dabe9d3cb8003e0b</id>
      <updated>2011-11-17T16:23:27-05:00</updated>
      <published>2011-11-16T22:00:00-05:00</published>
      <category term="sinatra" /><category term="ssl" />
      <summary type="html">&lt;p&gt;I recently rolled out &lt;span class="caps"&gt;SSL&lt;/span&gt; support on &lt;a href="http://get.gaug.es/"&gt;Gaug.es&lt;/a&gt;. Gaug.es is a fairly simple Sinatra application. Most of the app requires authentication, but there are a few public URLs that are unauthenticated. Here&amp;#8217;s how I implemented it.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;I recently rolled out &lt;span class="caps"&gt;SSL&lt;/span&gt; support on &lt;a href="http://get.gaug.es/"&gt;Gaug.es&lt;/a&gt;. Gaug.es is a fairly simple Sinatra application. Most of the app requires authentication, but there are a few public URLs that are unauthenticated.&lt;/p&gt;
&lt;p&gt;There two essential steps in securing an app which requires user authentication and then gives them unlimited access to their data:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Only set and use session cookies over secure connections&lt;/li&gt;
	&lt;li&gt;Mark all cookies as &lt;a href="http://en.wikipedia.org/wiki/HTTP_cookie#Secure_cookie"&gt;Secure&lt;/a&gt; so the browser doesn&amp;#8217;t transmit them when requesting a non-secure &lt;span class="caps"&gt;URL&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I evaluated a couple options, but quickly settled on &lt;a href="https://github.com/josh/rack-ssl"&gt;Josh Peek&amp;#8217;s simple rack-ssl&lt;/a&gt; gem. &lt;code&gt;Rack::SSL&lt;/code&gt; is a middleware that just redirects &lt;code&gt;http://&lt;/code&gt; requests to &lt;code&gt;https://&lt;/code&gt; and marks any cookies set by your app as secure.&lt;/p&gt;
&lt;p&gt;I wanted to create a clear separation between the parts of Gaug.es that require authentication and those that don&amp;#8217;t to avoid accidentally leaking cookies (yeah, that&amp;#8217;s a technical term), so I moved all the non-authenticated routes to a separate Sinatra application and insert it into the main app. Here&amp;#8217;s essentially what I ended up with:&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Application&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;Sinatra&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;
  set &lt;span class="sy"&gt;:ssl&lt;/span&gt;, lambda { !development? }

  use &lt;span class="co"&gt;PublicApplication&lt;/span&gt;
  use &lt;span class="co"&gt;Rack&lt;/span&gt;::&lt;span class="co"&gt;SSL&lt;/span&gt;, &lt;span class="sy"&gt;:exclude&lt;/span&gt; =&amp;gt; lambda { !ssl? }
  use &lt;span class="co"&gt;Rack&lt;/span&gt;::&lt;span class="co"&gt;Session&lt;/span&gt;::&lt;span class="co"&gt;Cookie&lt;/span&gt;, &lt;span class="sy"&gt;:expire_after&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;1&lt;/span&gt;.week, &lt;span class="sy"&gt;:secret&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;…&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;

  get &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
    &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;Secure!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;
  &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="r"&gt;end&lt;/span&gt;

&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;PublicApplication&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;Sinatra&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;
  get &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
    &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;Hello world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;
  &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/zrLghaBeVOQ" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2011/11/16/sinatra-and-ssl/</feedburner:origLink></entry>
  
    <entry>
      <title>Why another queuing library?</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/ZEC9_NPxhpI/" />
      <id>4e8930c4dabe9d4b0a00af5e</id>
      <updated>2011-10-19T22:06:26-04:00</updated>
      <published>2011-10-19T21:41:00-04:00</published>
      <category term="qu" /><category term="ruby" />
      <summary type="html">&lt;p&gt;Along with the release of &lt;a href="http://speakerdeck.com"&gt;Speaker Deck&lt;/a&gt;, we released &lt;a href="https://github.com/bkeepers/qu"&gt;Qu&lt;/a&gt;, a Ruby library for queuing and processing background jobs. Why another queuing library? I briefly explain why I wrote Qu in the &lt;a href="https://github.com/bkeepers/qu#readme"&gt;&lt;span class="caps"&gt;README&lt;/span&gt;&lt;/a&gt;, so read that first. But I wanted to give a little more of the backstory.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;Along with the release of &lt;a href="http://speakerdeck.com"&gt;Speaker Deck&lt;/a&gt;, we released &lt;a href="https://github.com/bkeepers/qu"&gt;Qu&lt;/a&gt;, a Ruby library for queuing and processing background jobs. Why another queuing library? I briefly explain why I wrote Qu in the &lt;a href="https://github.com/bkeepers/qu#readme"&gt;&lt;span class="caps"&gt;README&lt;/span&gt;&lt;/a&gt;, so read that first. But I wanted to give a little more of the backstory.&lt;/p&gt;
&lt;h2&gt;Reason #1: Resque issues on Heroku&lt;/h2&gt;
&lt;p&gt;Speaker Deck makes heavy use of background processing to turn PDFs of slides into the gorgeous player that you see on the site. Speaker Deck was using Resque, but unfortunately there a few issues with Resque on Heroku (&lt;a href="https://github.com/defunkt/resque/issues/368"&gt;#368&lt;/a&gt;, &lt;a href="https://github.com/defunkt/resque/issues/319"&gt;#319&lt;/a&gt;). The mishandling of signals described in #368 was especially an issue for us,  because any time we deploy or scale the workers up or down, we would lose jobs; they wouldn&amp;#8217;t show up as failed, they would just completely vanish.&lt;/p&gt;
&lt;p&gt;So the logical approach would be to just fork resque, reverse the signals, and use that version, right? If that was my only issue with resque, then yes, but…&lt;/p&gt;
&lt;h2&gt;Reason #2: Infrastructure dependencies&lt;/h2&gt;
&lt;p&gt;At &lt;a href="http://orderedlist.com"&gt;Ordered List&lt;/a&gt;, all of our products are built on top of &lt;a href="http://mongodb.org"&gt;MongoDB&lt;/a&gt;. Both &lt;a href="http://get.harmonyapp.com"&gt;Harmony&lt;/a&gt; and &lt;a href="http://get.gaug.es"&gt;Gaug.es&lt;/a&gt; already have infrastructure set up to properly scale, replicate, and back up MongoDB. Both apps currently use resque minimally. Gaug.es literally only uses it to send email in the background since everything else is done in real-time. Harmony uses it slightly more.&lt;/p&gt;
&lt;p&gt;So to run resque, we have to administer 4 additional Redis instances (master and slave in both production and staging) for both products. If we were at a scale where we needed Redis, the dependency would be warranted. But mongo is currently more than capable of doing what we need.&lt;/p&gt;
&lt;p&gt;And I know we&amp;#8217;re not alone. I&amp;#8217;ve had many consulting clients in the past on shoe-string hosting budgets that didn&amp;#8217;t need the robustness of Redis. delayed_job, were it more stable, was perfect for them.&lt;/p&gt;
&lt;h2&gt;Reason #3: Cheap thrills&lt;/h2&gt;
&lt;p&gt;Having been the primary maintainer of a &lt;a href="https://github.com/collectiveidea/delayed_job"&gt;popular fork of delayed_job&lt;/a&gt; for a couple years, I feel like I formed a lot of ideas about how I would build it differently. I made a lot of mistakes with delayed_job (let&amp;#8217;s not talk about &lt;span class="caps"&gt;YAML&lt;/span&gt;), but I also learned a lot of lessons.&lt;/p&gt;
&lt;p&gt;Supporting multiple backends is one of the most important things I think Qu can provide. Resque has a thriving ecosystem that is all built around Redis. As I said in Reason #2, Redis often makes sense, but there are times when it doesn&amp;#8217;t. I find it extremely disappointing that the flourishing Resque ecosystem can&amp;#8217;t be leveraged with another database backend. I think Qu can create an ecosystem with the benefit of being able to use it in every case.&lt;/p&gt;
&lt;p&gt;One criticism of this approach is that databases have different features and strengths, so it will be impossible to make the backends behave the same. Each backend will certainly have it&amp;#8217;s quirks, but fundamentally, I think they can all behave the same. The current Redis and Mongo backends are perfect examples of this. Internally, they both operate very differently, but their &lt;span class="caps"&gt;API&lt;/span&gt; is the same and they both pass the &lt;a href="https://github.com/bkeepers/qu/blob/master/lib/qu/backend/spec.rb"&gt;same specs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So far, I&amp;#8217;m very pleased with how Qu is turning out. There is still a lot of work to be done, but I think I have a good start and have been pleased by the intrest in it already.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/ZEC9_NPxhpI" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2011/10/19/why-another-queuing-library/</feedburner:origLink></entry>
  
    <entry>
      <title>bundle exec fatigue</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/yCzhlTOj93o/" />
      <id>4e70ea4fdabe9d70ab0013b9</id>
      <updated>2011-09-15T10:15:07-04:00</updated>
      <published>2011-09-14T13:54:00-04:00</published>
      <category term="bundler" /><category term="git" />
      <summary type="html">&lt;p&gt;My setup for overcoming &lt;code&gt;bundle exec&lt;/code&gt; fatigue.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;Tired of typing &lt;code&gt;bundle exec rake&lt;/code&gt;? Me too. I even aliased it to &lt;code&gt;bx&lt;/code&gt;, but that still breaks autocomplete and annoys me in subtle ways. So this week I finally remedied that situation, and thought I would share my setup since I haven&amp;#8217;t seen anyone else with as nice of a setup.&lt;/p&gt;
&lt;p&gt;First, bundler has a handy feature to install stubs for all of the bins in your gem dependencies.&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;$ bundle install --binstubs&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This is great, but it creates them all in a &lt;code&gt;bin&lt;/code&gt; directory in your project, so not only do you have to type `./bin/rake`, but you also have to ignore them from git. I&amp;#8217;m lazy and that&amp;#8217;s &lt;strong&gt;too much work&lt;/strong&gt;!&lt;/p&gt;
&lt;h2&gt;The Final Solution&lt;/h2&gt;
&lt;p&gt;My bundler setup has 3 essential components: 1) an alias for &lt;code&gt;bundle install&lt;/code&gt; which always installs bin stubs into &lt;code&gt;.bin&lt;/code&gt;, 2) adding &lt;code&gt;.bin&lt;/code&gt; to &lt;code&gt;PATH&lt;/code&gt;, and 3) globally excluding the &lt;code&gt;.bin&lt;/code&gt; directory from all git repos.&lt;/p&gt;
&lt;p&gt;First, put the following in &lt;code&gt;.bashrc&lt;/code&gt;, &lt;code&gt;.profile&lt;/code&gt;, or &lt;code&gt;.bash_profile&lt;/code&gt; to alias &lt;code&gt;bundle install&lt;/code&gt;  and add &lt;code&gt;.bin&lt;/code&gt; to your &lt;code&gt;PATH&lt;/code&gt;. :&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;alias bi=&amp;quot;bundle install --binstubs=.bin&amp;quot;
export PATH=&amp;quot;./.bin:$PATH&amp;quot;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Now every time you run &lt;code&gt;bi&lt;/code&gt;, it will install the gem dependencies and add the bin stubs in your path so you can just run &lt;code&gt;rake&lt;/code&gt; like the good ol&amp;#8217; days.  Now all you need to do is create a global &lt;code&gt;~/.gitignore&lt;/code&gt; that always excludes &lt;code&gt;.bin&lt;/code&gt; from every project, and tell git to use it.&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;$ git config --global core.excludesfile ~/.gitignore&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This approach has been working wonderfully for me.  Now I just need to get out of the habit of typing &lt;code&gt;bx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;ins&gt;Update: Travis Tilley pointed out &lt;a href="https://github.com/mpapis/rubygems-bundler"&gt;rubygems-bundler&lt;/a&gt; in the comments. It looks like another unobtrusive (and more secure) way to accomplish this same thing.&lt;/ins&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/yCzhlTOj93o" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2011/09/14/bundle-exec-fatigue/</feedburner:origLink></entry>
  
    <entry>
      <title>Sortable lists in key-value data stores</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/9I0twm-ejkA/" />
      <id>4e66386edabe9d3ad000017b</id>
      <updated>2011-09-06T12:43:45-04:00</updated>
      <published>2011-09-06T11:13:00-04:00</published>
      <category term="database" /><category term="git" /><category term="toystore" />
      <summary type="html">&lt;p&gt;Using floats between 0 and 1 to specify manual sort order in a key-value data store.&lt;/p&gt;</summary>
      <content type="html">&lt;p class="feedreader"&gt;Note: This post contains illustrations that may not appear properly in your feed reader. You may want to view the original post in your browser&lt;/p&gt;
&lt;p&gt;Last week I &lt;a href="http://opensoul.org/blog/archives/2011/09/01/git-the-nosql-database/"&gt;blogged about using Git as a key-value database&lt;/a&gt; as I experiment with using Git as a database backend for an application.&lt;/p&gt;
&lt;p&gt;One of the first challenges I encountered is not unique to Git, but to key-value stores in general; allowing users to &lt;strong&gt;manually sort&lt;/strong&gt; a list of items without the niceties of atomic operations.&lt;/p&gt;
&lt;p&gt;The most common way to allow users to manually sort a list is simply to store a &lt;strong&gt;position&lt;/strong&gt; along with each record. When the user reorders the records, then you update the position on each record. This works well as long as the data set is small and there are guards in place to prevent two users from sorting at the same time.&lt;/p&gt;
&lt;p&gt;However, with large amounts of data or a data store that doesn&amp;#8217;t support atomic operations on multiple records, this &lt;strong&gt;doesn&amp;#8217;t scale well&lt;/strong&gt;. And specific to Git, updating every single record pretty much guarantees a merge conflict when the Git repos are pushed to a common origin.&lt;/p&gt;
&lt;p&gt;I needed a simple way to create a manually sortable list that avoided merge conflicts. Preferably, a manual reorder would only update the one record whose order changed, and leave the others untouched.&lt;/p&gt;
&lt;h2&gt;Floats between 0 and 1&lt;/h2&gt;
&lt;p&gt;The solution I arrived at is to specify order as a float between 0 and 1. When you move an item, it&amp;#8217;s position becomes a float that is half-way between the item before and the item after it.  Items moved to the top of the list will get closer and closer to 0, and items to the bottom of the list get closer to 1.&lt;/p&gt;
&lt;p&gt;Let me illustrate with some examples. The first item added to the list will get a position of &lt;strong&gt;0.5&lt;/strong&gt;, half-way between &lt;strong&gt;0&lt;/strong&gt; and &lt;strong&gt;1&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="example"&gt;
&lt;ul&gt;
&lt;li&gt;
Item A
&lt;span class="position"&gt;position: 0.5&lt;/span&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;The second item added will be half-way between the last item, &lt;strong&gt;0.5&lt;/strong&gt;, and &lt;strong&gt;1&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="example"&gt;
&lt;ul&gt;
    &lt;li class="previous"&gt;Item A &lt;span class="position"&gt;position: 0.5&lt;/span&gt;&lt;/li&gt;
    &lt;li&gt;Item B &lt;span class="position"&gt;position: 0.75&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;The third item added will be half-way between the last item, &lt;strong&gt;0.75&lt;/strong&gt;, and &lt;strong&gt;1&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="example"&gt;
&lt;ul&gt;
    &lt;li class="previous"&gt;Item A &lt;span class="position"&gt;position: 0.5&lt;/span&gt;&lt;/li&gt;
    &lt;li class="previous"&gt;Item B &lt;span class="position"&gt;position: 0.75&lt;/span&gt;&lt;/li&gt;
    &lt;li&gt;Item C &lt;span class="position"&gt;position: 0.875&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;Now, if the user wanted to move &lt;strong&gt;Item C&lt;/strong&gt; in between &lt;strong&gt;Item A&lt;/strong&gt; and &lt;strong&gt;Item B&lt;/strong&gt;, then its new position would be half way between the position of the item before and after it.&lt;/p&gt;
&lt;div class="example"&gt;
&lt;ul&gt;
    &lt;li class="previous"&gt;Item A &lt;span class="position"&gt;position: 0.5&lt;/span&gt;&lt;/li&gt;
    &lt;li&gt;Item C &lt;span class="position"&gt;position: 0.625&lt;/span&gt;&lt;/li&gt;
    &lt;li class="previous"&gt;Item B &lt;span class="position"&gt;position: 0.75&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;If the user wanted to move &lt;strong&gt;Item B&lt;/strong&gt; to the top of the list, then its new position would be half way between the first item in the list and 0.&lt;/p&gt;
&lt;div class="example"&gt;
&lt;ul&gt;
    &lt;li&gt;Item B &lt;span class="position"&gt;position: 0.25&lt;/span&gt;&lt;/li&gt;
    &lt;li class="previous"&gt;Item A &lt;span class="position"&gt;position: 0.5&lt;/span&gt;&lt;/li&gt;
    &lt;li class="previous"&gt;Item C &lt;span class="position"&gt;position: 0.625&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;And so on and so on…&lt;/p&gt;
&lt;p&gt;This solution allows the user to reorder items without updating every record in the data store. As more items get added to the list and moved around, the position just becomes more and more precise. There is a theoretical limit to the number of times items can be moved, but it is very high.&lt;/p&gt;
&lt;style&gt;
  .feedreader {
    display:none;
  }
  .example {
    padding: 1.5em;
  }
  .example ul {
    margin:0;
    list-style:none;
    top:0;
  }
  .example li {
    margin: 1em;
    padding: 1em 2em;
    background: #EEE;
    background: -webkit-linear-gradient(#EEE, #DDD 80%);
    -webkit-box-shadow: 0px 1px 3px rgba(0,0,0,0.3);
    -webkit-border-radius: 5px;
   font-weight: bold;
  }
  .example li .position {
    display: inline-block;
    float: right;
    font-weight:normal;
    font-size:0.9em;
    width: 90px;
  }
  .example li.previous {
    opacity: 0.5;
  }
&lt;/style&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/9I0twm-ejkA" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2011/09/06/sortable-lists-in-key-value-data-stores/</feedburner:origLink></entry>
  
    <entry>
      <title>Git: the NoSQL database</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/Q7FXfg0Fu0A/" />
      <id>4e5d7350dabe9d09d0003809</id>
      <updated>2011-12-09T10:27:07-05:00</updated>
      <published>2011-09-01T09:00:00-04:00</published>
      <category term="git" /><category term="nosql" /><category term="popular" /><category term="ruby" /><category term="toystore" />
      <summary type="html">&lt;p&gt;Use toystore and adapter-git to work with the hottest new NoSQL database.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;We all know that Git is pretty amazing. It&amp;#8217;s fast, reliable, flexible, and it keeps our project history safely nuzzled in it&amp;#8217;s cozy object database while we sleep soundly at night. But I&amp;#8217;m curious to see if it can be used for &lt;strong&gt;more than code&lt;/strong&gt;. I&amp;#8217;ve had a few apps in the back of my mind for a while now that would be really interesting if the data was stored in Git.&lt;/p&gt;
&lt;p&gt;If only there was an easy way to read and write a Git repo from Ruby…&lt;/p&gt;
&lt;h2&gt;Toystore &amp;amp; adapter-git&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/newtoy/toystore"&gt;Toystore&lt;/a&gt; is an ActiveModel-based object mapper for key-value data stores. The beauty of Toystore is that it doesn&amp;#8217;t care what the backend is. It uses &lt;a href="https://github.com/newtoy/adapter"&gt;Adapter&lt;/a&gt; to abstract the connection to any data store that can set, get, and delete keys.&lt;/p&gt;
&lt;p&gt;Well, &lt;strong&gt;Git is a key-value store&lt;/strong&gt;; it supports set, get and delete on keys (a.k.a. paths). So I sat down with Scott Chacon&amp;#8217;s &lt;a href="http://peepcode.com/products/git-internals-pdf"&gt;Git Internals&lt;/a&gt; Peepcode &lt;span class="caps"&gt;PDF&lt;/span&gt; and put together &lt;a href="https://github.com/bkeepers/adapter-git"&gt;adapter-git&lt;/a&gt;, built on top of &lt;a href="https://github.com/mojombo/grit"&gt;Grit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now I can create pretty models that are stored in Git.&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Item&lt;/span&gt;
  include &lt;span class="co"&gt;Toy&lt;/span&gt;::&lt;span class="co"&gt;Store&lt;/span&gt;

  store &lt;span class="sy"&gt;:git&lt;/span&gt;, &lt;span class="co"&gt;Gaskit&lt;/span&gt;.repo,
    &lt;span class="sy"&gt;:branch&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;,
    &lt;span class="sy"&gt;:path&lt;/span&gt;   =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;

  attribute &lt;span class="sy"&gt;:title&lt;/span&gt;,       &lt;span class="co"&gt;String&lt;/span&gt;
  attribute &lt;span class="sy"&gt;:description&lt;/span&gt;, &lt;span class="co"&gt;String&lt;/span&gt;
  attribute &lt;span class="sy"&gt;:created_at&lt;/span&gt;   &lt;span class="co"&gt;Time&lt;/span&gt;, &lt;span class="sy"&gt;:default&lt;/span&gt; =&amp;gt; lambda { &lt;span class="co"&gt;Time&lt;/span&gt;.now }
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Toystore uses conventions that will be familiar to anyone that has used Active Record or MongoMapper.&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;item = &lt;span class="co"&gt;Item&lt;/span&gt;.create!(&lt;span class="sy"&gt;:title&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;Git: the NoSQL database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)
item.update_attributes(&lt;span class="sy"&gt;:description&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;OMG this is awesome!&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The biggest difference is that you can&amp;#8217;t &amp;#8220;find&amp;#8221; records. The data stored in a key-value store is opaque, so all you can do is &lt;strong&gt;get&lt;/strong&gt; it by key.&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;item = &lt;span class="co"&gt;Item&lt;/span&gt;.get!(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;3FB053FA-0A3B-4903-9CE0-2A8A964E0F37&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;I have no idea if Git will work as a data backend for an application. I&amp;#8217;m sure GitHub has solved many of the problems with concurrency and scaling a filesystem.&lt;/p&gt;
&lt;p&gt;There is still a lot of room for improvement on adapter-git too.  Here are just a few things I&amp;#8217;d like to add soon:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Locking&lt;/strong&gt; &amp;#8211; I wouldn&amp;#8217;t want to use adapter-git for anything with a lot of concurrency at the moment. Git commits are atomic; you&amp;#8217;ll never corrupt a repo by a failed commit, but if you have a lot of concurrent access, you might loose commits.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Custom commit messages&lt;/strong&gt; &amp;#8211; Currently adapter-git just uses the ID of the key being set in the commit message. In the app I&amp;#8217;m experimenting with, I&amp;#8217;ve already had a desire to set custom commit messages.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Update working copy&lt;/strong&gt; &amp;#8211; adapter-git currently just works against the git repo itself. It doesn&amp;#8217;t update your working copy. So `git status` will currently tell you that you&amp;#8217;ve deleted files from your working copy after you update records with adapter-git.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Merge conflicts&lt;/strong&gt; &amp;#8211; I&amp;#8217;m looking forward to being able to programmatically resolve merge conflicts. Riak has a cool pattern for resolving conflicts on read, so I&amp;#8217;d love to see if I can build something into adapter-git and toystore to work in a similar fashion.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Check out &lt;a href="https://github.com/bkeepers/adapter-git"&gt;adapter-git&lt;/a&gt; on GitHub and try building an app backed by Git!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/Q7FXfg0Fu0A" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2011/09/01/git-the-nosql-database/</feedburner:origLink></entry>
  
    <entry>
      <title>Pusher notifications with EventMachine</title>
      <link href="http://feedproxy.google.com/~r/opensoul/~3/ZicFrSUM89w/" />
      <id>4e258a01dabe9d7285017e6b</id>
      <updated>2011-08-30T19:31:24-04:00</updated>
      <published>2011-08-30T18:55:00-04:00</published>
      <category term="em" /><category term="gauges" /><category term="pusher" /><category term="redis" />
      <summary type="html">&lt;p&gt;We used Redis to queue up notifications to send to Pusher and an EventMachine process to send them in near real-time on &lt;a href="http://get.gaug.es"&gt;Gaug.es&lt;/a&gt;.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;&lt;a href="http://gaug.es"&gt;Gaug.es&lt;/a&gt; has been a fun exercise in building an app to scale from the start.  &lt;a href="http://railstips.org/blog/archives/2011/05/04/eventmachine-and-passenger/"&gt;John Nunemaker previously posted about our quest&lt;/a&gt; to send live updates to the browser without slowing down the request cycle.&lt;/p&gt;
&lt;h2&gt;Backstory&lt;/h2&gt;
&lt;p&gt;We use &lt;a href="http://pusher.com/"&gt;Pusher&lt;/a&gt; to send realtime updates of stats to the Gaug.es dashboard. We started out on Heroku–which uses Thin, which runs on EventMachine–so we could just use EventMachine and Pusher&amp;#8217;s &lt;code&gt;trigger_async&lt;/code&gt; method to queue the Pusher update on the next tick, thus not affecting response times. But then we moved from Heroku to &lt;a href="http://railsmachine.com/"&gt;RailsMachine&lt;/a&gt; and their Passenger stack, so we wanted to find a way to keep the Pusher update out of the request cycle but still &amp;#8220;realtime&amp;#8221;.&lt;/p&gt;
&lt;p&gt;So John devised a genius plan of starting up a thread in Passenger that runs EventMachine. We can still use the &lt;code&gt;trigger_async&lt;/code&gt; method and take advantage of EventMachine&amp;#8217;s IO goodness, and only suffer hits in response time occasionally when Ruby&amp;#8217;s naive thread scheduling interrupts the passenger process in the middle of a request.&lt;/p&gt;
&lt;h2&gt;It &lt;del&gt;didn&amp;#8217;t&lt;/del&gt; &lt;ins&gt;did&lt;/ins&gt; work&lt;/h2&gt;
&lt;p&gt;This ran great in production for a few weeks. But as our traffic started to increase thanks to launching several new features, we started to run into problems. Passenger processes frequently started hanging and gobbling up ungodly amounts of &lt;span class="caps"&gt;CPU&lt;/span&gt;. The only way to recover was to snipe the processes with the friendly &lt;code&gt;kill -9&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So we tried to concoct a theory: since most of our hits are ridiculously fast (5-10ms), and we were running multiple threads but weren&amp;#8217;t doing anything fancy to set priority or control scheduling, the passenger thread must be handling requests faster than the EventMachine thread can send them to Pusher, causing the Event Machine thread to get backed up to the point where it has so many IO connections open that it thrashes the &lt;span class="caps"&gt;CPU&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;It turns out our theory was wrong (we were actually hitting a &lt;a href="https://gist.github.com/1079284"&gt;crazy bug with the regex engine in all major versions of Ruby&lt;/a&gt;), but we didn&amp;#8217;t know that at the time. So we concluded that we had to take the Pusher notification out of the request cycle.&lt;/p&gt;
&lt;h2&gt;Redis and EventMachine to the rescue&lt;/h2&gt;
&lt;p&gt;With help from &lt;a href="http://twitter.com/gaffneyc"&gt;Chris Gaffney&lt;/a&gt;, I came up with a different approach that pushes hits on a Redis list and uses a separate EventMachine process to send them to Pusher.&lt;/p&gt;
&lt;p&gt;In our Sinatra application, we just push &lt;span class="caps"&gt;JSON&lt;/span&gt; from the notification onto a list.&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="co"&gt;Gauges&lt;/span&gt;.redis.rpush(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, notification.to_json)&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Redis list operations are fast, even with a decent-sized blob of text. Now our app doesn&amp;#8217;t have to worry about the overhead of sending the notifications.&lt;/p&gt;
&lt;p&gt;Next we needed background process to process the Redis list and communicate with Pusher. EventMachine is well-suited for all of this IO, so we wrote this simple script:&lt;/p&gt;
&lt;div class="multiline_code"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;Gauges&lt;/span&gt;
  &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;Pusher&lt;/span&gt;
    &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.&lt;span class="fu"&gt;redis&lt;/span&gt;
      &lt;span class="iv"&gt;@redis&lt;/span&gt; ||= &lt;span class="co"&gt;EM&lt;/span&gt;::&lt;span class="co"&gt;Hiredis&lt;/span&gt;.connect(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;redis://localhost:6379&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
    &lt;span class="r"&gt;end&lt;/span&gt;

    &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.&lt;span class="fu"&gt;next&lt;/span&gt;
      redis.blpop(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="i"&gt;0&lt;/span&gt;).callback &lt;span class="r"&gt;do&lt;/span&gt; |list, data|
        notification = &lt;span class="co"&gt;JSON&lt;/span&gt;.parse(data)
        channel = notification.delete(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;channel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)
        ::&lt;span class="co"&gt;Pusher&lt;/span&gt;[channel].trigger_async(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;hit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, notification)
        &lt;span class="co"&gt;EM&lt;/span&gt;.next_tick(&amp;amp;method(&lt;span class="sy"&gt;:next&lt;/span&gt;))
      &lt;span class="r"&gt;end&lt;/span&gt;
    &lt;span class="r"&gt;end&lt;/span&gt;
  &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="r"&gt;end&lt;/span&gt;

&lt;span class="co"&gt;EM&lt;/span&gt;.run &lt;span class="r"&gt;do&lt;/span&gt;
  &lt;span class="co"&gt;Gauges&lt;/span&gt;::&lt;span class="co"&gt;Pusher&lt;/span&gt;.next
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;It uses em-hiredis to connect to Redis and run a blocking left pop, which will wait until there is an item on the list and then send the notification to Pusher with &lt;code&gt;trigger_async&lt;/code&gt;. It then re-schedules itself to run on the next tick, causing it to continuously loop.&lt;/p&gt;
&lt;p&gt;This has been working really well for us in production for a couple months now. The biggest disadvantage currently is if the process ever dies and notifications start to build up, Pusher will get inundated with requests form us because it will send them as fast as Redis can pop them off the list. We will eventually look into adding some rate-limiting.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/opensoul/~4/ZicFrSUM89w" height="1" width="1"/&gt;</content>
      <author>
        <name>Brandon Keepers</name>
      </author>
    <feedburner:origLink>http://opensoul.org/blog/archives/2011/08/30/pusher-notifications-with-eventmachine/</feedburner:origLink></entry>
  
</feed>

