<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="http://unfoldingneurons.com/"
	>

<channel>
	<title>Virtuous Code</title>
	<atom:link href="http://devblog.avdi.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://devblog.avdi.org</link>
	<description>&#34;The three virtues of a programmer: laziness, impatience, and hubris&#34; -- Larry Wall</description>
	<lastBuildDate>Wed, 16 May 2012 16:59:08 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>Creating Cowsays.com Part 2: Unit Tests and Cow Files</title>
		<link>http://devblog.avdi.org/2012/05/16/creating-cowsays-com-part-2-unit-tests-and-cow-files/</link>
		<comments>http://devblog.avdi.org/2012/05/16/creating-cowsays-com-part-2-unit-tests-and-cow-files/#comments</comments>
		<pubDate>Wed, 16 May 2012 13:00:42 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[Screencasts]]></category>
		<category><![CDATA[bdd]]></category>
		<category><![CDATA[heroku]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[screencasts]]></category>
		<category><![CDATA[sinatra]]></category>
		<category><![CDATA[tdd]]></category>

		<guid isPermaLink="false">http://devblog.avdi.org/?p=2312</guid>
		<description><![CDATA[Part two in my &#8220;live&#8221;-style screencast series is now available! Watch me code up a small web app from scratch using test-driven development. In this hour-long episode, I switch from integration testing to unit testing in order to drive out &#8230; <a href="http://devblog.avdi.org/2012/05/16/creating-cowsays-com-part-2-unit-tests-and-cow-files/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://devblog.avdi.org/2012/05/16/creating-cowsays-com-part-2-unit-tests-and-cow-files/creating-cowsays-3d-2/" rel="attachment wp-att-2318"><img class="alignright  wp-image-2318 product" title="creating-cowsays-3d" src="http://devblog.avdi.org/wp-content/uploads/2012/05/creating-cowsays-3d-150x150.png" alt="" width="150" height="150" /></a>Part two in my &#8220;live&#8221;-style screencast series is <a href="https://shiprise.dpdcart.com/cart/add?product_id=38354&amp;method_id=38512">now available</a>! Watch me code up a small web app from scratch using test-driven development.</p>
<p>In this hour-long episode, I switch from integration testing to unit testing in order to drive out some more features for the Sinatra-based web service I&#8217;ve been building. You&#8217;ll see me:</p>
<ul>
<li>Use the &#8220;hub&#8221; gem to interact with GitHub.</li>
<li>Talk about the decision point to drop from integration to unit testing.</li>
<li>Use <code>IO.popen</code> to pipe text through an external process.</li>
<li>Isolate tests for filesystem interaction using simple dependency injection.</li>
<li>Use <code>Enumerable#each_cons</code> and enumerators to identify a sub-sequence in an Array.</li>
<li>Use the <code>Pathname</code> standard library.</li>
<li>&#8230;and more!</li>
</ul>
<div>
<p>Here&#8217;s a five-minute preview:</p>
<p><script type='text/javascript' src='http://content.bitsontherun.com/players/Sq4RhUqK-uGtfOrbJ.js'></script></p>
<p>If that has piqued your interest, the whole episode is available for immediate purchase.</p>
</div>
<div class="cta "><p class="medium"><a href="https://shiprise.dpdcart.com/cart/add?product_id=38354&amp;method_id=38512">Buy Screencast for $5</a></p></div>
<p>Did you miss part 1? Get it now:</p>
<div class="cta "><p class="small"><a href="https://shiprise.dpdcart.com/cart/add?product_id=36134&amp;method_id=36189">Buy Part 1: Zero to Heroku for $5</a></p></div>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/05/16/creating-cowsays-com-part-2-unit-tests-and-cow-files/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Underscores are stupid</title>
		<link>http://devblog.avdi.org/2012/05/15/underscores-are-stupid/</link>
		<comments>http://devblog.avdi.org/2012/05/15/underscores-are-stupid/#comments</comments>
		<pubDate>Tue, 15 May 2012 13:00:58 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Rants]]></category>

		<guid isPermaLink="false">http://devblog.avdi.org/?p=2297</guid>
		<description><![CDATA[I hate underscores. They are ugly. They are like the neon orange belt pack of syntax. Dated and unfashionable no matter what era they are found in. As the former owner of a neon orange belt pack, I feel I &#8230; <a href="http://devblog.avdi.org/2012/05/15/underscores-are-stupid/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I hate underscores. They are ugly. They are like the neon orange belt pack of syntax. Dated and unfashionable no matter what era they are found in. As the former owner of a neon orange belt pack, I feel I can speak with authority on this. Aesthetically speaking, underscores are one step shy of &#8220;<code>C:\Progra~1</code>&#8220;.</p>
<p>Only nerds even know what an underscore is. What&#8217;s the first thing I have to teach people who are brand new to programming? What the hell an underscore is, and where to find it on the keyboard. No, not that line. The other line. Hold down shift. There you go.</p>
<p>Underscores require me to hit the Shift key. When writing prose, I only hit the shift once or twice per sentence.  In Ruby, I have to hit the shift key every few letters. Awkward. Inefficient.</p>
<p>I&#8217;ve been thinking about rebinding the dash key to underscore in Emacs ruby-mode. It&#8217;s stupid that I&#8217;m even considering such a thing.</p>
<p>Underscores are visually ambiguous. When the font is underlined for some reason, the underscores get muddled up with the underlining. In badly-rendered text boxes it&#8217;s not always clear where the underscore leaves off and the bottom border begins.</p>
<p>CamelCase is no better. I still have to hit Shift all the time. Plus it relies on a peculiarity of the Latin family of languages.</p>
<p>Lisp gets it right. Lisp S-expressions have one basic rule that vastly simplifies the syntax: whitespace separates tokens. Always.</p>
<p>This means that &#8220;<code>foo - bar</code>&#8221; parses out to the three tokens &#8220;foo&#8221;, &#8220;-&#8221;, &#8220;bar&#8221;. Whereas &#8220;<code>foo-bar</code>&#8221; is a single token that happens to have a dash in the middle. That&#8217;s right, your tokens can have dashes in the middle. Ordinary, easy-to-type, recognizable, visually unambiguous dashes. Lisp functions have names like:</p>
<pre>find-file</pre>
<p>All you have give up for this is tightly-packed arithmetic statements, like this:</p>
<pre>5*5-3</pre>
<p>Instead, you have to give your tokens some breathing room:</p>
<pre>5 * 5 - 3</pre>
<p>That&#8217;s a trade-off I&#8217;ll gladly make if it means using dashed identifiers instead of underscores.</p>
<p>I mostly code in Ruby these days, and I love almost every aspect of its very rich syntax. But I hate all the <code>snake_casing</code>. I want a Ruby that lets me use dashes in identifiers. If I never type another underscore, it will be too soon.</p>
<p>UPDATE: The Recursive blog points out that <a href="http://recursive-design.com/blog/2012/05/16/underscores-are-stupid-get-a-japanese-keyboard/">Japanese keyboards may be more comfortable for typing Ruby</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/05/15/underscores-are-stupid/feed/</wfw:commentRss>
		<slash:comments>61</slash:comments>
		</item>
		<item>
		<title>FigLeaf Gem Now Available</title>
		<link>http://devblog.avdi.org/2012/05/14/figleaf-gem-now-available/</link>
		<comments>http://devblog.avdi.org/2012/05/14/figleaf-gem-now-available/#comments</comments>
		<pubDate>Mon, 14 May 2012 04:00:00 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[gems]]></category>
		<category><![CDATA[oor]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://devblog.avdi.org/?p=2293</guid>
		<description><![CDATA[One of the points I try to make in Objects on Rails is that you don&#8217;t need to let other libraries or frameworks dictate the public face of your objects. Just because you use ActiveRecord, for instance, to persist the &#8230; <a href="http://devblog.avdi.org/2012/05/14/figleaf-gem-now-available/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>One of the points I try to make in <a href="http://objectsonrails.com">Objects on Rails</a> is that you don&#8217;t need to let other libraries or frameworks dictate the public face of your objects. Just because you use ActiveRecord, for instance, to persist the state of an object doesn&#8217;t mean you have to commit that object to honoring the effectively infinite ActiveRecord model API. You can use the facilities of ActiveRecord internally to that object, but still project a tidy, well-understood, easily-substituted interface to the rest of the system. </p>
<p> And of course you can do this entirely by convention, by simply being careful to interact only with the methods you&#8217;ve explicitly exposed. But it&#8217;s easy to forget these conventions in the heat of implementation, gradually coupling the rest of the program to more and more framework-provided methods until one day you discover that your object&#8217;s interface surface area has become quite large indeed. </p>
<p> As an exercise in mindfully limiting the interface exposed by objects and classes, in Objects on Rails I demonstrated a tool called &#8220;<code>FigLeaf</code>&#8220;. <code>FigLeaf</code> gives you something akin to the &#8220;private inheritance&#8221; found in some other OO languages. While it doesn&#8217;t actually hide the fact that a class is derived from a framework class, it enables you to selectively privatize some, most, or all methods derived from ancestor classes and modules. </p>
<p> Here&#8217;s an example from the readme </p>
<pre class="src src-ruby"><span class="org-keyword">class</span> <span class="org-type">Post</span> &lt; <span class="org-type">ActiveRecord</span>::<span class="org-type">Base</span>
  include <span class="org-type">FigLeaf</span>
  hide <span class="org-type">ActiveRecord</span>::<span class="org-type">Base</span>, <span class="org-constant">ancestors</span>: <span class="org-variable-name">true</span>,
       <span class="org-constant">except</span>: [<span class="org-type">Object</span>, <span class="org-constant">:init_with</span>, <span class="org-constant">:new_record?</span>,<span class="org-whitespace-trailing"> </span>
                <span class="org-constant">:errors</span>, <span class="org-constant">:valid?</span>, <span class="org-constant">:save</span>]
  hide_singletons <span class="org-type">ActiveRecord</span>::<span class="org-type">Calculations</span>,<span class="org-whitespace-trailing"> </span>
                  <span class="org-type">ActiveRecord</span>::<span class="org-type">FinderMethods</span>,
                  <span class="org-type">ActiveRecord</span>::<span class="org-type">Relation</span>
  <span class="org-comment-delimiter"># </span><span class="org-comment">...</span>
</pre>
<p> And here&#8217;s what happens when we try to send class or instance-level messages which have been hidden by <code>FigLeaf</code> </p>
<pre class="example">ruby-1.9.2-p0 &gt; Post.find(1)
NoMethodError: private method `find' called for #&lt;Class:0xa1a4a50&gt;

ruby-1.9.2-p0 &gt; Post.new.destroy
NoMethodError: Attempt to call private method
</pre>
<p> Today I&#8217;m happy to announce that thanks to the dilligent efforts of <a href="http://www.codeodor.com/">Sammy Larbi</a>, <code>FigLeaf</code> is <a href="http://rubygems.org/gems/fig_leaf">now available as a RubyGem</a>. You can install it with: </p>
<pre class="example">gem install fig_leaf
</pre>
<p> The project is also <a href="https://github.com/objects-on-rails/fig-leaf">available on GitHub</a>. </p>
<p> It&#8217;s important to note that <code>FigLeaf</code> is not intended as a hammer to keep your coworkers or your library clients in line. It&#8217;s not as if that would work, anyway; the strictures that it adds are easy enough to circumvent. Instead, it&#8217;s intended role is more along the lines of the &#8220;rumble strips&#8221; along highways which give you a jolt when you veer off into the shoulder. It provides a sharp reminder when you&#8217;ve unthinkingly introduced a new bit of coupling to an interface you are trying to keep isolated from the rest of the codebase. Then, you can consciously make the decision whether to make that method public, or find a different way of going about what you were doing. </p>
<p> Got questions or suggestions about <code>FigLeaf</code> or about OO practices in general? Please join us on the <a href="https://groups.google.com/forum/?fromgroups#!forum/objects-on-rails">Objects on Rails discussion list</a>! </p>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/05/14/figleaf-gem-now-available/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>A Ruby Conversion Idiom</title>
		<link>http://devblog.avdi.org/2012/05/07/a-ruby-conversion-idiom/</link>
		<comments>http://devblog.avdi.org/2012/05/07/a-ruby-conversion-idiom/#comments</comments>
		<pubDate>Mon, 07 May 2012 13:00:00 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[coercion]]></category>
		<category><![CDATA[conversion]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://devblog.avdi.org/?p=2280</guid>
		<description><![CDATA[@CapnKernul writes: [do you know of] a Ruby idiom for converting an object to a type if it isn&#8217;t already that type. For example, if you want to only store an attribute of type Foo, you could write an accessor &#8230; <a href="http://devblog.avdi.org/2012/05/07/a-ruby-conversion-idiom/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="https://twitter.com/#!/CapnKernul">@CapnKernul</a> writes:</p>
<blockquote><p>[do you know of] a Ruby idiom for converting an object to a type if it isn&#8217;t already that type. For example, if you want to only store an attribute of type Foo, you could write an accessor method that would pass any non-Foo object to Foo&#8217;s constructor.</p></blockquote>
<p>There is a de facto idiom among Ruby built-ins to define a method with the same name as a class for doing conversions to that class. My favorite is <code>Kernel#Array</code>:</p>
<pre class="src src-ruby"><span class="org-type">Array</span>(<span class="org-string">"foo"</span>)                    <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; ["foo"]</span>
<span class="org-type">Array</span>([1,2,3])                  <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; [1, 2, 3]</span>
<span class="org-type">Array</span>(<span class="org-variable-name">nil</span>)                      <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; []</span>
<span class="org-type">Array</span>({<span class="org-constant">:a</span> =&gt; 1, <span class="org-constant">:b</span> =&gt; 2})       <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; [[:a, 1], [:b, 2]]</span></pre>
<p>But there are others. <code>Kernel#Integer</code> provides a stricter form of integer conversion than does <code>#to_i</code>:</p>
<pre class="src src-ruby"><span class="org-string">"42"</span>.to_i                       <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; 42</span>
<span class="org-type">Integer</span>(<span class="org-string">"42"</span>)                   <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; 42</span>
<span class="org-string">"30 seconds"</span>.to_i               <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; 30</span>
<span class="org-type">Integer</span>(<span class="org-string">"30 seconds"</span>)           <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt;</span>
<span class="org-comment-delimiter"># </span><span class="org-comment">~&gt; -:4:in `Integer': invalid value for Integer(): "30 seconds" (ArgumentError)</span>
<span class="org-comment-delimiter"># </span><span class="org-comment">~&gt; from -:4:in `&lt;main&gt;'</span></pre>
<p>And there are also <code>Kernel#String</code>, <code>Kernel#Float</code>, <code>Kernel#Complex</code>, and <code>Kernel#Rational</code>:</p>
<pre class="src src-ruby"><span class="org-type">String</span>(123)                     <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; "123"</span>
<span class="org-type">Float</span>(<span class="org-string">"123"</span>)                    <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; 123.0</span>
<span class="org-type">Rational</span>(4,5)                   <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; (4/5)</span>
<span class="org-type">Complex</span>(1,3)                    <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; (1+3i)</span></pre>
<p>You can also find this in standard libraries. The URI library defines an idempotent conversion method for URIs:</p>
<pre class="src src-ruby">require <span class="org-string">'uri'</span>

u = <span class="org-type">URI</span>(<span class="org-string">"http://example.com"</span>)   <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; #&lt;URI::HTTP:0x000000030cac80 URL:http://example.com&gt;</span>
<span class="org-type">URI</span>(u)                          <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; #&lt;URI::HTTP:0x000000030cac80 URL:http://example.com&gt;</span></pre>
<p>There&#8217;s also <code>Pathname</code>:</p>
<pre class="src src-ruby">require <span class="org-string">'pathname'</span>

p = <span class="org-type">Pathname</span>(<span class="org-string">"~/.emacs.d"</span>)      <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; #&lt;Pathname:~/.emacs.d&gt;</span>
<span class="org-type">Pathname</span>(p)                     <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; #&lt;Pathname:~/.emacs.d&gt;</span></pre>
<p>I like to extend this idiom into my own code for idempotent conversions into commonly-used value objects. <a href="http://objectsonrails.com/#sec-17-1">I did this in Objects on Rails</a> for TagList objects.</p>
<pre class="src src-ruby"><span class="org-keyword">module</span> <span class="org-type">Conversions</span>
  <span class="org-keyword">private</span>
  <span class="org-keyword">def</span> <span class="org-function-name">TagList</span>(value)
    <span class="org-keyword">return</span> value <span class="org-keyword">if</span> value.is_a?(<span class="org-type">TagList</span>)
    <span class="org-type">TagList</span>.new(value)
  <span class="org-keyword">end</span>
<span class="org-keyword">end</span></pre>
<p>The <code>TagList</code> method is shorter than calling <code>TagList.new(...)</code>. And in addition, it&#8217;s safe to call on inputs which might or might not already be <code>TagList</code> objects.</p>
<p>Although they may look a little odd at first, capitalized conversion methods are a well-established Ruby idiom for methods which &#8220;do the right thing&#8221; to convery any reasonable input value into a desired class. And because unlike <code>#to_x</code> methods they are outside of the objects being converted, they incur none of the maintenance danger of a monkey-patch. I don&#8217;t define them for every class in my program; but for oft-used value object types they can be quite convenient.</p>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/05/07/a-ruby-conversion-idiom/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>HTTP forwarding services for local Facebook development</title>
		<link>http://devblog.avdi.org/2012/04/27/http-forwarding-services-for-local-facebook-development/</link>
		<comments>http://devblog.avdi.org/2012/04/27/http-forwarding-services-for-local-facebook-development/#comments</comments>
		<pubDate>Fri, 27 Apr 2012 16:07:49 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Links]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[forwarding]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[proxy]]></category>
		<category><![CDATA[ssh]]></category>
		<category><![CDATA[ssl]]></category>
		<category><![CDATA[tunnel]]></category>
		<category><![CDATA[tunneling]]></category>

		<guid isPermaLink="false">http://devblog.avdi.org/?p=2273</guid>
		<description><![CDATA[I&#8217;m working on Facebook Open Graph features for a client, and developing these features requires enabling Facebook to somehow crawl my locally-served pages. I asked around for solutions to this problem, and got a pretty long list of replies. I&#8217;m &#8230; <a href="http://devblog.avdi.org/2012/04/27/http-forwarding-services-for-local-facebook-development/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m working on Facebook Open Graph features for a client, and developing these features requires enabling Facebook to somehow crawl my locally-served pages. I asked around for solutions to this problem, and got a pretty long list of replies. I&#8217;m listing them here for future reference.</p>
<p>All of these offer a pretty similar service: you download a command-line tool (often in the form of a Ruby gem), start up your server, run the command, and hey presto, your local pages served on localhost:3000 are now being served at http://yoursubdomain.example.com. Most, if not all, use SSH tunneling as the underlying technology.</p>
<ul>
<li><a href="https://showoff.io/">Showoff.io</a>: 5m free, $1 for a day pass, otherwise $5/mo unlimited.</li>
<li><a href="http://pagekite.net/">Pagekite</a>: Features transparent SSL support and unlimited subdomains. $5/mo for a basic plan. EDIT: Looks like they also provide a <a href="https://pagekite.net/wiki/Howto/GNULinux/DebianPackage/">Debian/Ubuntu package source</a>, nice touch! EDIT2: PageKite informs me that their service is as low as $3/mo for an individual, and that they do not use SSH as their underlying tunnel in order to be friendlier to Windows users.</li>
<li><a href="http://progrium.com/localtunnel/">Localtunnel</a>: Free, sponsored by Twilio. Looks similar to Showoff.io.</li>
<li><a href="http://tunnlr.com/">Tunnlr</a>: Explicitly marketed to FB developers. $5/mo for a single subdomain (notice a trend?)</li>
<li><a href="http://proxylocal.com/">ProxyLocal</a>: Free, appears very similar to Showoff.io, LocalTunnel, etc.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/04/27/http-forwarding-services-for-local-facebook-development/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Watch me write an app, starting from nothing. Part 1 now available!</title>
		<link>http://devblog.avdi.org/2012/04/16/new-screencast-available-creating-cowsays-com-part-1-zero-to-heroku/</link>
		<comments>http://devblog.avdi.org/2012/04/16/new-screencast-available-creating-cowsays-com-part-1-zero-to-heroku/#comments</comments>
		<pubDate>Mon, 16 Apr 2012 13:00:25 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[Screencasts]]></category>
		<category><![CDATA[Videos]]></category>
		<category><![CDATA[heroku]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[screencasts]]></category>
		<category><![CDATA[sinatra]]></category>

		<guid isPermaLink="false">http://devblog.avdi.org/?p=2241</guid>
		<description><![CDATA[A couple weeks ago I created cowsays.com, in an effort to introduce the joy of Cowsay to more people. In the announcement I talked about how I created the site over a 24-hour period (not counting some UI tweaks). What &#8230; <a href="http://devblog.avdi.org/2012/04/16/new-screencast-available-creating-cowsays-com-part-1-zero-to-heroku/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>A couple weeks ago I created <a href="http://www.cowsays.com">cowsays.com</a>, in an effort to introduce the joy of <a href="http://www.nog.net/~tony/warez/cowsay.shtml">Cowsay</a> to more people. In <a href="http://devblog.avdi.org/2012/03/27/the-cow-says/">the announcement</a> I talked about how I created the site over a 24-hour period (not counting some UI tweaks). What I <em>didn&#8217;t </em>mention was that <strong>I also recorded the entire process</strong>.</p>
<p>Since then I&#8217;ve been working on editing the video in my spare time, and I&#8217;m happy to announce that today part 1, (&#8220;Zero to Heroku&#8221;), is ready and available.</p>
<p>This video series is a raw, over-the-shoulder look at my solo development process. I narrate what I&#8217;m doing and thinking as I work, and occasionally I digress to explain why I&#8217;m making a particular decision. I&#8217;ve tried to edit out the really long pauses. But you&#8217;ll still hear a lot of &#8220;hm, what next?&#8221;.</p>
<p><a href="http://devblog.avdi.org/2012/04/16/new-screencast-available-creating-cowsays-com-part-1-zero-to-heroku/creating-cowsays-3d/" rel="attachment wp-att-2249"><img class="alignright  wp-image-2249 product" title="creating-cowsays-3d" src="http://devblog.avdi.org/wp-content/uploads/2012/04/creating-cowsays-3d-300x300.png" alt="" width="180" height="180" /></a>In this first <strong>hour-long video</strong> I take the app from an empty directory up through a successful &#8220;tracer bullet&#8221; deployed to Heroku. Along the way, you&#8217;ll watch me:</p>
<ul>
<li>Start with nothing but a failing integration test.</li>
<li>Use IO.popen to call out to Perl from Ruby.</li>
<li>Set up <a href="https://github.com/guard/guard">Guard</a> to automate my tests and Bundler updates.</li>
<li>Use <a href="https://github.com/defunkt/hub">Hub</a> and <a href="http://zagadka.vm.bytemark.co.uk/magit/magit.html">Magit</a> to simplify working with Git and GitHub.</li>
<li>TDD a simple Sinatra service using RSpec and rack-test.</li>
<li>Create a Heroku app.</li>
<li>Create a Rackup file and deploy to Heroku.</li>
</ul>
<p>Sound interesting? It&#8217;s yours for $5.</p>
<div class="buy"><span class="price">$5.00</span><a class="dpdcart iframe" href="https://shiprise.dpdcart.com/cart/add?product_id=36134" target="_top"><img src="https://getdpd.com/images/buy_buttons/atc/icon1/dpd_atc_fff_basket.png" alt="Add to Cart" border="0" /></a></div>
<p>Over the coming weeks I&#8217;ll be releasing the rest of the series. I have all the video, I just need to edit it. In upcoming episodes you can expect to see:</p>
<ul>
<li>Refactoring the Sinatra app from a &#8220;classic&#8221; app to a &#8220;modular&#8221; style app.</li>
<li>Switching to Unit Tests as method logic increases in complexity.</li>
<li>Adding more cowsay features.</li>
<li>Smoothly migrating from a Sinatra app to a Rails app that mounts the Sinatra app as a service.</li>
<li>Using <a href="http://datamapper.org">Datamapper</a> for persistence.</li>
<li>Using <a href="http://erector.rubyforge.org/">Erector</a> for object-oriented views.</li>
<li>Using Coffeescript and jQuery to liven up the UI.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/04/16/new-screencast-available-creating-cowsays-com-part-1-zero-to-heroku/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>New maintainer needed for NullDB</title>
		<link>http://devblog.avdi.org/2012/04/13/new-maintainer-needed-for-nulldb/</link>
		<comments>http://devblog.avdi.org/2012/04/13/new-maintainer-needed-for-nulldb/#comments</comments>
		<pubDate>Fri, 13 Apr 2012 17:45:18 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[activerecord]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[nulldb]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://devblog.avdi.org/?p=2237</guid>
		<description><![CDATA[NullDB, for those who don&#8217;t know, is a null backend for ActiveRecord. Unlike RSpec&#8217;s stub_object, rather than raise an exception on DB access, will NullDB DB interactions simply become no-ops. This is handy for things like testing after_save hooks in &#8230; <a href="http://devblog.avdi.org/2012/04/13/new-maintainer-needed-for-nulldb/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="https://github.com/nulldb/nulldb">NullDB</a>, for those who don&#8217;t know, is a null backend for ActiveRecord. Unlike RSpec&#8217;s stub_object, rather than raise an exception on DB access, will NullDB DB interactions simply become no-ops. This is handy for things like testing after_save hooks in isolation.</p>
<p>Myron Marston has done a stellar job shepherding it through the Rails 3 transition, but he&#8217;s busy with <a href="https://github.com/myronmarston/vcr">other awesome projects</a> now and has asked me to look for a new maintainer. So: If you&#8217;re interested in helping keep NullDB up to date with the latest changes to ActiveRecord, please get in touch!</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/04/13/new-maintainer-needed-for-nulldb/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Assistants: Not just for the boss anymore.</title>
		<link>http://devblog.avdi.org/2012/04/03/assistants/</link>
		<comments>http://devblog.avdi.org/2012/04/03/assistants/#comments</comments>
		<pubDate>Tue, 03 Apr 2012 13:00:00 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Business]]></category>

		<guid isPermaLink="false">http://devblog.avdi.org/?p=2225</guid>
		<description><![CDATA[This is not strictly software-development-related, but a number of developers have asked me about my experience finding and working with a remote assistant. I&#8217;ve had my assistant for a few months now and I thought I&#8217;d jot down some notes &#8230; <a href="http://devblog.avdi.org/2012/04/03/assistants/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>This is not strictly software-development-related, but a number of developers have asked me about my experience finding and working with a remote assistant. I&#8217;ve had my assistant for a few months now and I thought I&#8217;d jot down some notes on the experience. </p>
<div id="outline-container-1" class="outline-4">
<h4 id="sec-1">The V-word</h4>
<div class="outline-text-4" id="text-1">
<p> First, let&#8217;s get one thing out of the way. I <em>despise</em> the term &#8220;virtual assistant&#8221;, along with &#8220;virtual team&#8221; and &#8220;telecommuting&#8221;. I&#8217;ve been working from home for years and there&#8217;s nothing &#8220;virtual&#8221; about the work I do. The same goes for the work I delegate to my assistant. She may be &#8220;remote&#8221;, but the work is very real. This may seem like semantic quibbling, but if there&#8217;s one thing I find most developers can agree on it&#8217;s that using the right names for things is important. </p>
</p></div>
</p></div>
<div id="outline-container-2" class="outline-4">
<h4 id="sec-2">Assistant&hellip; isn&#8217;t that something executives have?</h4>
<div class="outline-text-4" id="text-2">
<p> This was my problem for the longest time when I would consider the idea of getting an assistant. When your time is largely filled with communication&mdash;email, meetings, calls, travel&mdash;it&#8217;s easy to see where an assistant would fit in. But for someone whose work consists primarily of <em>making</em> things, where would an assistant be able to help? I knew I needed to do <em>something</em> to lighten the load, but it wasn&#8217;t at all clear if an assistant would help. </p>
<p> What finally convinced me to start looking for an assistant was the problem of appointment scheduling. I&#8217;ve found myself booking more and more appointments&mdash;consultations, team meetings, remote pair-programming sessions, interviewing people for Wide Teams, or, occasionally, being interviewed for other podcasts&mdash;and the constant back-and-forth email tag to set the meetings up was stressing me out. I tried <em>all</em> the automated solutions. <a href="http://www.tungle.me/Home/">Tungle</a>, <a href="http://support.google.com/calendar/bin/answer.py?hl=en&amp;answer=190998">Google Appointment slots</a>, <a href="http://www.bookingbug.com/">BookingBug</a>, <a href="http://youcanbook.me/">YouCanBookMe</a>, you name it. They all sucked in their own special ways. None of them could learn my own special preferences: for instance, while a day might have six open &#8220;blocks&#8221; in it for an Open-Source pair-programming session, if someone books <em>one</em> of those blocks, I don&#8217;t want any more blocks booked that day. </p>
<p> I realized that the bottom line was that there&#8217;s just no substitute for a human being who can learn my habits and trade emails with people. </p>
<p> So initially I set out to get someone to handle my calendar; with the hope that once I <em>had</em> an assistant, I&#8217;d think up more stuff I could delegate to them. Which is exactly how it turned out. </p>
</p></div>
</p></div>
<div id="outline-container-3" class="outline-4">
<h4 id="sec-3">Finding an assistant</h4>
<div class="outline-text-4" id="text-3">
<p> There are a number of V***ual Assistant agencies around these days. I opted to skip them and look for someone on <a href="https://www.odesk.com/">ODesk</a>. I did this for a few reasons: </p>
<ol>
<li>I had had a good experience hiring people for small jobs on ODesk in the past. </li>
<li>I&#8217;m a cheapskate, and I wanted to cut out the middleman. </li>
<li>Ideally, I wanted to establish a relationship with a real human    being, not with an agency. </li>
</ol>
<p> So I put an ad up on ODesk explaining what I was looking for an assistant. Predictably, I got a dozens of responses. I began to think I needed to hire an assistant just to go through all the assistant applicants. </p>
<p> Most of the applicants were in the Philippines, India, and South America, with a smattering of Americans and Europeans. Then one day I checked ODesk and was startled to see an applicant from just a few miles up the road in York, PA. Intrigued, I replied, and that&#8217;s how I met my assistant Mandy. </p>
<p> As it turned out, it was not purely the result of wild chance. ODesk is syndicated by various job sites, some of which localize their listings based on the location of the job poster. Mandy had seen one of these listings, and created an ODesk account to respond. </p>
<p> Philosophically I am sort of a combination localist/globalist: I have no problem with sending my dollars overseas to workers who need to feed their families every bit as much as Americans do. To my mind there isn&#8217;t a great deal of difference between hiring someone in Detroit and hiring someone in Korea. But I do believe in participating in the <em>really</em> local economy as much as I can. That is, local as in we-could-meet-up-for-coffee local. So I was excited to see a nearby applicant. </p>
<p> I conducted a short interview over video chat. Once we established that both of us were real people and not Internet scammers, I decided that the best way to figure out if she was a good fit was just to start throwing some tasks at her for a week or two and see how she did. With ODesk you there&#8217;s no commitment and you only pay for the verified hours worked (and you can set a weekly cap), so this wasn&#8217;t a huge risk. </p>
<p> Mandy has been helping me out on a part-time basis ever since, so obviously it went well. </p>
</p></div>
</p></div>
<div id="outline-container-4" class="outline-4">
<h4 id="sec-4">What my assistant does</h4>
<div class="outline-text-4" id="text-4">
<p>     Here&#8217;s a sampling of the work I delegate to Mandy. My biggest     roadblock to hiring an assistant was figuring out what I would be     able to delegate to them. If you&#8217;re pondering the same question, I     hope this list will help you get a better idea of the sort of     tasks you might be able to turn over to an assistant. </p>
<ul>
<li>Scheduling. She has write access to my calendar. If someone wants       to make an appointment&mdash;e.g. to schedule an Open-Source pairing       session&mdash;I forward it on to her and she makes the arrangements       and makes sure they are all set up with Skype etc. before the       meeting. She can see my calendar and knows my daily routine, so       she&#8217;s able to make appointments with zero &#8220;is this date/time       good for you?&#8221; back-and-forth. She also sends out reminders and       confirms that the other parties are still able to make it to the       appointment. </li>
<li>Every Monday she briefs me for about fifteen minutes over Google       Talk, reminding me of any appointments or other commitments I       have for the week. Yes, in theory, I could see this stuff on my       calendar; but I&#8217;m notorious for forgetting to check it. </li>
<li>Customer service: if any one has a problem with a book order I       pass it along to her. She has access to my storefront account       and is able to resolve most problems without any further       intervention on my part. </li>
<li>Blog maintenance: if an old post of mind starts attracting a lot       of spam, I send it to her and she cleans it up and shuts down       comments.  </li>
<li>Lots of similar blog maintenance tasks: for instance:       today I had her pull down all of my old Blip.tv-hosted       screencasts, put them in Dropbox, re-upload them to my new video       host, and edit all the relevant blog posts to embed the new       video widget. </li>
<li>I had collected half a room full of mouldering computer hardware       over the years, and it was keeping me from rearranging my       office. She researched local computer recycling centers, and       arranged to have a local acquaintance of hers come and haul the       hardware away for a small fee. Stuff like this is one of the       reasons I like having someone local, with local contacts. </li>
<li>I wanted to know if it was cheaper to have a local tire shop buy       a set of tires for me, or to order them online and have them       shipped. I had her do the online research and call the tire shop       to get a quote, saving me the time. </li>
<li>She did a bunch of research for me for my (sadly canceled) trip       to Poland. She looked into international phone options       (including determining if my phone was international-capable),       researched credit card foreign transaction fees, determined that       I didn&#8217;t need a visa to travel, etc. </li>
<li>Billing: She sets up and sends invoices. </li>
<li>Record keeping: Some clients don&#8217;t go through my usual invoicing       channels, for one reason or another (e.g. Code Benders does       direct deposit). Mandy keeps <a href="http://freshbooks.com">Freshbooks</a> up to date with these       nonstandard payments. </li>
<li>When I prepare a new version of <a href="http://objectsonrails.com">Objects on Rails</a>, I send it to       her and she types up the changelog, sends out the update, and       notifies the mailing list. </li>
<li>When people send me errata on the Objects on Rails <a href="https://groups.google.com/d/forum/objects-on-rails">mailing list</a>       she creates a ticket so I don&#8217;t lose track of the issue. </li>
<li>She tracks the names of everyone who contributes suggestions,       errata, and other comments to Objects on Rails. Then she uses       Github&#8217;s online file editing to update the acknowledgments       section, which then gets rolled into the next release. </li>
<li>I&#8217;m bringing her up to speed on audio editing, with the aim of       turning over the podcast post-production duties for       WideTeams.com to her. </li>
<li>She helped me prepare this list! </li>
</ul></div>
</p></div>
<div id="outline-container-5" class="outline-4">
<h4 id="sec-5">Tools</h4>
<div class="outline-text-4" id="text-5">
<p>     Here are some of the tools that have helped me work with an     assistant. </p>
<ul>
<li>Google Talk: For voice, video, and text chat. </li>
<li><a href="http://www.screenr.com/">ScreenR</a> has been instrumental in enabling me to quickly       demonstrate how to perform various tasks. </li>
<li>Google Docs for sharing notes, instructions, results of       research, etc. </li>
<li><a href="https://lastpass.com/enterprise_overview.php">LastPass Enterprise</a> for securely sharing credentials to       assorted online services, such as my storefront provider. </li>
<li><a href="https://www.dropbox.com/">Dropbox</a> for filesharing. </li>
<li>I&#8217;ve started using <a href="https://astrid.com/">Astrid</a> for task tracking. The Android and Web       UIs are both quite nice, and I can just include Astrid in the TO       line of an email in order to create the task and automatically       assign it to the primary recipient. </li>
</ul></div>
</p></div>
<div id="outline-container-6" class="outline-4">
<h4 id="sec-6">Do you need an assistant?</h4>
<div class="outline-text-4" id="text-6">
<p>     Some people are going to be able to make better use of an     assistant than others. If you have a simple write-code-all-day     routine, you probably wouldn&#8217;t get much benefit. But if your life     is more complex, with a lot of different people asking for your     time, and an increasing number of administrative headaches that fall     outside the core of your craft, you might find that an assistant     frees up enough of your time to be worth the investment. </p>
</p></div>
</p></div>
<div id="outline-container-7" class="outline-4">
<h4 id="sec-7">Hire my assistant!</h4>
<div class="outline-text-4" id="text-7">
<p>     If after reading this far you&#8217;ve realized that you could benefit     from the services of a remote assistant, I have some good news: my     assistant, Mandy Moore, has authorized me to announce that she     currently has availability to take on more clients. You can <a href="https://www.odesk.com/users/~~222cb23dad53db0d?sid=28001">find her ODesk profile here</a>. She&#8217;s relatively new to the remote     assistant trade, but she learns fast, gets things done, and has     been a real pleasure to work with. I wholeheartedly recommend her. </p>
</p></div>
</p></div>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/04/03/assistants/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Generating cows with IO.popen()</title>
		<link>http://devblog.avdi.org/2012/03/29/generating-cows-with-io-popen/</link>
		<comments>http://devblog.avdi.org/2012/03/29/generating-cows-with-io-popen/#comments</comments>
		<pubDate>Thu, 29 Mar 2012 04:00:00 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[processes]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://devblog.avdi.org/?p=2202</guid>
		<description><![CDATA[I find the subject of starting and interacting with other OS processes fascinating. A few years ago I wrote a never-completed series on the many ways to spawn off processes in Ruby: Part 1: Backticks and system() Part 2: Opening &#8230; <a href="http://devblog.avdi.org/2012/03/29/generating-cows-with-io-popen/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I find the subject of starting and interacting with other OS processes fascinating. A few years ago I wrote a never-completed series on the many ways to spawn off processes in Ruby: </p>
<ul>
<li><a href="http://devver.wordpress.com/2009/06/30/a-dozen-or-so-ways-to-start-sub-processes-in-ruby-part-1/">Part 1</a>: Backticks and <code>system()</code> </li>
<li><a href="http://devver.wordpress.com/2009/07/13/a-dozen-or-so-ways-to-start-sub-processes-in-ruby-part-2/">Part 2</a>: Opening pipes to processes </li>
<li><a href="http://devver.wordpress.com/2009/10/12/ruby-subprocesses-part_3/">Part 3</a>: Open3, PTY, and Shell standard libraries </li>
</ul>
<p> However, those were all written for Ruby 1.8, and are a little out of date. </p>
<p> Ruby 1.9 substantially expanded and revamped the Ruby process API. Many of the tasks which once required third-party gems like <a href="https://github.com/ahoward/open4">Open4</a> can now be easily accomplished using the built-in calls. Particularly notable is that <a href="http://ruby-doc.org/core-1.9.3/Process.html#method-c-spawn"><code>Process.spawn</code></a> was added with a comprehensive set of options for customizing how the process is started, and the other standard process-starting calls were updated to accept the same set of arguments as <code>Process.spawn</code>. </p>
<p> Here&#8217;s how <a href="http://www.cowsays.com">www.cowsays.com</a> starts a Perl process in order to generate ASCII-art cows: </p>
<pre class="src src-ruby">cowsay_path = <span class="org-type">Pathname</span>(__FILE__).dirname + <span class="org-string">"../bin/cowsay"</span>
perl_path   = <span class="org-string">"/usr/bin/perl"</span>
cows_path   = <span class="org-type">Pathname</span>(__FILE__).dirname + <span class="org-string">"cowsay/cows"</span>
env         = {
  <span class="org-string">'COWPATH'</span> =&gt; cows_path.to_s
}
args        = %<span class="org-type">W</span>[-f <span class="org-variable-name">#{@cowfile}</span>]
<span class="org-variable-name">@io</span>.popen([env, perl_path, cowsay_path.to_s, *args], <span class="org-string">'r+'</span>) <span class="org-keyword">do</span>
  |process|
  process.write(message)
  process.close_write
  process.read
<span class="org-keyword">end</span>
</pre>
<p> Notes: </p>
<ul>
<li>Since I know where Perl is found on Heroku boxes, I hardcode the   path to it. Unless you have a specific reason for allowing program   paths to be overridden, it&#8217;s always safer to hardcode the path to   executables. That&#8217;s one less vector for attacks. </li>
<li><code>@io</code> simply points to the <code>IO</code> class in production. It&#8217;s an   instance variable so I can write isolated unit tests verifying how   my code interacts with the system. </li>
<li><a href="http://www.ruby-doc.org/core-1.9.3/IO.html#method-c-popen"><code>IO.popen</code></a> starts a process and creates a pipe to that process,   which your program can use to send data into that process&#8217; STDIN,   and read data from its STDOUT. </li>
<li>I pass an array instead of a string to <code>IO.popen</code>. If you pass a   string, Ruby will start up a shell process and pass the string to   the shell for interpretation. This is handy for commands like <code>ls -l   *.rb</code> where you want standard shell-expansion to be performed. It&#8217;s   also very dangerous when dealing with user-supplied data; it opens   up a door for shell-injection attacks similar to <a href="http://xkcd.com/327/">SQL injection</a> exploits. Only shell-injection is even more dangerous,   since a shell injection can potentially execute any command that   your server process has the rights to execute. </li>
<li>The array version, by contrast, skips the shell. The arguments are   passed directly to the process as strings in it the ARGV   variable. This version is <em>always</em> my preference unless I have a   specific reason to want to do shell expansion. This is also why I   avoid backticks (<code>`some-command ...`</code>) or <code>%x()</code> except in little   personal-use scripts. </li>
<li>I make use of another feature of <code>IO.popen</code>, <code>Process.spawn</code> and   friends: I customize the environment variables the process will see   by passing a hash as the first argument. I want more Ruby   programmers to know about this feature; I sometimes see code which   accomplishes this effect by setting <code>ENV['somevar']</code> before spawning   a process and then re-setting it afterwards. Passing a hash is much   cleaner. </li>
<li>Note that by default, only the hash keys specified in the   environment hash will be changed for the subprocess; the rest will   be copied from the current environment. There&#8217;s an option, however,   to use the hash as a complete replacement for the current   environment. See the <code>IO.spawn</code> documentation for details. </li>
<li>I pass <code>r+</code> as the &#8220;file mode&#8221; argument. This tells Ruby I want to   read from <em>and</em> write to the child process. The default is   read-only. </li>
<li>Note the line where I call <code>process.close_write</code>. This is very   important, and something which bites a lot of first-time users of   <code>popen</code> (it sure used to bite me!). With many programs, if you don&#8217;t   explicitly close the pipe for writing after finishing your write,   you&#8217;ll wait forever trying to read the program&#8217;s output. This is   because the program being executed as a subprocess was written to   read its input up to EOF before outputting and exiting. And it won&#8217;t   get that EOF until you close the pipe for writing. In addition, the   operating system may buffer the data flowing from your program to   the subprocess, and calling <code>.close_write</code> forces that buffer to be   flushed. </li>
</ul>
<p> I hope you&#8217;ve learned something new about starting processes in Ruby from this post. If you have any process tips of your own, or any burning questions about spawning processes, feel free to bring them up in the comments! </p>
<p> By the way, if you&#8217;re interested in finding out more about working with UNIX processes in Ruby, the <a href="http://rubyrogues.com/">Ruby Rogues</a> book club is currently reading Jesse Storimer&#8217;s <a href="http://workingwithunixprocesses.com/">book on the subject</a>. </p>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/03/29/generating-cows-with-io-popen/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>The cow says&#8230;</title>
		<link>http://devblog.avdi.org/2012/03/27/the-cow-says/</link>
		<comments>http://devblog.avdi.org/2012/03/27/the-cow-says/#comments</comments>
		<pubDate>Tue, 27 Mar 2012 04:00:00 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Announcements]]></category>

		<guid isPermaLink="false">http://devblog.avdi.org/?p=2197</guid>
		<description><![CDATA[Introducing cowsays.com <a href="http://devblog.avdi.org/2012/03/27/the-cow-says/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;ve seen my <a href="http://avdi.org/talks/confident-code-rubymidwest-2011/">Confident Code talk</a> you know I&#8217;m a fan of the <a href="http://www.nog.net/~tony/warez/cowsay.shtml">cowsay</a> utility. </p>
<pre class="src src-sh">cowsay <span class="org-string">"Quack"</span>
</pre>
<pre class="example">
 _______
&lt; Quack &gt;
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
</pre>
<p> In fact, I like it so much I wanted to share it with people who might not have had a chance to try it yet. So <a href="http://www.cowsays.com/">I put it on the web</a>. </p>
<p> Enjoy it for all of your ASCII-animal speech-bubble needs. The <a href="https://github.com/avdi/cow">source code is on GitHub</a> if you&#8217;re curious. Interesting implementation notes: </p>
<ul>
<li>It was written from scratch starting last Saturday night and then   over part of last Sunday. </li>
<li>The original Cowsay program, written in Perl, is included in the   app. I use <code>IO.popen</code> to open a Perl process and generate the ASCII   art. </li>
<li>I started with a simple Sinatra service. Then I added Rails in, and   mounted the Sinatra service in the routes file. </li>
<li>I use <a href="http://erector.rubyforge.org/">Erector</a> for views, and <a href="http://datamapper.org">DataMapper</a> for persistence (what little   there is). </li>
</ul>
<div id="outline-container-1" class="outline-4">
<h4 id="sec-1">On a personal note&hellip;</h4>
<div class="outline-text-4" id="text-1">
<p> It&#8217;s a silly little app, but it&#8217;s kind of a big milestone for me personally. All my programming career I&#8217;ve worked on other people&#8217;s projects. I&#8217;ve made regular attempts to kick off projects of my own in my spare time, but invariably I&#8217;d get sucked back into day-job/client work, lose momentum, and stop in the middle. My home directory is littered with half-completed project repos. </p>
<p> As sad as it may sound, I think this marks the first time I&#8217;ve been able to find a small enough project scope that I could actually launch a working V1. I&#8217;d proven to myself that I could write libraries and books, but until now I still hadn&#8217;t proven I could launch an app of my own. </p>
<p> So, yay for me! </p>
</p></div>
</p></div>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/03/27/the-cow-says/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 0.685 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2012-05-16 13:00:55 -->
<!-- Compression = gzip -->
