<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>subWindow Blog</title>
    <link>http://subwindow.com/articles</link>
    <pubDate>Mon, 17 Nov 2008 20:46:33 GMT</pubDate>
    <description>Just another crappy programmer's blog</description>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/subwindow" type="application/rss+xml" /><item>
      <title>Ruby Module Reference</title>
      <link>http://subwindow.com/articles/25</link>
      <description>&lt;p&gt;In Ruby, I always get confused as to the difference between include and extend with modules, and the effect of class &lt;&lt; self inside those.  So here's a reference script as to what gets included where:&lt;/p&gt;

&lt;pre class="blackboard"&gt;&lt;span class="Keyword"&gt;module&lt;/span&gt; &lt;span class="Entity"&gt;ExtendedMod&lt;/span&gt;
  &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;a&lt;/span&gt;
    &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;a&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;

  &lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;&amp;lt;&amp;lt;&lt;/span&gt; self&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;b&lt;/span&gt;
      &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;b&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Keyword"&gt;module&lt;/span&gt; &lt;span class="Entity"&gt;IncludedMod&lt;/span&gt;
  &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;c&lt;/span&gt;
    &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;c&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;

  &lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;&amp;lt;&amp;lt;&lt;/span&gt; self&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;d&lt;/span&gt;
      &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;d&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Klass&lt;/span&gt;
  &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;e&lt;/span&gt;
    &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;e&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;

  &lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;&amp;lt;&amp;lt;&lt;/span&gt; self&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;f&lt;/span&gt;
      &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;f&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;SubKlass&lt;span class="Superclass"&gt; &lt;span class="Superclass"&gt;&amp;lt;&lt;/span&gt; Klass&lt;/span&gt;&lt;/span&gt;
  &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;g&lt;/span&gt;
    &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;g&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Comment"&gt;  &lt;span class="Comment"&gt;#&lt;/span&gt; Skip h (ERB::Util.h)&lt;/span&gt;
  &lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;&amp;lt;&amp;lt;&lt;/span&gt; self&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;i&lt;/span&gt;
      &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;i&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Support"&gt;Klass&lt;/span&gt;.&lt;span class="Entity"&gt;extend&lt;/span&gt; &lt;span class="Variable"&gt;ExtendedMod&lt;/span&gt;
&lt;span class="Support"&gt;Klass&lt;/span&gt;.&lt;span class="Entity"&gt;send&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;include&lt;/span&gt;, &lt;span class="Variable"&gt;IncludedMod&lt;/span&gt;)

[&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;a&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;b&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;c&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;d&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;e&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;f&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;g&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;i&lt;/span&gt;].&lt;span class="Entity"&gt;each&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;m&lt;/span&gt;|
  puts &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;Klass responds to &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;m&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;? &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Support"&gt;Klass&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;respond_to?&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt;m&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  puts &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;Klass.new responds to &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;m&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;? &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Support"&gt;Klass&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;new&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;respond_to?&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt;m&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  puts &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;SubKlass responds to &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;m&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;? &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Support"&gt;SubKlass&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;respond_to?&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt;m&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  puts &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;SubKlass.new responds to &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;m&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;? &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Support"&gt;SubKlass&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;new&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;respond_to?&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt;m&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  puts &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="Constant"&gt;\n&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;/pre&gt;

&lt;p&gt;And the output: &lt;/p&gt;
&lt;pre class="blackboard"&gt;&lt;span class="Variable"&gt;Klass&lt;/span&gt; responds to a? &lt;span class="Constant"&gt;true&lt;/span&gt;
&lt;span class="Support"&gt;Klass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to a? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Variable"&gt;SubKlass&lt;/span&gt; responds to a? &lt;span class="Constant"&gt;true&lt;/span&gt;
&lt;span class="Support"&gt;SubKlass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to a? &lt;span class="Constant"&gt;false&lt;/span&gt;

&lt;span class="Variable"&gt;Klass&lt;/span&gt; responds to b? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Support"&gt;Klass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to b? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Variable"&gt;SubKlass&lt;/span&gt; responds to b? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Support"&gt;SubKlass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to b? &lt;span class="Constant"&gt;false&lt;/span&gt;

&lt;span class="Variable"&gt;Klass&lt;/span&gt; responds to c? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Support"&gt;Klass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to c? &lt;span class="Constant"&gt;true&lt;/span&gt;
&lt;span class="Variable"&gt;SubKlass&lt;/span&gt; responds to c? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Support"&gt;SubKlass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to c? &lt;span class="Constant"&gt;true&lt;/span&gt;

&lt;span class="Variable"&gt;Klass&lt;/span&gt; responds to d? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Support"&gt;Klass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to d? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Variable"&gt;SubKlass&lt;/span&gt; responds to d? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Support"&gt;SubKlass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to d? &lt;span class="Constant"&gt;false&lt;/span&gt;

&lt;span class="Variable"&gt;Klass&lt;/span&gt; responds to e? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Support"&gt;Klass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to e? &lt;span class="Constant"&gt;true&lt;/span&gt;
&lt;span class="Variable"&gt;SubKlass&lt;/span&gt; responds to e? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Support"&gt;SubKlass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to e? &lt;span class="Constant"&gt;true&lt;/span&gt;

&lt;span class="Variable"&gt;Klass&lt;/span&gt; responds to f? &lt;span class="Constant"&gt;true&lt;/span&gt;
&lt;span class="Support"&gt;Klass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to f? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Variable"&gt;SubKlass&lt;/span&gt; responds to f? &lt;span class="Constant"&gt;true&lt;/span&gt;
&lt;span class="Support"&gt;SubKlass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to f? &lt;span class="Constant"&gt;false&lt;/span&gt;

&lt;span class="Variable"&gt;Klass&lt;/span&gt; responds to g? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Support"&gt;Klass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to g? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Variable"&gt;SubKlass&lt;/span&gt; responds to g? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Support"&gt;SubKlass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to g? &lt;span class="Constant"&gt;true&lt;/span&gt;

&lt;span class="Variable"&gt;Klass&lt;/span&gt; responds to i? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Support"&gt;Klass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to i? &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="Variable"&gt;SubKlass&lt;/span&gt; responds to i? &lt;span class="Constant"&gt;true&lt;/span&gt;
&lt;span class="Support"&gt;SubKlass&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; responds to i? &lt;span class="Constant"&gt;false&lt;/span&gt;

&lt;/pre&gt;

&lt;p&gt;The takeaways?  Don't use class &lt;&lt; self in modules.  Always put class methods and instance methods in different modules.  Use extend for class methods and include for instance methods.  I had already "learned" this several times, but for some reason I couldn't remember it until I put this thing together.&lt;/p&gt;</description>
      <pubDate>Mon, 17 Nov 2008 20:46:33 GMT</pubDate>
      <guid>http://subwindow.com/articles/25</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Places where Rails is not DRY</title>
      <link>http://subwindow.com/articles/24</link>
      <description>&lt;p&gt;As I get more and more involved in Rails, I keep finding places where it is definitely not DRY.  When adding, removing or changing something, I never have complete confidence that I have checked every place where it could be referenced.  This is always a sure sign that something is amiss with the framework.  The following is kind of a running list of un-DRY thorns that I keep running into while developing Rails applications:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;Migrations:  Specify both up AND down.&lt;/li&gt;
&lt;li&gt;Migrations-&gt;Validations: Specify column creation in migration and validations.&lt;/li&gt;
&lt;li&gt;Associations: Specify column creation in migration and association in model.&lt;/li&gt;
&lt;li&gt;Validations-&gt;View: Specify validations in model and specify which ones are required in the view (red-asterisk problem)&lt;/li&gt;
&lt;li&gt;Migrations/Model-&gt;Show view: Specify columns in migration and in the "Show" view.&lt;/li&gt;
&lt;li&gt;Migrations/Model-&gt;Index view: Specify columns in migration and in the "Index" view.&lt;/li&gt;
&lt;li&gt;Migrations/Model-&gt;Form view: Specify columns in migration and in the "Form" view partial (edit/new).&lt;/li&gt;
&lt;li&gt;Migrations/Model Associations-&gt;Show view(s): Specify an association in the migration, in the model, and in the views for BOTH the child AND the parent.&lt;/li&gt;
&lt;li&gt;Model "Human Identifiers": Human-recognizable triggers for a specific record ("Name" for Users, "Title" for books, etc). Specify column in migration and potentially dozens of places in the view and/or helpers.&lt;/li&gt;
&lt;li&gt;Controllers: The vast majority of my controllers are cookie-cutter.  Update is almost always find-&gt;update_attributes-&gt;if no error, redirect to show/if error render edit.&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;Can anyone think of any others?&lt;/p&gt;

&lt;p&gt;The particularly sticky part of this is that these all stack.  With an important column you end up having its specification in potentially dozens of places, which is completely unmaintainable.  By "Specification" I mean its name, its data type, its associations, its validations, how it is displayed in a show/index, how it is displayed in a form, and search characteristics.  Is it possible to keep this all in one place?  I'd be satisfied with two places, which is what I'm targeting (migration and "Presenter").&lt;/p&gt;

&lt;p&gt;Although there are some existing (and hacky) workarounds to a few of these problems.  I look forward to them being solved in a cohesive manner.  Specifically, I'm working on #4-10, and my coworker &lt;a href="http://bunster.org"&gt;Jordi&lt;/a&gt; is working on 1-3.  Is there anyone else working on these problems?  DataMapper and Merb come to mind, but so far I'm not satisfied that they solve the problems correctly.  That doesn't mean we can't learn from them, though.&lt;/p&gt;</description>
      <pubDate>Fri, 05 Sep 2008 19:23:37 GMT</pubDate>
      <guid>http://subwindow.com/articles/24</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Summer Rails Plugin Series #3: acts_as_referenced</title>
      <link>http://subwindow.com/articles/23</link>
      <description>&lt;p&gt;Next up in my summer series of plugins is acts_as_referenced.  This one is a bit small, but I think it can be extremely useful.  This is a versatile and reasonably well-tested plugin that I'm using every day.  On my main project, I have about 8 models that use reference numbers- five of them use the same numbering scheme, and the other three use different ones.  I needed a way to DRY up the assignment of these reference numbers while creating a central search index (GREAT for barcoded reference numbers), allowing for legacy reference numbers AND having them be valuable to a human reader by including the date and object type in the reference number.&lt;/p&gt;

&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;If you are on edge rails:&lt;/p&gt;

&lt;pre class="blackboard"&gt;script/plugin install git://github.com/subwindow/acts_as_referenced.git&lt;/pre&gt;
  
&lt;p&gt;If you are not on edge rails:&lt;/p&gt;

&lt;pre class="blackboard"&gt;git clone git://github.com/subwindow/acts_as_referenced.git vendor/plugins/acts_as_referenced&lt;/pre&gt;

&lt;h2&gt;Usage&lt;/h2&gt;

&lt;p&gt;To make use of acts_as_referenced, you first need to create the Reference model &amp; its table:&lt;/p&gt;
  &lt;pre class="blackboard"&gt;script/generate reference reference
rake db:migrate&lt;/pre&gt;
  
&lt;p&gt;You also need a column 'reference' in all of the tables you plan on using acts_as_referenced with. (You can change this with the :referenced_column option)&lt;/p&gt;

&lt;p&gt;Enable the functionality by declaring acts_as_referenced on your model&lt;/p&gt;

&lt;pre class="blackboard"&gt;&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Order&lt;span class="Superclass"&gt; &lt;span class="Superclass"&gt;&amp;lt;&lt;/span&gt; ActiveRecord::Base&lt;/span&gt;&lt;/span&gt;
  acts_as_referenced
&lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;
  
&lt;h2&gt;Advanced Usage&lt;/h2&gt;

&lt;p&gt;Advanced usage is enabled through a set of options.  Here they are:&lt;/p&gt;
  
&lt;pre class="blackboard"&gt;&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Separator&lt;/span&gt;
acts_as_referenced &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Defaults to '-', e.g.: '080722-1'&lt;/span&gt;
acts_as_referenced &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;separator&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;e.g.: '0807221'&lt;/span&gt;
acts_as_referenced &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;separator&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;_&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;e.g.: '080722_1'&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Prefix.  A string that is appended at the front of the reference number.  Does not affect incrementing.  Accepts a proc.&lt;/span&gt;
acts_as_referenced &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Defaults to '', e.g.: '080722-1'&lt;/span&gt;
acts_as_referenced &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;prefix&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;OR&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; e.g.: &amp;quot;OR-080722-1&amp;quot;&lt;/span&gt;
acts_as_referenced &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;prefix&lt;/span&gt; =&amp;gt; &lt;span class="Support"&gt;Proc&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; {|&lt;span class="Variable"&gt;o&lt;/span&gt;| o.&lt;span class="Entity"&gt;category&lt;/span&gt;.&lt;span class="Entity"&gt;first&lt;/span&gt;.&lt;span class="Entity"&gt;upcase&lt;/span&gt; } &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; e.g.: &amp;quot;M-080722-1&amp;quot;&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Increment base.  A string that is used as the base for incrementing- all reference numbers with the same base will be incremented together.  Accepts strftime arguments.  &lt;/span&gt;
acts_as_referenced &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Defaults to &amp;quot;%y%m%d&amp;quot;, e.g.: &amp;quot;080722-1&amp;quot;&lt;/span&gt;
acts_as_referenced &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;increment_base&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt;&lt;span class="Constant"&gt;65&lt;/span&gt;&lt;span class="Keyword"&gt;+&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt;&lt;span class="Support"&gt;Time&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;now&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;year&lt;/span&gt;&lt;/span&gt;&lt;span class="Keyword"&gt;-&lt;/span&gt;&lt;span class="Constant"&gt;2003&lt;/span&gt;&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;chr&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;%m%d&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; e.g.: &amp;quot;F0722-1&amp;quot;&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Increment size.  An integer that is used to pad incrementers.  Useful if you require reference numbers with a consistent legnth.&lt;/span&gt;
acts_as_referenced &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Defaults to 0, e.g.: &amp;quot;080722-1&amp;quot;, &amp;quot;080722-9999&amp;quot;&lt;/span&gt;
acts_as_referenced &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;increment_size&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;4&lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;e.g.: &amp;quot;080722-0001&amp;quot;, &amp;quot;080722-9999&amp;quot;&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Referenced Column.  Change the name of the &amp;quot;reference&amp;quot; column that is stored on the model.&lt;/span&gt;
acts_as_referenced &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Defaults to 'reference'&lt;/span&gt;
acts_as_referenced &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;referenced_column&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;order_number&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;

&lt;h2&gt;A Note on Racing&lt;/h2&gt;
&lt;p&gt;There's a racing condition inherent in the plugin as it is shipped.  If there are two processes creating references at approximately the same time, it is entirely possible that they will collide and create reference numbers at the same time.  Rails may or may not catch this, so the possibility for duplicate reference numbers exists, and the possibility for confusing end-user errors is even greater.  To solve this, you have to lock the table from reads while creating a reference number.  It should only take &lt; 50ms, so in most cases it is not that big of a deal.  The locking procedures are different for different DBMSs.  For PostgreSQL, uncomment lib/acts_as_referenced.rb line #91.  For MySQL, uncomment lib/acts_as_referenced.rb lines #93 and #99.  For other systems, simply write your own table locking/unlocking SQL and place it in the same spots.&lt;/p&gt;
 
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;As always, I hope someone can find this plugin useful.  Please let me know if there are any bugs or you are confused by something.  My normal use-case is a bit esoteric, so if you have questions on how this might be useful, let me know in a comment and I can try to explain it better.  For any other questions or comments, email me at erik [at] subwindow (dot} com.  To contribute code, fork it at &lt;a href="http://github.com/subwindow/acts_as_referenced/tree/master"&gt;github&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Wed, 23 Jul 2008 05:31:45 GMT</pubDate>
      <guid>http://subwindow.com/articles/23</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Summer Rails Plugin Series #2: needs_approval</title>
      <link>http://subwindow.com/articles/22</link>
      <description>&lt;p&gt;Ok, so I know I promised a Rails plugin every week.  About a week after I made that promise I learned something: making a Rails plugin every week is fucking impossible.  Even if you've already written the code and all you need to do is extract it.  It is impossible.  I'd be lucky to fit in one plugin a month.  Still, one plugin a month for 6 months is still a pretty decent clip, so I'd be satisfied if I can hold myself to that (relaxed) schedule.&lt;/p&gt;

&lt;p&gt;Anyways, next up is a plugin called needs_appoval. This is a pretty simple plugin designed to help you manage approval flows.  If you have records that need to be approved by users, this plugin might help you out.  It also has the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ladder-based approvals (where one user can only approve after the users listed previous to him have approved as well)&lt;/li&gt;
&lt;li&gt;Approval requirement conditions (where a user only needs to approve on certain conditions)&lt;/li&gt;
&lt;li&gt;Optional, built-in authentication (where a user must enter in their password whenever they approve something- often required by 21 CFR Part 11 and ISO 9001)&lt;/li&gt;&lt;/ul&gt;

&lt;h2&gt;Instructions&lt;/h2&gt;
&lt;p&gt;Install using the command&lt;/p&gt;
&lt;pre class="blackboard"&gt;script/plugin install git://github.com/subwindow/needs_approval.git
&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;Generate a scaffolded approval model/controller using the command&lt;/p&gt;
&lt;pre class="blackboard"&gt;script/generate approval APPROVALMODEL USERMODEL [--include-authentication] [--skip-migration]
&lt;/pre&gt;
Example
 &lt;pre class="blackboard"&gt;script/generate approval approval user
&lt;/pre&gt;
  
&lt;p&gt;Put the approval structure definition in your desired models&lt;p&gt;
&lt;pre class="blackboard"&gt;&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Order&lt;span class="Superclass"&gt; &lt;span class="Superclass"&gt;&amp;lt;&lt;/span&gt; ActiveRecord::Base&lt;/span&gt;&lt;/span&gt;
    needs_approval &lt;span class="Keyword"&gt;do&lt;/span&gt;
      of &lt;span class="Support"&gt;User&lt;/span&gt;.&lt;span class="Entity"&gt;find_by_login&lt;/span&gt;(&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;boss&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
      of &lt;span class="Support"&gt;User&lt;/span&gt;.&lt;span class="Entity"&gt;find_by_login&lt;/span&gt;(&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;bigboss&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class="Keyword"&gt;if&lt;/span&gt; total_price &lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt; &lt;span class="Constant"&gt;1000&lt;/span&gt;
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;For ladder-based approvals, use the following.  The function next_approvers goes in order of approval definition.&lt;/p&gt;
&lt;pre class="blackboard"&gt;&lt;span class="Entity"&gt;needs_approval&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;ladder&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;true&lt;/span&gt;)
&lt;/pre&gt;
 
&lt;p&gt;Include approval information in the show page of an approved object&lt;/p&gt;
&lt;pre class="blackboard"&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;%=&lt;/span&gt; &lt;span class="Entity"&gt;approvals_for&lt;/span&gt;(&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;order&lt;/span&gt;) &lt;span class="String"&gt;&lt;span class="String"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;

&lt;h2&gt;Other useful functions:&lt;/h2&gt;
&lt;pre class="blackboard"&gt;&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Boolean, returns true if all approvals have been satisfied&lt;/span&gt;
&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;order&lt;/span&gt;.&lt;span class="Entity"&gt;has_all_approvals?&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Returns an array of all users that are required to approve this object&lt;/span&gt;
&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;order&lt;/span&gt;.&lt;span class="Entity"&gt;approvals_needed&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Returns an array of users that have approved this object&lt;/span&gt;
&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;order&lt;/span&gt;.&lt;span class="Entity"&gt;approvals&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Returns an array of users that need to approve this object, but have not yet&lt;/span&gt;
&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;order&lt;/span&gt;.&lt;span class="Entity"&gt;failed_approvals&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Boolean, returns true if this object requires and does not yet have the approval of this user&lt;/span&gt;
&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;order&lt;/span&gt;.&lt;span class="Entity"&gt;needs_approval_of&lt;/span&gt;(user)

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Boolean, returns true if this user has approved this object&lt;/span&gt;
&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;order&lt;/span&gt;.&lt;span class="Entity"&gt;passes_approval&lt;/span&gt;(user)

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Returns an array of users that are set to approve this next (for ladder-based approvals)&lt;/span&gt;
&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;order&lt;/span&gt;.next_approver
&lt;/pre&gt;
  
&lt;h2&gt;Note&lt;/h2&gt;
&lt;p&gt;This plugin kind of depends on restful_authentication or something similar.  A "current_user" method must be accessible which returns the currently logged in user.&lt;/p&gt;
&lt;p&gt;If the current_user function is named differently, either:&lt;/p&gt;
    &lt;ul&gt;&lt;li&gt;define an alias, or&lt;/li&gt;
    &lt;li&gt;change the references in the approvals controller and pass in the current user as a second argument to the approvals_for helper [eg: "approvals_for(@order, active_user)"]&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;An example app is located in the directory example_app.  You can delete this if you don't want it (especially if you use TextMate-style "Go To File").  To run, execute the following commands:&lt;/p&gt;
&lt;pre class="blackboard"&gt;cd vendor/plugins/needs_approval/example_app
script/plugin install git://github.com/subwindow/needs_approval.git
rake db:create
rake db:migrate
rake db:fixtures:load
script/server
&lt;/pre&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope somebody can find this helpful.  This was originally a much larger plugin (&gt;100 lines; currently ~50), but in the process of extracting it I refactored the code and made it much smaller. This means it doesn't seem as useful, but at the same time it was very useful to myself because I end up with a much nicer codebase.  As usual, If you have any questions, concerns or have found any bugs, please email me at erik [at] subwindow dotcom.  Or, reply here.  Or, go to the &lt;a href="http://github.com/subwindow/needs_approval/tree/master"&gt;github page&lt;/a&gt; and fork the code.&lt;/p&gt;

&lt;p&gt;Next up: acts_as_filtered.  A plugin to assist in dynamically filtered models.  The code is very mature and in production use, but it might need to be refactored and generalized a bit.  Hopefully I'll get it out before the end of the month.&lt;/p&gt;</description>
      <pubDate>Wed, 09 Jul 2008 06:54:26 GMT</pubDate>
      <guid>http://subwindow.com/articles/22</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>What You're Doing is Not New, Not Revolutionary</title>
      <link>http://subwindow.com/articles/21</link>
      <description>&lt;p&gt;In 1971, Mark Zuckerberg was negative 13 years old.  The phrase "social network" would have returned awkward silences and weird stares, even amongst the savviest tech entrepreneurs.  If you had said the word "Blog" people would have thought you were coughing something up.  Yet, in their own weird little ways, thousands of people were social networking and blogging.&lt;/p&gt;

&lt;p&gt;In my post Wednesday, I said:
&lt;blockquote&gt;We tend to live in our own little bubbles and think that somehow, our problems are brand new, and that they've never been solved before. Well, guess what: almost all of them have been solved before, have been solved better, and are included in almost all Unix systems out there.&lt;/blockquote&gt;
I've found that this maxim is almost universally applicable to movements in technology.  I've there's some big new thing, it has probably been done before, and it has probably been done better, and it was probably included in Unix.&lt;/p&gt;

&lt;h2&gt;finger is to Friendster as Friendster is to Facebook&lt;/h2&gt;
&lt;p&gt;The finger Unix program was developed in 1971 by Les Earnest.  It provided a way for you to figure out what other people were up to.  You'd execute the command "finger user@domain" and you'd get all sorts of useful information back: The user's phone number, how long they've been idle, how long it's been since they've checked their mail, and the contents of their .project and .plan files.  Here's what it looks like if you execute "finger erik@subwindow.com":&lt;/p&gt;

&lt;pre class="blackboard"&gt;
[subwindow.com]
Login: erik           			Name: 
Directory: /home/erik               	Shell: /bin/bash
On since Sat Jul  5 14:11 (UTC) on pts/0 from c-98-242-74-4.hsd1.ga.comcast.net
   3 minutes 9 seconds idle
No mail.
Project:
Current Projects:
  -Super Secret project.  Relates to http://subwindow.com/articles/14
  -Rails plugin for managing approvals.  Was called acts_as_approved, but I think I'm renaming it.

Projects in Stasis:
  -Aloe: A long-term work in progress. Aloe (or "A-l=o.e": Assets - Liabilities = Owner's Equity) will
    be an easy-to-use online accounting system. It is designed for the small business owner that does
    not specifically have any skills in accounting. It is currently in pre-alpha.  http://aloe-acct.com/
  -Skribit: Formed as part of Startup Weekend Atlanta, I'm an ongoing contributor to this social suggestion
    engine for bloggers.  Contributions are erratic, based on the activity of those in the "Current Projects"
    section.  Others are more active than me, so the entire project is not in stasis, mind you.

Projects in Maintenance:
  -Forecaster.ws A simple and uncluttered weather information service, tailored specifically for mobile
    devices. Currently in late public beta.
  -PropertyBuilder A proposed alternative to Object#andand and the ilk. Available through the
    'gem install propertybuilder' command.
  -Zsff A parser/validator for the ZSFF feed format. Version 1.0 released as a gem and is available on
    Rubyforge or through the 'gem install zsff' command
  -Rhobbler A Last.fm / AudioScrobbler track submission tool for Rhapsody. Currently in public beta.

Plan:
Today, I'm going to have a 4th of July party/Kelly's Birtday party.  But it's on the 5th.  Because even parties
    can be fashionably late.

This week: Super-secret project continues.  On Tuesday I hope to release my approval Rails plugin.
&lt;/pre&gt;

&lt;p&gt;Hmm.  That looks awfully similar to some other services I use heavily.  It's pretty much the amalgamation of my Facebook page, my blog, and my tweets.  And this was available in 1971!&lt;/p&gt;

&lt;h2&gt;The Trouble With finger&lt;/h2&gt;
&lt;p&gt;You may be wondering "If finger was so great, how come I've never heard of it, and how come we aren't using it now?"  Well, finger had a lot of problems, unfortunately.  Here's my understanding of what caused the downfall of finger (Also note that I was negative 12 years old when finger was invented, and about 10 when most people stopped using it):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It depended on a doomed computing style.  The only way finger really made sense was if everyone at an organization regularly logged into the same physical Unix machine.  This made a ton of sense in the mainframe era, but finger's popularity was directly proportional to the popularity of the mainframe.&lt;/li&gt;
&lt;li&gt;There were no privacy protections.  finger was notorious for being used as a tool for crackers and other nefarious characters to gain information.  If you fingered the root of a domain (ie: 'finger @gatech.edu'), you'd get a listing of every user in the system- their phone numbers, and what they were up to.  This information is kind of sensitive to just be putting out into the ether.  Subsequently, fingering was seen has kind of a rude thing to do to someone unless you knew them and had a justification for seeing what they were up to.  This didn't sit well with lots of people and contributed greatly to its demise.  finger had no privacy features like Facebook now has.  If it did (ie: Here's who my friends/coworkers are, let them see this information.  Only display this subset to other users), I think it might still be in heavy use today.&lt;/li&gt;
&lt;li&gt;finger was a protocol, and not a platform.  There was no party responsible for carrying it forward or for providing necessary upgrades.  When Facebook users complained about too much information being available to the public, they instituted incredibly granular privacy controls.  When finger users complained about the exact same thing, there was either nobody to complain to, or they said "Well, turn it off."  So they did.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Lessons are for Hares&lt;/h2&gt;
&lt;p&gt;Of course, there's some valuable lessons to be learned here.  The main one is that you should always make more of an effort to see what's been done before you.  The makers of Facebook shouldn't have just learned about Friendster, but they should have made more of an effort to learn about the earlier ancestors of social networking.  If they had looked for and learned about the reasons for finger's demise, they might have avoided about half of the major confrontations that they've had with their users.  With a full understanding of history, you can better realize that you are not a unique butterfly, and that your idea is not really that new.  You can better gain perspective and learn the lessons of your forebears.  This is something that's not unique to technology.  You should apply this maxim everywhere in life- especially politics.&lt;/p&gt;</description>
      <pubDate>Sat, 05 Jul 2008 18:44:08 GMT</pubDate>
      <guid>http://subwindow.com/articles/21</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Background Processing in Rails Using 'at'</title>
      <link>http://subwindow.com/articles/20</link>
      <description>&lt;p&gt;So here's a fairly typical problem that happens in the Rails world:  you have a long-running process that you need to offload/queue up, and you don't have time to fuck around with &lt;a href="http://xph.us/software/beanstalkd/"&gt;some&lt;/a&gt; &lt;a href="http://rubyforge.org/forum/forum.php?forum_id=19781"&gt;kind&lt;/a&gt; of &lt;a href="http://backgroundrb.rubyforge.org/"&gt;elegant&lt;/a&gt; &lt;a href="http://code.google.com/p/activemessaging/wiki/ActiveMessaging"&gt;solution&lt;/a&gt;.  Before, either I'd just make the user eat it and wait for the long request (at the risk of timing out the request and/or pissing off the user), or figure out some way to procrastinate on implementing the feature in the first place.&lt;/p&gt;  

&lt;p&gt;But alas, I came to a situation that absolutely had to be offloaded: a 50MB file import that takes about 3-5 minutes to process.  The request is guaranteed to time out, and it is something that will be done fairly regularly.  I'd love to tell my users "Hey, this shit is going to break, but it will be the best and most awesome breaking you've ever seen," but somehow I don't think that would fly.  Damn.  I don't want to screw around for two days trying to cook up some kind of "scalable" solution involving worker processes and messaging queues.  I don't need scalability (this will be run 2 or 3 times a week, max), I just need it to work.&lt;/p&gt;

&lt;p&gt;When faced with a problem like this, I think web developers in general underestimate the massive amounts of thought and effort that have gone into the systems that we use every day and generally take for granted.  We tend to live in our own little bubbles and think that somehow, our problems are brand new, and that they've never been solved before.  Well, guess what: almost all of them have been solved before, have been solved better, and are included in almost all Unix systems out there.&lt;/p&gt;

&lt;h2&gt;Enter &lt;code style="display: inline"&gt;at&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;If you have a command, and want to have it wait a tick before it starts working, and want the work of several jobs to be handled in some kind of sane fashion, &lt;code style="display: inline"&gt;at&lt;/code&gt; is the perfect solution.  Like most things Unix, &lt;code style="display: inline"&gt;at&lt;/code&gt; is pretty damned simple.  You have a set of commands in some kind of a file.  You want to execute them at some date in the future.  You send those to &lt;code style="display: inline"&gt;at&lt;/code&gt;:  &lt;pre class="blackboard" style="display: inline"&gt;at -t 07021824 -f /path/to/my/commands&lt;/pre&gt;.  That's pretty damned beautiful.&lt;/p&gt;

&lt;p&gt;Now, putting it in Rails is actually damned easy.  I wrote a 3-line method in my ApplicationController so that I can offload any arbitrary command.  Take a look at this awesome sauce:
&lt;pre class="blackboard"&gt;&lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;offload&lt;/span&gt;(&lt;span class="Variable"&gt;command&lt;/span&gt;)
  job_id &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Variable"&gt;MD5&lt;/span&gt;.&lt;span class="Entity"&gt;hexdigest&lt;/span&gt;(&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;command&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;+&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Support"&gt;Time&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;now&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;to_i&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;+&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;$&lt;/span&gt;$&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
  &lt;span class="String"&gt;&lt;span class="String"&gt;`&lt;/span&gt;echo &amp;quot;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;RAILS_ROOT&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;/script/runner -e &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class="Constant"&gt;\\&lt;/span&gt;&amp;quot;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;command&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="Constant"&gt;\\&lt;/span&gt;&amp;quot;&amp;quot; &amp;gt; /tmp/&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;job_id&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt; &amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="String"&gt;   at -t &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Constant"&gt;1&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;minute&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;from_now&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;strftime&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;%m%d%H%M&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt; -f /tmp/&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;job_id&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;Every time I need to offload some arbitrary command, all I do is pass it to &lt;code style="display: inline"&gt;offload&lt;/code&gt;: &lt;pre class="blackboard" style="display: inline"&gt;offload("Part.import_bom('#{massive_file}')")&lt;/pre&gt;.  Simple as pie.&lt;/p&gt;

&lt;p&gt;Scaling?  You say you want scaling?  You have 3 application servers that you want to dish these offloaded processes to as equitably as possible?  You are also as lazy as I am?  Awesome:
&lt;pre class="blackboard"&gt;&lt;span class="Variable"&gt;APP_SERVERS&lt;/span&gt; &lt;span class="Keyword"&gt;=&lt;/span&gt; [&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;172.16.200.50&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;172.16.200.51&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;172.16.200.52&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;]
&lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;offload&lt;/span&gt;(&lt;span class="Variable"&gt;command&lt;/span&gt;)
  job_id &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Variable"&gt;MD5&lt;/span&gt;.&lt;span class="Entity"&gt;hexdigest&lt;/span&gt;(&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;command&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;+&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Support"&gt;Time&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;now&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;to_i&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;+&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;$&lt;/span&gt;$&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
  &lt;span class="String"&gt;&lt;span class="String"&gt;`&lt;/span&gt;ssh user@&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;APP_SERVERS&lt;/span&gt;&lt;span class="String"&gt;[&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="Entity"&gt;rand&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt;&lt;span class="Variable"&gt;APP_SERVERS&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;size&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;]&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt; &amp;quot;echo &lt;span class="Constant"&gt;\\&lt;/span&gt;&amp;quot;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;RAILS_ROOT&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;/script/runner -e &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class="Constant"&gt;\\&lt;/span&gt;&lt;span class="Constant"&gt;\\&lt;/span&gt;&amp;quot;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;command&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="Constant"&gt;\\&lt;/span&gt;&lt;span class="Constant"&gt;\\&lt;/span&gt;&amp;quot;&lt;span class="Constant"&gt;\\&lt;/span&gt;&amp;quot; &amp;gt; /tmp/&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;job_id&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt; &amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="String"&gt;   at -t &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Constant"&gt;1&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;minute&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;from_now&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;strftime&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;%m%d%H%M&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt; -f /tmp/&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;job_id&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&amp;quot;&lt;span class="String"&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;
 
&lt;p&gt;Now you've got your distributed job queue implemented.  Three lines of code.  Win.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;Sidenote:  If your dev machine is running OSX like mine is, and you want this to work, you're going to have to run the command &lt;code style="display: inline"&gt;sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.atrun.plist&lt;/code&gt; and restart before this stuff will work locally.&lt;/small&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 03 Jul 2008 02:13:10 GMT</pubDate>
      <guid>http://subwindow.com/articles/20</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>English as Code as Pseudocode</title>
      <link>http://subwindow.com/articles/19</link>
      <description>&lt;h3&gt;The Tradition of Pseudocode&lt;/h3&gt;

&lt;p&gt;Traditionally, when developers got together to talk about code, they would invariably end up writing pseudocode on the nearest white board, piece of paper or dinner napkin.  The reasons for this are myriad.  First, developers favor different languages, and might not be able to read their friend's favorite language.  Especially 15 or 20 years ago, languages used to be so stylistically different that a developer who hadn't already had exposure to, say, Algol or Fortran, might not be able to easily parse even basic code statements.  The second main reason for the use of pseudo code is that 20 years ago, most languages didn't read like English.  If you wanted to &lt;i&gt;really&lt;/i&gt; describe how a program worked, you would have to use pseudocode, because all of the programming languages you knew read like shit.&lt;/p&gt;

&lt;p&gt;That's not such a big deal these days, also for a couple of reasons.  First, the languages currently in favor are much more high level and all have much more sophisticated Object models than the ones of yore.  You generally don't have to explicitly allocate memory, declare variables, or do other kinds of tedious dreck.  With more refined OOP concepts, you (generally) don't have to write as much boilerplate or worry about dumb gotchas as you had to with antiquated object models like Java or Perl's sad excuse for OO.  Secondly, most newer languages have gone a long way towards actually reading like English.  Ruby and Python, in particular, have both eschewed the use of unnecessary tokens (braces in particular)  and made their calling syntax much more natural.&lt;/p&gt;

&lt;p&gt;As a result, you don't see much pseudocode being written these days.  It used to be that &lt;i&gt;any&lt;/i&gt;time you would talk about algorithms, you would use pseudocode.  Now?  not so much.&lt;/p&gt;

&lt;h3&gt;Taking it a bit Further&lt;/h3&gt;
&lt;p&gt;So, since modern languages read so well that they have essentially supplanted pseudocode, should we stop there?  Is it good enough that a programming language can be readily understood by any novice?  I say no.  I think we shouldn't stop pressing the development of languages until the concepts that the code is portraying &lt;b&gt;cannot be expressed any clearer or more succinctly than they already are&lt;/b&gt;.  That seems like a pretty hefty goal- to have a programming language that expresses things so clearly and succinctly that there is literally no room for improvement.  We've generally seen the succinct side maximized- I would argue that Perl takes the cake on that one, and it is unlikely that there will ever be a language as succinct as it is.  But Perl focused on succinctness at the expense of clarity.  That's not really necessary.&lt;/p&gt;

&lt;p&gt;So our goal is maximum succinctness and clarity.  How far away are we?  Surprisingly, not as far away as I had thought.  Certain things have already hit this "wall of awesomeness"- domain-specific languages in particular.  This following code snippet of the Rails plugin acts_as_state_machine does a great job of explaining both why I love DSLs so much, and why they're our best hope for accomplishing our goals:&lt;/p&gt;

&lt;pre class="blackboard"&gt;acts_as_state_machine &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;initial&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;pending&lt;/span&gt;
state &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;pending&lt;/span&gt;
state &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;submitted&lt;/span&gt;

event &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;submit&lt;/span&gt; &lt;span class="Keyword"&gt;do&lt;/span&gt;
  transitions &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;from&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;pending&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;to&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;submitted&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;Try reading that code.  Literally read it.  It should read something like: "This acts as a state machine, with an initial state of 'Pending'.  It has two states: 'Pending' and 'Submitted'.  It has an event called 'submit' which transitions from pending to submitted."  You'll notice something about reading that- the code is actually more clear and more succinct than the English representation of the code.  Drawing a diagram of this small state machine would take longer and possibly be less clear than the code sample itself.  When code is both clearer and shorter than either English or a drawing, you know you've won.&lt;/p&gt;

&lt;h3&gt;Are We There Yet?  Are We There Yet?  No!&lt;/h3&gt;

&lt;p&gt;That one example explains why I'm such a big fan of Ruby.  It encourages code to both read like English and to read &lt;i&gt;better&lt;/i&gt; than English.  Whenever I come across a common pattern in my code, I find it extremely easy to abstract out that pattern into a DSL and bask in the clarity.  This usually achievable, but isn't always easy, as in this example:&lt;/p&gt;

&lt;pre class="blackboard"&gt;needs_approval &lt;span class="Keyword"&gt;do&lt;/span&gt;
  of department.&lt;span class="Entity"&gt;manager&lt;/span&gt;
  of &lt;span class="Support"&gt;User&lt;/span&gt;.&lt;span class="Entity"&gt;cfo&lt;/span&gt; &lt;span class="Keyword"&gt;if&lt;/span&gt; total_price &lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt; &lt;span class="Constant"&gt;5000&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;This code comes from a plugin that I was working on when I originally published this article.  This code specifically deals with an approval structure for a Purchase Order.  Each purchase order needs the approval of the Department Manager and, if the total price of the PO is over $5000, the CFO.  This is actually pretty good.&lt;/p&gt;

&lt;p&gt;I updated this article on July 17th, after I released the plugin.  I was able to figure out a way to clean up the syntax quite a bit, and remove all of the brackets.  I thought that I needed Ruby to have more power (specifically, being able to pass blocks around willy-nilly), but really I just needed to work hardt to create some nice syntax.  So are we there yet?  Maybe.  I might be.&lt;/p&gt;</description>
      <pubDate>Sat, 28 Jun 2008 21:02:54 GMT</pubDate>
      <guid>http://subwindow.com/articles/19</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Summer Rails Plugin Series</title>
      <link>http://subwindow.com/articles/18</link>
      <description>&lt;p&gt;I apologize for my extremely lengthy blogging break.  But I'm back! I promise!  I've got quite a backup of code that I want to release, and as part of that, I promise to release a Rails plugin each week for the rest of the summer.  That's quite a hefty promise, but I've got June mapped out, at the very least.  We'll see where it goes from there.  First up in my series of Summer Rails Plugins is &lt;code style="display:inline"&gt;negative_captcha&lt;/code&gt;:&lt;/p&gt;

&lt;h2&gt;What is a Negative Captcha?&lt;/h2&gt;
&lt;p&gt;A negative captcha has the exact same purpose as your run-of-the-mill image captcha: To keep bots from submitting forms.  Image ("positive") captchas do this by implementing a step which only humans can do, but bots cannot: read jumbled characters from an image.  But this is bad.  It creates usability problems, it hurts conversion rates, and it confuses the shit out of lots of people.  Why not do it the other way around? Negative captchas create a form that has tasks that only bots can perform, but humans cannot.  This has the exact same effect, with (anecdotally) a much lower false positive identification rate when compared with positive captchas.  All of this comes without making humans go through any extra trouble to submit the form.  It really is win-win.&lt;/p&gt;
  
&lt;h2&gt;How does a Negative Captcha Work?&lt;/h2&gt;
&lt;p&gt;In a negative captcha form there are two main parts and three ancillary parts.  I'll explain them thusly.&lt;/p&gt;
&lt;p&gt;&lt;h3&gt;Main Part #1: Honeypots&lt;/h3&gt;
Honeypots are form fields which look exactly like real form fields.  Bots will see them and fill them out.  Humans cannot see them and thusly will not fill them out.  They are hidden indirectly- usually by positioning them off to the left of the browser. They look kind of like this:&lt;br /&gt;
&lt;pre class="blackboard"&gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;div&lt;/span&gt; &lt;span class="MetaTag"&gt;style&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;position: absolute; left: -2000px;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;input&lt;/span&gt; &lt;span class="MetaTag"&gt;type&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;text&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="MetaTag"&gt;name&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;name&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;  &lt;span class="MetaTag"&gt;value&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; /&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;div&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Main Part #2: Real fields&lt;/h3&gt;
    These fields are the ones humans will see, will subsequently fill out, and that you'll pull your real form data out of.  The form name will be hashed so that bots will not know what it is.  They look kind of like this:&lt;br /&gt;
      &lt;pre class="blackboard"&gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;input&lt;/span&gt; &lt;span class="MetaTag"&gt;type&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;text&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="MetaTag"&gt;name&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;685966bd3a1975667b4777cc56188c7e&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; /&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;h3&gt;Ancillary Part #1: Timestamp&lt;/h3&gt;
    This is a field that is used in the hash key to make the hash different on every GET request, and to prevent replayability.&lt;/p&gt;
&lt;p&gt;&lt;h3&gt;Ancillary Part #2: Spinner&lt;/h3&gt;
    This is a rotating key that is used in the hash method to prevent replayability.  I'm not sold on its usefulness.&lt;/p&gt;
&lt;p&gt;&lt;h3&gt;Ancillary Part #3: Secret key&lt;/h3&gt;
    This is simply some key that is used in the hashing method to prevent bots from backing out the name of the field from the hashed field name.&lt;/p&gt;
    
&lt;h2&gt;How does the Negative Captcha Plugin Work?&lt;/h2&gt;
&lt;ol&gt;&lt;li&gt;Install the plugin:
&lt;p&gt;With Subversion
&lt;pre class="blackboard"&gt;script/plugin install http://code.subwindow.com/negative_captcha&lt;/pre&gt;
&lt;/p&gt;&lt;p&gt;With git
&lt;pre class="blackboard"&gt;git submodule add git://github.com/subwindow/negative-captcha.git vendor/plugins/negative_captcha&lt;/pre&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;Add in the controller hooks
&lt;pre class="blackboard"&gt;&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;At top:&lt;/span&gt;
&lt;span class="Support"&gt;before_filter&lt;/span&gt; &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;setup_negative_captcha&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;only&lt;/span&gt; =&amp;gt; [&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;new&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;create&lt;/span&gt;]

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;At bottom:&lt;/span&gt;
&lt;span class="Keyword"&gt;private&lt;/span&gt;
  &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;setup_negative_captcha&lt;/span&gt;
    &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;captcha&lt;/span&gt; &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;NegativeCaptcha&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;(
&lt;span class="Comment"&gt;      &lt;span class="Comment"&gt;#&lt;/span&gt;A secret key entered in environment.rb.  'rake secret' will give you a good one.&lt;/span&gt;
      &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;secret&lt;/span&gt; =&amp;gt; ::&lt;span class="Entity"&gt;NEGATIVE_CAPTCHA_SECRET&lt;/span&gt;, 
      &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;spinner&lt;/span&gt; =&amp;gt; request.&lt;span class="Entity"&gt;remote_ip&lt;/span&gt;,
&lt;span class="Comment"&gt;      &lt;span class="Comment"&gt;#&lt;/span&gt;Whatever fields are in your form  &lt;/span&gt;
      &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;fields&lt;/span&gt; =&amp;gt; [&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;name&lt;/span&gt;, email, body],
      &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;params&lt;/span&gt; =&amp;gt; params)
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;Modify your POST action(s) to check for the validity of the negative captcha form

&lt;pre class="blackboard"&gt;&lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;create&lt;/span&gt;
  &lt;span class="Keyword"&gt;if&lt;/span&gt; &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;captcha&lt;/span&gt;.&lt;span class="Entity"&gt;valid?&lt;/span&gt;
    &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;comment&lt;/span&gt; &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;Comment&lt;/span&gt;.&lt;span class="Entity"&gt;create&lt;/span&gt;(&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;captcha&lt;/span&gt;.&lt;span class="Entity"&gt;values&lt;/span&gt;) &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;Decrypted params&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;

  &lt;span class="Keyword"&gt;if&lt;/span&gt; &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;captcha&lt;/span&gt;.&lt;span class="Entity"&gt;valid?&lt;/span&gt; &lt;span class="Keyword"&gt;&amp;amp;&lt;/span&gt;amp;&lt;span class="Keyword"&gt;&amp;amp;&lt;/span&gt;amp; &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;comment&lt;/span&gt;.&lt;span class="Entity"&gt;valid?&lt;/span&gt;
    &lt;span class="Support"&gt;redirect_to&lt;/span&gt; &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;comment&lt;/span&gt;
  &lt;span class="Keyword"&gt;else&lt;/span&gt;
    flash[&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;notice&lt;/span&gt;] &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;captcha&lt;/span&gt;.&lt;span class="Entity"&gt;error&lt;/span&gt;
    &lt;span class="Support"&gt;render&lt;/span&gt; &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;action&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;new&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;Modify your form to include the honeypots and other fields.  You can probably leave any select, radio, and check box fields alone.  The text field/text area helpers should be sufficient.
&lt;pre class="blackboard"&gt;&amp;lt;% form_tag comments_path &lt;span class="Keyword"&gt;do &lt;/span&gt;-%&amp;gt;
  &amp;lt;%= &lt;span class="Entity"&gt;negative_captcha&lt;/span&gt;(&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;captcha&lt;/span&gt;) %&amp;gt;
  &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;ul&lt;/span&gt; &lt;span class="MetaTag"&gt;class&lt;/span&gt;=&amp;amp;&lt;span class="MetaTag"&gt;quot&lt;/span&gt;;&lt;span class="MetaTag"&gt;contact&lt;/span&gt;_us&amp;amp;&lt;span class="MetaTag"&gt;quot&lt;/span&gt;;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;li&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;label&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Name:&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;label&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &amp;lt;%= negative_text_field_tag &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;captcha&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;name&lt;/span&gt; %&amp;gt;
    &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;li&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;li&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;label&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Email:&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;label&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &amp;lt;%= negative_text_field_tag &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;captcha&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;email&lt;/span&gt; %&amp;gt;
    &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;li&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;li&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;label&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Your Comment:&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;label&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &amp;lt;%= negative_text_area_tag &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;captcha&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;body&lt;/span&gt; %&amp;gt;
    &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;li&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;li&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &amp;lt;%= submit_tag %&amp;gt;
    &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;li&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;ul&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&amp;lt;% &lt;span class="Keyword"&gt;end&lt;/span&gt; -%&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Test and enjoy!&lt;/li&gt;&lt;/ol&gt;
  
&lt;h2&gt;Possible Gotchas and other concerns&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;It is still possible for someone to write a bot to exploit a single site by closely examining the DOM.  This means that if you are Yahoo, Google or Facebook, negative captchas will not be a complete solution.  But if you have a small application, negative captchas will likely be a very, very good solution for you.  There are no easy work-arounds to the dom-predictability problem quite yet.  Let me know if you have one.&lt;/li&gt;
&lt;li&gt;I'm not a genius.  It is possible that a bot can figure out the hashed values and determine which fields are which.  I don't know how, but I think they might be able to.  I welcome people who have thought this out more thoroughly to criticize this method and help me find solutions.  I like this idea a lot and want it to succeed.&lt;/li&gt;&lt;/ul&gt;

&lt;h2&gt;Credit&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;The idea for negative captchas is not mine.  It originates (I think) from Damien Katz of CouchDB.  &lt;/li&gt;
&lt;li&gt;I (Erik Peterson) wrote the plugin.  Calvin Yu wrote the original class which I refactored quite a bit and made into the plugin.&lt;/li&gt;
&lt;li&gt;We did this while working on Skribit, an Atlanta startup (if you have a blog, please check us out at &lt;a href="http://skribit.com/"&gt;http://skribit.com/&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;If you have any questions, concerns or have found any bugs, please email me at erik [at] subwindow dotcom
&lt;/li&gt;
&lt;li&gt;If you'd like to help improve or refactor this plugin, please create a fork on Github and let me know about it.  &lt;a href="http://github.com/subwindow/negative-captcha"&gt;http://github.com/subwindow/negative-captcha&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</description>
      <pubDate>Wed, 11 Jun 2008 05:44:10 GMT</pubDate>
      <guid>http://subwindow.com/articles/18</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Those Pesky Expectations</title>
      <link>http://subwindow.com/articles/17</link>
      <description>&lt;p&gt;I live in Atlanta.  A major city in one of the most prosperous nations in the world.  I can't get WiMax.&lt;/p&gt;

&lt;p&gt;You know where you can get WiMax service?  &lt;a href="http://www.internetghana.com/skyburst.htm"&gt;Ghana&lt;/a&gt;.  A third world country in Africa.&lt;/p&gt;

&lt;p&gt;I was pondering a vacation out there just after the rainy season- December or so- and was wondering about Internet access in the country.  I figured I'd be lucky to catch DSL-like speeds once in a blue moon.  It turns out that not only can I get DSL-like speeds, but I can get them with a mobile device, and I can get them for almost &lt;b&gt;half&lt;/b&gt; the cost of my (slower) CDMA wireless card.&lt;/p&gt;

&lt;p&gt;Be careful with the expectations you carry about other people and places.  Just because you assume you're in a good position, doesn't mean that you actually are.  Challenge your expectations every once in awhile.  Go to Ghana.  See how fast and cheap their Internet access is.&lt;/p&gt;</description>
      <pubDate>Sat, 10 May 2008 05:26:04 GMT</pubDate>
      <guid>http://subwindow.com/articles/17</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Sometimes, the little things count</title>
      <link>http://subwindow.com/articles/16</link>
      <description>&lt;pre class="blackboard"&gt;&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;TrueClass&lt;/span&gt;
  &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;to_exclamation&lt;/span&gt;
    &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;Yes&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;FalseClass&lt;/span&gt;
  &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;to_exclamation&lt;/span&gt;
    &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;No&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

(&lt;span class="Constant"&gt;1&lt;/span&gt;&lt;span class="Keyword"&gt;+&lt;/span&gt;&lt;span class="Constant"&gt;2&lt;/span&gt; &lt;span class="Keyword"&gt;==&lt;/span&gt; &lt;span class="Constant"&gt;3&lt;/span&gt;).&lt;span class="Entity"&gt;to_exclamation&lt;/span&gt;
=&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;Yes&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
(&lt;span class="Constant"&gt;1&lt;/span&gt;&lt;span class="Keyword"&gt;+&lt;/span&gt;&lt;span class="Constant"&gt;1&lt;/span&gt; &lt;span class="Keyword"&gt;==&lt;/span&gt; &lt;span class="Constant"&gt;4&lt;/span&gt;).&lt;span class="Entity"&gt;to_exclamation&lt;/span&gt;
=&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;No&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;This demonstrates two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Small things &lt;i&gt;can&lt;/i&gt; matter.  Even something that is seemingly inconsequential can turn into something pleasant.&lt;/li&gt;
&lt;li&gt;Just because something (IE monkeypatching) &lt;i&gt;can&lt;/i&gt; be harmful, doesn't mean it always is.&lt;/li&gt;
&lt;/ol&gt;</description>
      <pubDate>Mon, 28 Apr 2008 22:36:16 GMT</pubDate>
      <guid>http://subwindow.com/articles/16</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Thinking about yield(self)</title>
      <link>http://subwindow.com/articles/15</link>
      <description>&lt;p&gt;Maybe my brain is broken, but when using blocks in Ruby, I keep wanting to have access to the scope of the calling object:&lt;/p&gt;
&lt;pre class="blackboard"&gt;&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Foo&lt;/span&gt;
  &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;bar&lt;/span&gt;
    baz &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;qux&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Keyword"&gt;yield&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Keyword"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="Support"&gt;Foo&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;.&lt;span class="Entity"&gt;bar&lt;/span&gt; { puts baz }
&lt;span class="Variable"&gt;NameError&lt;/span&gt;: undefined local variable &lt;span class="Keyword"&gt;or&lt;/span&gt; method &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;baz&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;for&lt;/span&gt; main&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;Object&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;Which makes sense, of course.  But what if I really needed to know what baz was, in the context of the block?  (Assume that I'm determining my accessing needs at runtime, and can't add arguments willy-nilly to the yield call).  The solution I came up with was to use &lt;code style="display: inline"&gt;yield(self)&lt;/code&gt;.  It seems kind of dirty, and it is- but it works.  Mostly.&lt;/p&gt;
&lt;pre class="blackboard"&gt;&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Foo&lt;/span&gt;
  &lt;span class="Keyword"&gt;attr_accessor&lt;/span&gt; &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;accessible_baz&lt;/span&gt;
  &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;bar&lt;/span&gt;
    baz &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;qux&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;instance_baz&lt;/span&gt; &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;qux&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;accessible_baz&lt;/span&gt; &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;qux&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Keyword"&gt;yield&lt;/span&gt;(&lt;span class="Variable"&gt;self&lt;/span&gt;)
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;Attempt to get the local variable&lt;/span&gt;
&lt;span class="Keyword"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="Support"&gt;Foo&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;.&lt;span class="Entity"&gt;bar&lt;/span&gt; {|&lt;span class="Variable"&gt;container&lt;/span&gt;| puts container.&lt;span class="Entity"&gt;baz&lt;/span&gt; }
&lt;span class="Variable"&gt;NameError&lt;/span&gt;: undefined local variable &lt;span class="Keyword"&gt;or&lt;/span&gt; method &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;baz&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;for&lt;/span&gt; main&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;Object&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;Attempt to get an instance variable without an accessor&lt;/span&gt;
&lt;span class="Keyword"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="Support"&gt;Foo&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;.&lt;span class="Entity"&gt;bar&lt;/span&gt; {|&lt;span class="Variable"&gt;container&lt;/span&gt;| puts container.&lt;span class="Entity"&gt;instance_baz&lt;/span&gt; }
&lt;span class="Variable"&gt;NoMethodError&lt;/span&gt;: undefined method &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;instance_baz&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;for&lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="Keyword"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="Support"&gt;Foo&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;.&lt;span class="Entity"&gt;bar&lt;/span&gt; {|&lt;span class="Variable"&gt;container&lt;/span&gt;| puts container.&lt;span class="Entity"&gt;instance_variable_get&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;@instance_baz&lt;/span&gt;) }
qux

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;Attempt to get an instance variable with an accessor&lt;/span&gt;
&lt;span class="Keyword"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="Support"&gt;Foo&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;.&lt;span class="Entity"&gt;bar&lt;/span&gt; {|&lt;span class="Variable"&gt;container&lt;/span&gt;| puts container.&lt;span class="Entity"&gt;accessible_baz&lt;/span&gt; }
qux
&lt;/pre&gt;

&lt;p&gt;I think this might be an OK solution if you're just trying to get at methods or accessible instance variables of the calling object, but I'm not sure it is worth it to go through &lt;code style="display: inline"&gt;instance_variable_get&lt;/code&gt; in order to get other instance variables.  Is there a better way to get at this stuff?  Any method that is more elegant?&lt;/p&gt;</description>
      <pubDate>Thu, 10 Apr 2008 20:50:33 GMT</pubDate>
      <guid>http://subwindow.com/articles/15</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>The ERP Money-Back Guarantee</title>
      <link>http://subwindow.com/articles/14</link>
      <description>&lt;p&gt;In my &lt;a href="http://subwindow.com/articles/13"&gt;last post&lt;/a&gt;, I detailed some of the problems with the existing ERP vendor ecosystem.  In this post, I want to go over &lt;i&gt;why&lt;/i&gt; these ERP vendors fail, and what would be necessary for a successful ERP implementation.&lt;/p&gt;

&lt;blockquote&gt;The problem is realizing that if the software to automate a business process is complicated and contradictory and hard to use, then the real cause is a business process that is complicated and contradictory and probably not serving the company well.&lt;/blockquote&gt;

&lt;div style="text-align: right"&gt;--&lt;a href="http://weblog.raganwald.com/2008/03/process-is-to-software-as-software-is.html"&gt;Raganwald&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;Reginald (can I call him that?) hits the nail on the head.  If you haven't read that article, you really should- it does quite a good job at describing this problem. &lt;/p&gt;

&lt;p&gt;Applied to ERP systems, the problem is that ERP vendors strive to have an "out-of-the-box" solution, that with "minimal customization" can be applied to any business.  What an incredible crock of shit.  This, right here- that one sentence, describes the failure of these ERP vendors more than anything.  Sure, the salesmen are slimeballs and they send out junior programmers to wrestle these huge systems into conformance.  But this is their one grevious error.  Trying to pretend that putting an ERP system into place is anything less than a &lt;b&gt;complete reinvention of nearly every operating process of the business&lt;/b&gt; is a disservice.&lt;/p&gt;

&lt;p&gt;All too often, when trying to automate a business process, software developers take a look at the current (non-automated) system, and merely replicate that in software.  This usually entails complicated approval loops, unnecessary data entry, and human gating.  This is so, so, so wrong.  The problem with the current state of most business software is it asks the question &lt;b&gt;What?&lt;/b&gt; when it should be asking the question &lt;b&gt;Why?&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;A common scenario in business software is requiring approval for something.  Purchasing, in particular, is fraught with this bug.  Most organizations have a system set up where purchases need an increasing amount of approval for an increasing dollar value.  This makes sense, right?  You don't want someone buying $100,000 worth of materials completely unchecked.  In this situation (and in most approval/gating situations), the process needs to be switched from an &lt;b&gt;approval&lt;/b&gt; situation to a &lt;b&gt;monitoring&lt;/b&gt; situation.  Instead of requiring the approval of the COO for purchases over $50,000, why not shoot him an email detailing the purchase, and he can intervene if something is fishy?  The same objective is attained- notification and approval, but this time it is implicit approval, which is no less powerful.&lt;/p&gt;

&lt;p&gt;This kind of rethinking of the method is beneficial from both sides.  The business benefits because the process is much less manual- any time you can remove a human step from a process the whole organization becomes more efficient.  It also benefits the software developer because, all of a sudden, the software is much more simple.  Instead of an email notification, an approval/rejection interface, and the appropriate state gating, now you just have the email notification (and maybe a rejection interface).  This may seem like a small win, but replicate this win 100 times over the course of an implementation, and the system as a whole is dramatically &lt;i&gt;smaller&lt;/i&gt; (and we all know, smaller code bases have fewer bugs), and the processes mapped are dramatically more &lt;i&gt;efficient&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a style="float: right; margin-left: 5px;" href="http://www.flickr.com/photos/sylvar/127974313/"&gt;&lt;img src="http://images.subwindow.com/culdesac.jpg"&gt;&lt;/a&gt;In general, the process of implementing an ERP system is considered a one-way street.  The business comes up with requirements, and the vendor/consultant comes up with a system that (theoretically) fulfills those requirements.  It should be a two-way street.  No, screw that, it should be a freaking &lt;b&gt;cul-de-sac&lt;/b&gt;.  The business and developers need to work together to create a set of requirements that doesn't map the processes as they are, but maps the processes &lt;b&gt;as they should be.&lt;/b&gt;&lt;p&gt;

&lt;p&gt;This might seem like a sketchy proposition from the developer's perspective- "Who am I to tell these guys they need to rework all of their processes?"  I don't think this is a problem as long as it is established from the front.  The people selling the service need to be frank about the need to rework every process that the ERP touches from the ground up.  It might be a hard sell, but once you can demonstrate the advantages of this approach (and the perils of not reworking processes), I think companies might be willing to play ball.&lt;/p&gt;

&lt;p&gt;And playing ball is just what this is- the vendor and the client really should be on the same team.  I know this sounds like management dreck, but I'm serious.  Instead of the developers fighting the clients to come up with requirements and the clients fighting the developers to implement them, everyone needs to understand that it is in each other's best interests to help the other succeed.&lt;/p&gt;

&lt;p&gt;Why not take it one step further?  Why don't ERP vendors say at the start of the project: "If you're not happy with us a year from now, we'll give you your money back."  That sentence sounds like sacrilege in the current environment, but why not have a money-back guarantee?  After all, almost every single failure scenario is essentially the fault of the vendor.  The common defense for vendors is that the client didn't specify the requirements correctly, or some variation thereof.  Hogwash.  It is the responsibility of the vendor to know that the requirements the client gives them are worthless.  It is the responsibility of the vendor to cut through that and figure out what the &lt;i&gt;real&lt;/i&gt; requirements are, and to work with the client to make sure that their processes are going to take full advantage of the system.&lt;/p&gt;

&lt;p&gt;An ERP vendor that isn't willing to offer a money-back guarantee is one that has no financial interest in the success of the implementation.  After all, a failed implementation that takes two years will probably net them &lt;i&gt;more&lt;/i&gt; money than a successful one that lasts 15 months.  Shortening  this:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;An ERP vendor that isn't willing to offer a money-back guarantee is one that doesn't care if the implementation succeeds.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;That's absurd.  I'm really astonished that the market has gotten this far without realizing this.  A credible company that comes into this market that offers this will not only win, but will win in the kind of way that changes the entire industry.&lt;/p&gt;

&lt;p&gt;I'm game for that.&lt;/p&gt;</description>
      <pubDate>Wed, 09 Apr 2008 06:39:41 GMT</pubDate>
      <guid>http://subwindow.com/articles/14</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Enterprise Resource Planning: Mass epic... Opportunity.</title>
      <link>http://subwindow.com/articles/13</link>
      <description>&lt;p&gt;I've been thinking about Enterprise IT for a long time, but haven't really verbalized my thoughts.  After reading and commenting on a &lt;a href="http://blog.obiefernandez.com/content/2008/03/sap-sued-for-ty.html"&gt;post&lt;/a&gt; on Obie's blog, I think I'm ready to put some of these thoughts down.&lt;/p&gt;

&lt;p&gt;Any sufficiently large business needs some kind of an ERP- a cohesive software system that essentially manages all of the data of the business.  Doing without an ERP system is possible, but leads to a crazy amount of excel spreadsheets and paper pushers.  Efficiency at a large scale is, nearly without exception, impossible without such a system. [Note that whether it is called an ERP or not, some kind of cohesive data management system is necessary.  Google, for instance, essentially has an ERP- they just don't call it that.]  There are certain industries where a business can survive without this kind of a system, but obviously companies in these industries will left out of the conversation.&lt;/p&gt;

&lt;p&gt;Also, ERP implementations are notoriously expensive.  For a large business (&gt;50M/yr revenue), ERP implementations regularly pass the $5M mark.  Estimates for the cost of these things are usually a little lower- $2 or $3 million seems to be the common estimate.  However, once the vendors screw the pooch enough, businesses will be lucky to get out of there losing less than $10M, all things considered.  ERP "vendors" are notoriously bad, in every sense of the word.  They are slow, they are liars, they are expensive, and they fail way too often.&lt;/p&gt;

&lt;p&gt;Some research has been done on ERP implementation failure rates, and the common wisdom is that only 30% of ERP implementations succeed.  Even the rosiest studies propagated by ERP vendors talk about 50% success rates.  Can this possibly be true?  Take a look at this from the perspective of a business:&lt;/p&gt;

&lt;blockquote&gt;I need this system or else my business is likely to die due to inefficiency.  Implementing such a system will cost five million dollars and take about two years.  At the end of this five million dollars and two years, I have a 70% chance of walking away with nothing.&lt;/blockquote&gt;

&lt;p&gt;Man.  That sounds like a really raw deal.  If I were running a large company, I would seriously resist taking such a risk.  It is no wonder most large businesses are still pinnacles of inefficiency- getting rid of the fat is a slow, expensive, and insanely risky proposition. &lt;/p&gt;

&lt;p&gt;It goes even beyond this, however.  Even if these ERP implementations are successful- i.e., the business somehow doesn't &lt;a href="http://blogs.zdnet.com/projectfailures/?p=583"&gt;declare bankruptcy&lt;/a&gt; as a result of the ERP implementation- the systems are still really, terribly awful.  They are horribly unusable systems that are slow, impossible to change, and a huge burden on 90% of the company.  Just look at the &lt;a href="http://us-old.ortec.com/media/content/screenshot_connect.jpg"&gt;screenshots&lt;/a&gt; for some of these systems.  Do a Google search for "SAP Screenshot" (or substitute any other ERP vendor, you'll get pretty much the same thing.)  Do any of these look like systems you'd want to use?&lt;/p&gt;

&lt;p&gt;So why are these ERP vendors still in business?  Because even a slow, horrible, unusable ERP system is an incredibly huge win for a company.  There are two reasons for this.  First, the amount of power these systems put at the fingertips of decision makers is incredible.  Essentially, the data provided by an ERP gives managers ammunition to overcome the status quo in order to make necessary improvements.  Never underestimate the power of something that helps a business overcome internal equilibrium.  Second, ERP systems nearly completely eliminate the need for paper pushers.  Paper pushers, in a pre-ERP company, serve a vital role as a check on abuse and (ironically) waste.  Managers simply cannot keep track of all of the activities of the company, and the paper pushers are set up as gates so that they can prevent things from going haywire.  Once managers have the ability to keep an eye on things themselves and with software acting as this gate, paper pushers become largely unnecessary.  The result?  A much more efficient and strategically powerful organization.&lt;/p&gt;

&lt;p&gt;Let us recap all of this so far:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;There's a product which tens of thousands of businesses need, or else they will die.&lt;/li&gt;
  &lt;li&gt;Businesses are willing to pay insane sums of money for this product.&lt;/li&gt;
  &lt;li&gt;Existing suppliers are failing.  Hard.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You know what that smells like to me?  Opportunity.&lt;/p&gt;

&lt;p&gt;Next, I'll cover the reasons behind the endemic failure of ERP vendors, and how that can be fixed and profited from.&lt;/p&gt;</description>
      <pubDate>Fri, 28 Mar 2008 06:03:28 GMT</pubDate>
      <guid>http://subwindow.com/articles/13</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Startups and "Ownership of the Idea"</title>
      <link>http://subwindow.com/articles/12</link>
      <description>          &lt;p&gt;I want to talk for a minute on why hackers should only work on their own ideas. &lt;/p&gt;
           &lt;p&gt;Simply put, it is a question of motivation.  In almost any startup, 90% of the early effort is product development.  Therefore, it is unquestionably the best when the people developing the product are well motivated.  In a software startup, the hackers are the ones developing the product, and it is nearly impossible for a hacker to feel motivated to work hard for an idea that is not theirs. &lt;/p&gt;
            &lt;p&gt;It is a question of &lt;b&gt;ownership of the idea&lt;/b&gt;.  It isn't a question of equity, or money, or quality of the idea or business plan.  If I'm working on an idea that is somebody else's, the natural dynamic is for me to feel like the employee, and for the idea creator to feel like the employer.  I don't want to be an employee- I've got my day job for that.  Anybody busting their ass until 2 AM with little or no pay isn't going to bust their ass very long feeling like an employee.
             &lt;p&gt;With Apple, the idea for the company was Wozniak's.  He was a hacker, and he had made this cool computer that he was proud of.  As he was showing it off to some people, Jobs saw it and together they started to sell it.  Imagine how far Apple would have gotten if Jobs had gone up to Woz and said "Hey, man, I've got this killer idea for a computer- would make it for me?  I'll give you some equity!"  It wouldn't have happened.  The reason Woz was so dedicated is because it was &lt;b&gt;his&lt;/b&gt; computer. &lt;/p&gt;
              &lt;p&gt;The exact same thing happened with Microsoft.  Gates and Allen created a product, and only &lt;b&gt;five years later&lt;/b&gt; did they get Ballmer to help them sell it.  Software startups these days actually aren't all that different than Apple or Microsoft were in the 70's.  You need someone- a Wozniak- to build the product.  And only &lt;b&gt;after&lt;/b&gt; that, do you need someone- a Jobs- to do all the rest.  Hackers, no matter how skilled in business (myself included), will eventually need a "beef-headed M.B.A." to help them.     &lt;/p&gt;
               &lt;p&gt;There's a big difference, here, between a startup in development mode and a startup in sales mode.  Startups in development mode don't need a business guy.  In fact, it is probably harmful for a development-mode startup to have someone involved who is not working on the product.  However, once the startup starts making money, a business type is necessary.  Someone is needed to pursue funding, to balance the books, and to manage the marketing effort.  Hackers may, technically, be capable of these tasks- but shouldn't do them, because their heart is in the product.  A startup in sales mode needs somebody whose heart is &lt;b&gt;not&lt;/b&gt; in the product- someone to make clear, unbiased and responsible decisions that benefit the whole company, and not just the product.
                &lt;p&gt;At the absolute beginning stages of a software startup, two things are needed: a hacker (or two, or three), and his (or their) idea.  Anything else is just an impediment. &lt;/p&gt;

</description>
      <pubDate>Wed, 26 Mar 2008 02:16:07 GMT</pubDate>
      <guid>http://subwindow.com/articles/12</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>On Teams</title>
      <link>http://subwindow.com/articles/10</link>
      <description>&lt;p&gt;&lt;b&gt;So, here’s a question:&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;What’s the ideal size of a software team?&lt;/p&gt;

&lt;p&gt;Most people, I don’t care how long they’ve studied the “art” of software development, will come up with a vague answer.  “Six to 10” some might say.  “About Eight.”  “No more than 12.”  "Five or Six, I guess.”  These are pretty common answers.  If pressed to come up with a rationale behind them, you might hear some rumbling about Mythical Man Months, but other than that, nobody really knows why they think that “Roughly Six” is a good number for team size.&lt;/p&gt;

&lt;p&gt;Let me take a different approach.  Forget, for a minute, about software development.  Forget that this is a programming blog.&lt;/p&gt;

&lt;p&gt;What’s the ideal size of a team of humans organized to complete a common objective?&lt;/p&gt;

&lt;p&gt;Looking at the whole of human existence, wherever humans have to organize to achieve objectives, they always organize in groups of four or five.  Four is a bit more common than five.   Three is less common.  Six is much less common.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Where does this happen?&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;The military: the smallest unit in the military is the fire team.  four to five people.  &lt;/p&gt;

&lt;p&gt;Baseball: Yes, baseball teams have 25 players on a roster and nine players on the field at a time.  However, a team can be divided into smaller units based on their objectives on the field.  The top of a baseball lineup is four players, from leadoff to cleanup.  The infield is four players whose goal is to prevent balls from leaving the infield.  The outfield is three players who all shag fly balls.&lt;/p&gt;

&lt;p&gt;Basketball: Each team has five players on the court at a time.&lt;/p&gt;

&lt;p&gt;Soccer: Four defenders. four midfielders. four forwards.  The set of four is so important for Soccer, that even though they are limited to 11 players on the field (10 position, one goalie), players will move from team to team so that whichever task is most important at the time (defense, offense), that task will always have at least four (and sometimes five) players working towards it.&lt;/p&gt;

&lt;p&gt;Music:  The vast majority of bands have four or five members.  three and six are uncommon.  Rarity roughly increases with size.  Not just with modern music- the string quartet has long been one of the most popular musical group types.  Even in large orchestras, they’re broken down into smaller pieces.  Two groups of five violins in First and Second violins.  Two groups of four cellos.  Three Clarinets.  Three Bassoons. Four or five trumpets.&lt;/p&gt;

&lt;p&gt;Families: Most families these days have four or five members.  Families in history had more members, but that is because they were organized around disparate objectives.  Families aimed towards four men (3 boys and one father) to tend the field, and four women (3 girls and one mother) to tend the house.  Since humans used to die easily and sex is a coin toss, this ideal team was rarely achieved.  However, it was clearly the goal.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Why Does this Happen?&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Ok, enough with the non-software comparisons.  It should be pretty obvious that the ideal size of a group of humans organized around a single objective is four or five (with three occasionally being preferred).  So why do software teams so often exceed this number?&lt;/p&gt;

&lt;p&gt;I think the reason is that it is hard to define roles for software developers.  In a band, you have very clearly defined roles- one drummer, one or two  guitarists, one or two  vocalists, and one bassist.  Very rarely is this arrangement deviated from.  Similar in sports- each player has a very defined role- 3rd baseman, point guard, center forward, sweeper.  In families the role is defined by age.&lt;/p&gt;

&lt;p&gt;What about roles in a software development team?  Sometimes you’ll have a senior developer, but the role division usually doesn’t go any farther than that.  I’m quite convinced at this point that each member in a software team needs a clearly defined role, and the size of the team will naturally work itself out.&lt;/p&gt;

&lt;p&gt;The roles probably depend on the type of project.  For a database-backed web application a good setup might be: Lead, Quality, UI, Data.  With each member in that team you have a really good definition of what the role of the person is.  If a certain need isn’t being met properly by the defined roles, then you add another person.  The existing members naturally resist adding new members when there isn’t a role for them.  Team size reaches automatic stasis. &lt;/p&gt;

&lt;p&gt;Now, this is obviously pure conjecture at this point.  I look forward to putting it in practice and watching how this approach actually shakes out.  In the mean time, has anyone had experience with software development with tightly defined roles?  How did this effect team size?&lt;/p&gt;
</description>
      <pubDate>Fri, 14 Mar 2008 20:20:50 GMT</pubDate>
      <guid>http://subwindow.com/articles/10</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>What's so DRY about Rails?</title>
      <link>http://subwindow.com/articles/9</link>
      <description>&lt;p&gt;One of the main talking points for Rails evangelism states that it is really DRY.  You don't have to repeat yourself.  This might be true in comparison to other languages, but when you really get down to it, it isn't very DRY at all.  Take a look at your own workflow for common tasks with Rails development, and while line-by-line the code may be DRY, from a conceptual level it isn't at all.&lt;/p&gt;

&lt;h2&gt;Common task:  Adding a column&lt;/h2&gt;
&lt;p&gt;I need to add in a column for Parts that will hold the Reference number.  It is a required column.  Here's what my workflow for this kind of task:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;code&gt;script/generate migration parts_add_reference&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Open up the migration, add in:&lt;br /&gt;
		&lt;code&gt;add_column :parts, :reference, :string, :limit =&gt; 40, :null =&gt; false&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;Figure out some way (possibly in the migration) of dealing with old records that don't have this new required record.  Figure out a way to handle this gracefully both with development/testing fixtures and production.&lt;/li&gt;
  &lt;li&gt;Add in a test to verify that the validation is there &lt;br /&gt;
    &lt;code&gt;assert_validation(Part, :presence, :reference, {:good =&gt; ["XX-1234"]})&lt;/code&gt;&lt;br /&gt;
    (This uses a test helper package that I may or may not release in the future.  Implementation is not important.  The important part is that it asserts that the validates_presence_of validation is there.)&lt;/li&gt;
  &lt;li&gt;Run the test.  Test fails.&lt;/li&gt;
  &lt;li&gt;Add in:&lt;br /&gt;
    &lt;code&gt;validates_presence_of :reference&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Run the test.  Test passes.&lt;/li&gt;
  &lt;li&gt;Add in the functional test to make the field is displayed in the new/edit forms:&lt;br /&gt;
      &lt;code&gt;assert_tag(:input, :attributes =&gt; {:type =&gt; :text, :id =&gt; "part_reference", :name =&gt; "part[reference]"})&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Run the test.  Test fails.&lt;/li&gt;
  &lt;li&gt;Add in the code for the form:
&lt;code&gt;&lt;pre&gt;
&amp;lt;p&amp;gt;
  &amp;lt;b&amp;gt;Reference&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;
  &amp;lt;%= f.text_field :reference -%&amp;gt;
&amp;lt;/p&amp;gt;
&lt;/pre&gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Run the test. Test passes.&lt;/li&gt;
  &lt;li&gt;Add in the functional test to make sure the field is displayed in the show page:&lt;br /&gt;
    &lt;code&gt;assert_tag(:div, :attributes =&gt; {:class =&gt; 'property', :id =&gt; 'part_reference'})&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Run the test.  Test fails.&lt;/li&gt;
  &lt;li&gt;Add in the code for the show page:&lt;br /&gt;
    &lt;code&gt;&amp;lt;div id="property" id="part_reference"&amp;gt;&amp;lt;b&amp;gt;Reference:&amp;lt;/b&amp;gt; &amp;lt;%= @part.reference %&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Run the test.  Test passes.&lt;li&gt;
  &lt;li&gt;Run through all of this in a browser, testing it yourself.&lt;/li&gt;
  &lt;li&gt;Run the entire test suite.&lt;/li&gt;
  &lt;li&gt;Commit to source control:&lt;br /&gt;
    &lt;code&gt;git add db/migrate/XXX_parts_add_reference.rb &amp;&amp; git-commit -a&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Deploy: &lt;br /&gt;
    &lt;code&gt;cap deploy:migrations&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Check the live version, praying everything went OK.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Jesus fucking Christ.  I have to specify the column in 4 different places: the migration, the model, the tests, and the view.  Absolutely unacceptable.  If I have to change the name of the column from "reference" to "part_number", it would be like performing an appendectomy.  Putting it in in the first place requires 21 discrete steps. Just to add in one stinking column?  Why in the hell can't it be four?&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Write the code:&lt;br /&gt;
    &lt;code&gt;shows :reference, :required =&gt; true, :size =&gt; 40&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Test.&lt;/li&gt;
  &lt;li&gt;Commit.&lt;/li&gt;
  &lt;li&gt;Deploy.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now I know what you're saying: "That's magical web-app candyland!  There's no way we'll ever get to that level! Even if we could, we wouldn't want to!"  Bullshit.  I think it would be fucking &lt;b&gt;Stellar&lt;/b&gt; if someone came out with a web app framework that distilled development down to this tight cycle.  Write one line of code. Test. Commit. Deploy.  Four steps.  That's all you need.&lt;/p&gt;

&lt;p&gt;Stay tuned...&lt;/p&gt;

</description>
      <pubDate>Thu, 06 Mar 2008 02:48:40 GMT</pubDate>
      <guid>http://subwindow.com/articles/9</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Back By Popular Demand: Salesforce scraper</title>
      <link>http://subwindow.com/articles/8</link>
      <description>&lt;p&gt;In a previous incarnation of this here blog, I had a post about how to construct an app in Rails that scrapes Salesforce.com data to a local database.  This was about a year and a half ago, and the blog that it was posted on has been defunct for at least a year now.  To this day I still get requests for that post, so I figure I should repost it.  A lot of things have changed in that year and a half, however.  My project went from "integrate with Salesforce so we can get better reports" to "Replace Salesforce.com and then some."  Therefore, I haven't used the code I had written about in a long time.  I have no idea if it even still works.  So while my use-case for this code doesn't exist any more, it is apparently useful to other people.&lt;/p&gt;

&lt;p&gt;First off, this uses the activesalesforce gem.  I think the last version that I tested it on was 1.0.0, so any updates might break this code.  Also, there's apparently a activerecord-activesalesforce-adapter gem now, which works with Rails 2.0.  Therefore, things are apparently much different these days.  However, this code will probably give you some insight.&lt;/p&gt;

&lt;h2&gt;Database.yml&lt;/h2&gt;
&lt;pre class="blackboard"&gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;sf_production&lt;/span&gt;&lt;span class="MetaTag"&gt;:&lt;/span&gt;&lt;/span&gt;
  &lt;span class="String"&gt;&lt;span class="Entity"&gt;adapter&lt;span class="Entity"&gt;:&lt;/span&gt;&lt;/span&gt; &lt;span class="String"&gt;activesalesforce&lt;/span&gt;&lt;/span&gt;
  &lt;span class="String"&gt;&lt;span class="Entity"&gt;url&lt;span class="Entity"&gt;:&lt;/span&gt;&lt;/span&gt; &lt;span class="String"&gt;https://www.salesforce.com/services/Soap/u/8.0&lt;/span&gt;&lt;/span&gt;
  &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;username&lt;/span&gt;&lt;span class="MetaTag"&gt;:&lt;/span&gt; &lt;/span&gt;&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;your salesforce username&lt;/span&gt;
  &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;password&lt;/span&gt;&lt;span class="MetaTag"&gt;:&lt;/span&gt; &lt;/span&gt;&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;your salesforce password&lt;/span&gt;

&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;production&lt;/span&gt;&lt;span class="MetaTag"&gt;:&lt;/span&gt;&lt;/span&gt;
  &lt;span class="String"&gt;&lt;span class="Entity"&gt;adapter&lt;span class="Entity"&gt;:&lt;/span&gt;&lt;/span&gt; &lt;span class="String"&gt;postgresql #You can probably use MySQL or whatever.  I doubt it matters&lt;/span&gt;&lt;/span&gt;
  &lt;span class="String"&gt;&lt;span class="Entity"&gt;database&lt;span class="Entity"&gt;:&lt;/span&gt;&lt;/span&gt; &lt;span class="String"&gt;production&lt;/span&gt;&lt;/span&gt;
  &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;username&lt;/span&gt;&lt;span class="MetaTag"&gt;:&lt;/span&gt; &lt;/span&gt;&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;whatever&lt;/span&gt;
  &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;password&lt;/span&gt;&lt;span class="MetaTag"&gt;:&lt;/span&gt; &lt;/span&gt;&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;whatever&lt;/span&gt;
  &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;host&lt;/span&gt;&lt;span class="MetaTag"&gt;:&lt;/span&gt; &lt;/span&gt;&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;whatever&lt;/span&gt;
&lt;/pre&gt;

&lt;h2&gt;account.rb&lt;/h2&gt;
&lt;pre class="blackboard"&gt;&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Account&lt;span class="Superclass"&gt; &lt;span class="Superclass"&gt;&amp;lt;&lt;/span&gt; ActiveRecord::Base&lt;/span&gt;&lt;/span&gt;
  establish_connection &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;sf_&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Account_Postgres&lt;span class="Superclass"&gt; &lt;span class="Superclass"&gt;&amp;lt;&lt;/span&gt; ActiveRecord::Base&lt;/span&gt;&lt;/span&gt;
  set_primary_key &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;id&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  establish_connection &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  set_table_name &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;accounts&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;

&lt;h2&gt;Opportunity.rb&lt;/h2&gt;
&lt;pre class="blackboard"&gt;
&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Opportunity&lt;span class="Superclass"&gt; &lt;span class="Superclass"&gt;&amp;lt;&lt;/span&gt; ActiveRecord::Base&lt;/span&gt;&lt;/span&gt;
  establish_connection &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;sf_&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Opportunity_Postgres&lt;span class="Superclass"&gt; &lt;span class="Superclass"&gt;&amp;lt;&lt;/span&gt; ActiveRecord::Base&lt;/span&gt;&lt;/span&gt;
  establish_connection &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  set_primary_key &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;id&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  set_table_name &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;opportunities&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;I think you get the point.  You'll need to do the same for any other Salesforce objects you plan on using.  It really sucks having to have the Salesforce version have the normal AR name, and the Postgres version having to have the altered name, but the activesalesforce gem depends on this naming scheme.  Next, Here's a big fat dump of some of the helper methods that I use.  You might want to put this in a file in lib.&lt;/p&gt;

&lt;h2&gt;lib/salesforce_helpers.rb&lt;/h2&gt;
&lt;pre class="blackboard"&gt;&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;Creates a &amp;quot;migration&amp;quot; for a specified Salesforce class.  Eval this in a ActiveRecord::Schema.define() block&lt;/span&gt;
&lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;dumpclass&lt;/span&gt;(&lt;span class="Variable"&gt; aclass &lt;/span&gt;)
  dumpstr &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;  create_table &lt;span class="Constant"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;+&lt;/span&gt; aclass.&lt;span class="Entity"&gt;table_name&lt;/span&gt; &lt;span class="Keyword"&gt;+&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="Constant"&gt;\&amp;quot;&lt;/span&gt;, :id =&amp;gt; false, :force =&amp;gt; true do |t|&lt;span class="Constant"&gt;\n&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="Keyword"&gt;for&lt;/span&gt; column &lt;span class="Keyword"&gt;in&lt;/span&gt; aclass.&lt;span class="Entity"&gt;columns&lt;/span&gt;
    dumpstr &lt;span class="Keyword"&gt;+=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;    t.column &lt;span class="Constant"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;+&lt;/span&gt; column.&lt;span class="Entity"&gt;name&lt;/span&gt; &lt;span class="Keyword"&gt;+&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="Constant"&gt;\&amp;quot;&lt;/span&gt;, &lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="Keyword"&gt;if&lt;/span&gt; column.&lt;span class="Entity"&gt;type&lt;/span&gt;.&lt;span class="Entity"&gt;to_s&lt;/span&gt;.&lt;span class="Entity"&gt;eql?&lt;/span&gt;(&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;text&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class="Keyword"&gt;&amp;amp;&amp;amp;&lt;/span&gt; column.&lt;span class="Entity"&gt;limit&lt;/span&gt;.&lt;span class="Entity"&gt;to_i&lt;/span&gt; &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="Constant"&gt;1000&lt;/span&gt;
      dumpstr &lt;span class="Keyword"&gt;+=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;:string, :limit =&amp;gt; &lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;+&lt;/span&gt; column.&lt;span class="Entity"&gt;limit&lt;/span&gt;
    &lt;span class="Keyword"&gt;else&lt;/span&gt;
      &lt;span class="Keyword"&gt;if&lt;/span&gt; column.&lt;span class="Entity"&gt;type&lt;/span&gt;.&lt;span class="Entity"&gt;nil?&lt;/span&gt;
        dumpstr &lt;span class="Keyword"&gt;+=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;:string, :limit =&amp;gt; 255&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class="Keyword"&gt;else&lt;/span&gt;
        dumpstr &lt;span class="Keyword"&gt;+=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;:&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;+&lt;/span&gt; column.&lt;span class="Entity"&gt;type&lt;/span&gt;.&lt;span class="Entity"&gt;to_s&lt;/span&gt;
      &lt;span class="Keyword"&gt;end&lt;/span&gt;
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
    dumpstr &lt;span class="Keyword"&gt;+=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="Constant"&gt;\n&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
  &lt;span class="Keyword"&gt;return&lt;/span&gt; dumpstr &lt;span class="Keyword"&gt;+&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;  end&lt;span class="Constant"&gt;\n&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;Scrapes a single record from Salesforce to the local database&lt;/span&gt;
&lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;scrape&lt;/span&gt;(&lt;span class="Variable"&gt; aobj&lt;span class="Variable"&gt;,&lt;/span&gt; aclass&lt;span class="Variable"&gt;,&lt;/span&gt; adbclass &lt;/span&gt;)
  &lt;span class="Keyword"&gt;begin&lt;/span&gt;
    new_obj &lt;span class="Keyword"&gt;=&lt;/span&gt; adbclass.&lt;span class="Entity"&gt;new&lt;/span&gt;(&lt;span class="Entity"&gt;convert&lt;/span&gt;(aobj, aclass))
    new_obj.&lt;span class="Entity"&gt;id&lt;/span&gt; &lt;span class="Keyword"&gt;=&lt;/span&gt; aobj.&lt;span class="Entity"&gt;id&lt;/span&gt;
    new_obj.&lt;span class="Entity"&gt;save!&lt;/span&gt;
  &lt;span class="Keyword"&gt;rescue&lt;/span&gt;
    &lt;span class="Keyword"&gt;return&lt;/span&gt; &lt;span class="Constant"&gt;1&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
  &lt;span class="Keyword"&gt;return&lt;/span&gt; &lt;span class="Constant"&gt;0&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;Scrapes a whole Salesforce Object harshly- deleting all the local data, and the dumping.  Good for empty tables&lt;/span&gt;
&lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;hard_update_class&lt;/span&gt;(&lt;span class="Variable"&gt;aclass&lt;span class="Variable"&gt;,&lt;/span&gt; adbclass&lt;/span&gt;)
  count &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Constant"&gt;0&lt;/span&gt;
  adbclass.&lt;span class="Entity"&gt;delete_all&lt;/span&gt;
  &lt;span class="Keyword"&gt;for&lt;/span&gt; aobj &lt;span class="Keyword"&gt;in&lt;/span&gt; aclass.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;all&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;limit&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;0&lt;/span&gt;)
    &lt;span class="Entity"&gt;scrape&lt;/span&gt;(aobj, aclass, adbclass)
    count &lt;span class="Keyword"&gt;+=&lt;/span&gt; &lt;span class="Constant"&gt;1&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
  &lt;span class="Keyword"&gt;return&lt;/span&gt; count
&lt;span class="Keyword"&gt;end&lt;/span&gt;  

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;Scrapes a whole Salesforce Object softly.  Only looks for objects that were created/updated since the last scrape.&lt;/span&gt;
&lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;update_class&lt;/span&gt;(&lt;span class="Variable"&gt;aclass&lt;span class="Variable"&gt;,&lt;/span&gt; adbclass&lt;/span&gt;)
&lt;span class="Comment"&gt;  &lt;span class="Comment"&gt;#&lt;/span&gt;I honestly don't know why I did it this way.  It isn't very DRY.  There must be a reason, so tinker with caution.&lt;/span&gt;
  &lt;span class="Keyword"&gt;begin&lt;/span&gt;
    lastcreated &lt;span class="Keyword"&gt;=&lt;/span&gt; adbclass.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;first&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;order&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;created_date desc&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
    lastmodified &lt;span class="Keyword"&gt;=&lt;/span&gt; adbclass.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;first&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;order&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;last_modified_date desc&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
    &lt;span class="Keyword"&gt;for&lt;/span&gt; aobj &lt;span class="Keyword"&gt;in&lt;/span&gt; aclass.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;all&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;limit&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;0&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;conditions&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;createddate &amp;gt; &lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;+&lt;/span&gt; (lastcreated.&lt;span class="Entity"&gt;created_date&lt;/span&gt; &lt;span class="Keyword"&gt;-&lt;/span&gt; &lt;span class="Constant"&gt;18000&lt;/span&gt;).&lt;span class="Entity"&gt;to_s&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;iso_8601_special&lt;/span&gt;))
      &lt;span class="Entity"&gt;scrape&lt;/span&gt;(aobj, aclass, adbclass)
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
    &lt;span class="Keyword"&gt;for&lt;/span&gt; aobj &lt;span class="Keyword"&gt;in&lt;/span&gt; aclass.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;all&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;limit&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;0&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;conditions&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;lastmodifieddate &amp;gt; &lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;+&lt;/span&gt; (lastmodified.&lt;span class="Entity"&gt;last_modified_date&lt;/span&gt; &lt;span class="Keyword"&gt;-&lt;/span&gt; &lt;span class="Constant"&gt;18000&lt;/span&gt;).&lt;span class="Entity"&gt;to_s&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;iso_8601_special&lt;/span&gt;))
      adbclass.&lt;span class="Entity"&gt;delete&lt;/span&gt;(aobj.&lt;span class="Entity"&gt;id&lt;/span&gt;)
      &lt;span class="Entity"&gt;scrape&lt;/span&gt;(aobj, aclass, adbclass)
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
  &lt;span class="Keyword"&gt;rescue&lt;/span&gt;
    &lt;span class="Keyword"&gt;begin&lt;/span&gt;
      lastcreated &lt;span class="Keyword"&gt;=&lt;/span&gt; adbclass.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;first&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;order&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;created_date desc&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
      lastmodified &lt;span class="Keyword"&gt;=&lt;/span&gt; adbclass.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;first&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;order&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;last_modified_date desc&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
      &lt;span class="Keyword"&gt;for&lt;/span&gt; aobj &lt;span class="Keyword"&gt;in&lt;/span&gt; aclass.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;all&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;limit&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;0&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;conditions&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;created_date &amp;gt; &lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;+&lt;/span&gt; (lastcreated.&lt;span class="Entity"&gt;created_date&lt;/span&gt; &lt;span class="Keyword"&gt;-&lt;/span&gt; &lt;span class="Constant"&gt;18000&lt;/span&gt;).&lt;span class="Entity"&gt;to_s&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;iso_8601_special&lt;/span&gt;))
        &lt;span class="Entity"&gt;scrape&lt;/span&gt;(aobj, aclass, adbclass)
      &lt;span class="Keyword"&gt;end&lt;/span&gt;
      &lt;span class="Keyword"&gt;for&lt;/span&gt; aobj &lt;span class="Keyword"&gt;in&lt;/span&gt; aclass.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;all&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;limit&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;0&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;conditions&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;last_modified_date &amp;gt; &lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;+&lt;/span&gt; (lastmodified.&lt;span class="Entity"&gt;last_modified_date&lt;/span&gt; &lt;span class="Keyword"&gt;-&lt;/span&gt; &lt;span class="Constant"&gt;18000&lt;/span&gt;).&lt;span class="Entity"&gt;to_s&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;iso_8601_special&lt;/span&gt;))
        adbclass.&lt;span class="Entity"&gt;delete&lt;/span&gt;(aobj.&lt;span class="Entity"&gt;id&lt;/span&gt;)
        &lt;span class="Entity"&gt;scrape&lt;/span&gt;(aobj, aclass, adbclass)
      &lt;span class="Keyword"&gt;end&lt;/span&gt;
    &lt;span class="Keyword"&gt;rescue&lt;/span&gt;
      puts &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;Skipping &lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;+&lt;/span&gt; aclass.&lt;span class="Entity"&gt;to_s&lt;/span&gt;
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;Converter for a single object&lt;/span&gt;
&lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;convert&lt;/span&gt; (&lt;span class="Variable"&gt; aobj&lt;span class="Variable"&gt;,&lt;/span&gt; aclass &lt;/span&gt;)
  hash &lt;span class="Keyword"&gt;=&lt;/span&gt; {}
  aobj.&lt;span class="Entity"&gt;attributes&lt;/span&gt;.&lt;span class="Entity"&gt;each&lt;/span&gt; { | &lt;span class="Variable"&gt;key&lt;/span&gt;, &lt;span class="Variable"&gt;value&lt;/span&gt; | 
    hash[key] &lt;span class="Keyword"&gt;=&lt;/span&gt; value &lt;span class="Keyword"&gt;if&lt;/span&gt; aclass.&lt;span class="Entity"&gt;column_names&lt;/span&gt;.&lt;span class="Entity"&gt;include?&lt;/span&gt;(key)
  }
  hash
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;For some reason the Salesforce didn't interpret the ISO 8601 date format spec correctly&lt;/span&gt;
&lt;span class="Support"&gt;ActiveSupport&lt;/span&gt;::&lt;span class="Entity"&gt;CoreExtensions&lt;/span&gt;::&lt;span class="Entity"&gt;Time&lt;/span&gt;::&lt;span class="Entity"&gt;Conversions&lt;/span&gt;::&lt;span class="Entity"&gt;DATE_FORMATS&lt;/span&gt;.&lt;span class="Entity"&gt;merge!&lt;/span&gt;(
  &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;iso_8601_special&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;%Y-%m-%dT%H:%M:%S-05:00&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
)
&lt;/pre&gt;

&lt;p&gt;Ok, whew.  Sorry that was so long.  Now we can go about using these methods. Most of this stuff is in rake tasks.&lt;/p&gt;
&lt;pre class="blackboard"&gt;&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;Create local schema for Account&lt;/span&gt;
&lt;span class="Support"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="Entity"&gt;Schema&lt;/span&gt;.&lt;span class="Entity"&gt;define&lt;/span&gt;() &lt;span class="Keyword"&gt;do&lt;/span&gt;
  &lt;span class="Entity"&gt;eval&lt;/span&gt;(&lt;span class="Entity"&gt;dumpclass&lt;/span&gt;(&lt;span class="Variable"&gt;Account&lt;/span&gt;))
&lt;span class="Keyword"&gt;end&lt;/span&gt;

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;Prefill the data for Account&lt;/span&gt;
&lt;span class="Entity"&gt;hard_update_class&lt;/span&gt;(&lt;span class="Variable"&gt;Account&lt;/span&gt;, &lt;span class="Variable"&gt;Account_Postgres&lt;/span&gt;)

&lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;Update the data for Account&lt;/span&gt;
&lt;span class="Entity"&gt;update_class&lt;/span&gt;(&lt;span class="Variable"&gt;Account&lt;/span&gt;, &lt;span class="Variable"&gt;Account_Postgres&lt;/span&gt;)
&lt;/pre&gt;

&lt;br/&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope that was useful to someone.  I'm sorry that the code is in such a decrepit state.  If you have any questions, just comment here and I'll try to help you out.  I highly encourage anyone to modify/update/cleanup this code and make a Rails plugin or Gem for it.&lt;/p&gt;</description>
      <pubDate>Fri, 01 Feb 2008 23:54:59 GMT</pubDate>
      <guid>http://subwindow.com/articles/8</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Nested methods make me all DRY</title>
      <link>http://subwindow.com/articles/7</link>
      <description>&lt;p&gt;Here's a cool little thing I ran into when investigating &lt;a href="http://mikeferrier.ca/2008/1/28/scoped-struct"&gt;scoped_struct&lt;/a&gt;
  You can nest methods on ruby:&lt;/p&gt;
&lt;pre class="blackboard"&gt;&lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;a&lt;/span&gt;
  &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;b&lt;/span&gt;
    &lt;span class="Keyword"&gt;return&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;b&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;
  That way, when you do
  &lt;code style="display:inline"&gt;a.b&lt;/code&gt;
  you get "b" as the result.  Pretty neat, huh?  This would be much more useful if you used it in classes to shorten up method names.  Going with the example in Mike's blog, let's have a Player class that represents a football player.  We want methods to give us some stats back:&lt;/p&gt;

&lt;pre class="blackboard"&gt;&lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Player&lt;/span&gt;
  &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;fumbles&lt;/span&gt;
    &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;dropped&lt;/span&gt;
      &lt;span class="Constant"&gt;1&lt;/span&gt;
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
    &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;lost&lt;/span&gt;
      &lt;span class="Constant"&gt;2&lt;/span&gt;
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
    &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;recovered&lt;/span&gt;
      &lt;span class="Constant"&gt;3&lt;/span&gt;
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
    &lt;span class="Variable"&gt;self&lt;/span&gt;
  &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;
  This way, you get
  &lt;code style="display: inline"&gt; my_player.fumbles.dropped&lt;/code&gt;
  and
  &lt;code style="display: inline"&gt; my_player.fumbles.received&lt;/code&gt;
  all nested and defined all DRY like.  One little catch is that you have to put that
  &lt;code style="display: inline"&gt; self&lt;/code&gt;
  return right after the method definitions in the
  &lt;code style="display: inline"&gt; fumbles&lt;/code&gt;
  scope.  Otherwise, you'll get all kinds of nil errors.&lt;p&gt;</description>
      <pubDate>Wed, 30 Jan 2008 01:00:22 GMT</pubDate>
      <guid>http://subwindow.com/articles/7</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Git over HTTP walkthrough</title>
      <link>http://subwindow.com/articles/6</link>
      <description>&lt;p&gt;
  This is a separate post to publish how I solved the problem highlighted in my last post.  Specifically, I wanted a read-only Git repository available over HTTP through Apache.  I already had Apache set up and had a
  &lt;a href="http://code.subwindow.com/"&gt; subdomain&lt;/a&gt;
  set up via
    &lt;a href="http://www.apsis.ch/pound/"&gt; Pound&lt;/a&gt;.
  All I needed to do was set up the Git repository so that it could be accessed over HTTP.  Easy, right?  Apparently not.&lt;/p&gt;

&lt;p&gt;
  If someone has an easier solution, or I'm just approaching this problem incorrectly, feel free to let me know.  I'm perfectly open to the idea that I'm a retard.  After all, I am a
    &lt;a href="/articles/2"&gt; terrible programmer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
  Ok, so the deal is that, apparently, for this kind of repository, you can't just use
  &lt;code style="display: inline"&gt;git-init&lt;/code&gt;
  like you could with a normal local or remote repository.
  From what I've gleaned, you have to use
    &lt;code style="display: inline"&gt;git-init-db&lt;/code&gt;
  Which is silly, because the man entries for the two appear to be almost identical.  Again, maybe I'm just a tard, but I really don't understand the difference.&lt;/p&gt;

&lt;p&gt;
  Even after that, git-clone wouldn't work, and I had to run
    &lt;code style="display: inline"&gt;git-update-server-info&lt;/code&gt;
  Ho, ho, ho! But that wouldn't even work!  I had to do
  &lt;code style="display: inline"&gt;git-update-server-info --force&lt;/code&gt;
  for some ungodly reason.
  %i Keep in mind, this is still on a completely empty repository.&lt;/p&gt;

&lt;p&gt; Ok, so without further ado, here are the steps to get it actually working:&lt;/p&gt;
&lt;pre class="blackboard"&gt;mkdir myrepo.git
GIT_DIR=myrepo.git git-init-db
cd myrepo.git/
chmod +x *
git-update-server-info --force
&lt;/pre&gt;

&lt;p&gt; Not exactly straightforward.&lt;/p&gt;

</description>
      <pubDate>Fri, 25 Jan 2008 07:47:58 GMT</pubDate>
      <guid>http://subwindow.com/articles/6</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>On Git</title>
      <link>http://subwindow.com/articles/5</link>
      <description>&lt;p&gt;
  Over the past few weeks, I've been evaluating
  &lt;a href='http://git.or.cz/'&gt;Git&lt;/a&gt;.
  I'm sure you have heard of it, but in case you haven't, I'll give it a quick run down.  Git is the leading distributed version control system out there, mainly because it is what is used for the Linux kernel and Linus has been doing some
  &lt;a href='http://video.google.com/videoplay?docid=-2199332044603874737'&gt;pretty heavy promotion&lt;/a&gt;
  of it.  I figured I would try it, seeing as I'm a sucker for things that are hyped (and Git has definitely gotten its fair share of hype lately from all over the blogosphere).
&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;
  Git was pretty easy to install.  I downloaded and compiled the source, and it went really well.  There is also a
  &lt;a href='http://metastatic.org/text/Concern/2007/08/18/git-package-for-mac-os-x/'&gt;unofficial binary&lt;/a&gt;
  available, if you're afraid of
  &lt;code&gt;make&lt;/code&gt;.
  There aren't (to my knowledge) any nifty GUI tools for Git, or a Textmate bundle.  But that hasn't bothered me much- I never used the Subversion equivalents.  It always seemed faster to do it via the command line (while occasionally piping
  &lt;code&gt;svn diff&lt;/code&gt;
  to Textmate).
&lt;/p&gt;
&lt;h3&gt;Learning Curve&lt;/h3&gt;
&lt;p&gt;Overall, Git was really easy to understand.  Setting up a repository and committing things locally is pretty much identical to Subversion.  Setting up a remote repository and pushing to it was also pretty easy, and about on par with an svn+ssh setup.  Getting Git to work with Capistrano was also surprisingly easy.  It took about 20 minutes total to read up, install and start working with my basic Git setup.&lt;/p&gt;
&lt;h3&gt;Some bumps&lt;/h3&gt;
&lt;p&gt;
  I wanted to set up a
  &lt;a href='http://code.subwindow.com/'&gt;public-facing code repository&lt;/a&gt;
  that would make it more likely that I would share some of my code.  I figured that this would be a strong suit of Git since it is all about sharing and its decentralized nature means that pretty much every contributor is going to have their own publicly-available repository.  However, I was sorely mistaken.  The online FAQs for setting up this kind of repository over http were sparse, incorrect, or both.  After 3 different tries and about two hours of work, I finally got one working.  I'm still not sure if it is set up the right way, but at least
  &lt;code&gt;git clone&lt;/code&gt;
  works, and that's what really matters.
&lt;/p&gt;
&lt;p&gt;
  Overall, this portion was a pain in the ass and it would be nice if using
  &lt;code&gt;git init&lt;/code&gt;
  would work for a git repo over http.  The confusing combination of
  &lt;code&gt;git init&lt;/code&gt;
  and
  &lt;code&gt;git init-db&lt;/code&gt;
  while apparently requiring
  &lt;code&gt;git update-server-info --force&lt;/code&gt;
  for no reason makes this a big failure for Git.  Maybe there's another explanation- maybe I'm retarded.  I don't know.  But setting up something that should have been easy was instead horribly difficult and poorly documented.
&lt;/p&gt;
&lt;h3&gt;The Strong Suit: Branching&lt;/h3&gt;
&lt;p&gt;I will have to say, however, that branching a repository in Git is incredibly easy.  It really is impressive how easy it is to create a branch and to switch between the two.  This is useful even for only one developer, because I can maintain a "public" branch of some code.  Now I can, for instance, keep my database.yml intact without publishing my database passwords to everyone.  I don't even have to use nasty workarounds like database.yml.mine- I have my own branch that my own database.yml is checked in to.  What a breath of fresh air.&lt;/p&gt;
&lt;h3&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;Overall, Git is nothing revolutionary.  There are some really nice features, and I'm sure some other big gains for large open source projects.  But if you're working with your own code base or a small team, this won't change the way you work in any significant way.  What's more is that I don't see any reason why the biggest advantages of Git (branching, local repositories) can't be included in Subversion or any other centralized version control system.  The biggest obstacle is politics.&lt;/p&gt;
&lt;p&gt;
  Still, I highly suggest that you switch to Git for all of your development if you can. The ease of branching and the local repositories alone are enough to warrant the 10 minutes of work it will take to switch from Subversion.  Really- 10 minutes.  That's all it will take.
&lt;/p&gt;
&lt;p&gt;That said, will I be switching to Git for all of my development?  No.  While all of my personal development has already switched to Git, for my day job I'm kind of tied to Trac.  The ability to associate tickets with discrete versions in Trac is invaluable and actually required by law in our particular case.  So until Trac or another equally good bug-tracking system supports Git, I'm sticking with Subversion for my day job.&lt;/p&gt;
</description>
      <pubDate>Fri, 25 Jan 2008 07:31:17 GMT</pubDate>
      <guid>http://subwindow.com/articles/5</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>ZSFF</title>
      <link>http://subwindow.com/articles/4</link>
      <description>&lt;p&gt;
        &lt;a href='http://www.zedshaw.com'&gt;Zed Shaw&lt;/a&gt;
        has introduced a new syndication format called
        &lt;a href='http://www.zedshaw.com/blog/2008-01-13.html'&gt;ZSFF&lt;/a&gt;.
        I think it is a pretty cool idea, because RSS is generally pretty shitty.  As an author, it is hard not having control over your content- it being at the mercy of whatever RSS reader is going to fuck it up.  Plus, I have a natural aversion to anything XML.
      &lt;/p&gt;
      &lt;p&gt;
        So, I've
        &lt;a href='http://subwindow.com/articles.zsff'&gt;bought in&lt;/a&gt;.
        That's one of the key advantages to rolling your own blogging system- you can whip up something like that in 5 minutes and not worry about it.  It was literally trivial to create the ZSFF feed.  No libraries, no complex logic.  Here's my
        &lt;i&gt;entire&lt;/i&gt;
        index.zsff.erb file:
      &lt;/p&gt;
&lt;pre class="blackboard"&gt;ZSFF 1.0
  Author: Erik Peterson
  Title: subWindow Blog
  Site: http://www.subwindow.com/
  Subtitle: Just another crappy programmer's blog
  Copyright: 2008
  Content-type: text/plain
  
  &amp;lt;% &lt;span class="Keyword"&gt;for&lt;/span&gt; article &lt;span class="Keyword"&gt;in&lt;/span&gt; &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;articles&lt;/span&gt; -%&amp;gt;
  &amp;lt;%= &lt;span class="Entity"&gt;article_url&lt;/span&gt;(article) %&amp;gt;
  &amp;lt;% &lt;span class="Keyword"&gt;end&lt;/span&gt; -%&amp;gt;
&lt;/pre&gt;
      &lt;p&gt;
        I've also built a
        &lt;a href='http://subwindow.com/zsff/validator'&gt;validator&lt;/a&gt;.
        &lt;a href='http://pastie.caboo.se/pastes/138964'&gt;Here&lt;/a&gt;
        is the code as well, if you like.
      &lt;/p&gt;
      &lt;p&gt;
        &lt;b&gt;Update:&lt;/b&gt;
        I've refactored the code quite a bit and packaged it as a gem. It is now available through the command:
        &lt;pre class="blackboard"&gt;gem install zsff&lt;/pre&gt;</description>
      <pubDate>Tue, 15 Jan 2008 10:35:12 GMT</pubDate>
      <guid>http://subwindow.com/articles/4</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>Don't fuck with my expectations</title>
      <link>http://subwindow.com/articles/3</link>
      <description>&lt;p&gt;
              My hard drive died as part of some kind of karmic clusterfuck, and right before it hit the Cobain barrier I was trying out
              &lt;a href='http://git.or.cz/'&gt;git&lt;/a&gt;.
              Now that I'm back on my feet I wanted to download and recompile the source of git again so that I could continue my geekery.  There's this thing I like to do when downloading/compiling something: I get the URL for the tarball and:
            &lt;/p&gt;
            &lt;pre class="blackboard"&gt;wget {whatever} &amp;&amp; tar -xzf {whatever} &amp;&amp; cd {whatever} &amp;&amp; ./configure &amp;&amp; make &amp;&amp; sudo make install&lt;/pre&gt;
            &lt;p&gt;That way, the thing can download and compile while I don't have to tend to the whole process one step at a time.  I can go back to coding or whatever I was doing.&lt;/p&gt;
            &lt;p&gt;Much to my chagrin, OS X doesn't come with wget.  What the fuck?  wget has to be one of the most fundamental programs in existence- it is completely nonsensical to keep it out of a distribution that is Unix-based.  Sure, it has curl, but curl has more flags than Jimmy Carter has liver spots.  I'll be god damned if I'm going to remember all of those when I just want to download a fucking file.&lt;/p&gt;
            &lt;p&gt;Anyways, the point of this is that I went to get wget by going to the site, copying the url of the source, going to the terminal and typing in&lt;/p&gt;
            &lt;pre class="blackboard"&gt;wget http://ftp.gnu.org/gnu/wget/wget-1.10.1.tar.gz &amp;&amp; tar -xzf wget-1.10.1.tar.gz &amp;&amp; cd wget-1.10.1 &amp;&amp; ./configure &amp;&amp; make &amp;&amp; sudo make install&lt;/pre&gt;
            &lt;p&gt;Needless to say, I about had a fucking conniption fit.&lt;/p&gt;
            &lt;p&gt;The point here being: don't fuck with your user's expectations.  Especially the ones that are hard-wired into their brains so extensively that when they try to get around the expectation failure, they fail again.&lt;/p&gt;
            &lt;p&gt;Way to make your users feel like tards, Apple.&lt;/p&gt;</description>
      <pubDate>Tue, 15 Jan 2008 07:09:19 GMT</pubDate>
      <guid>http://subwindow.com/articles/3</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
    <item>
      <title>I'm a Terrible Programmer, And You Should Be Too</title>
      <link>http://subwindow.com/articles/2</link>
      <description>&lt;p&gt;This being the first post on this illustrious blog, I figure I'd explain that subheading you see right up there.  Let me lay it on you, short and sweet:&lt;/p&gt;
&lt;p&gt;
  &lt;b&gt;I am a terrible programmer.&lt;/b&gt;
&lt;/p&gt;
&lt;p&gt;Seriously.  I'm not very good.  About half the lines of code that I write are broken.  The way I do things the first time is almost always the wrong way.  It is rare that I look at a piece of code that I've written more than a month ago without saying "Damn.  That is some gross-looking code."  And you know what?  Somehow, I manage to ship rock-solid, scalable and nearly bug-free systems.  And you can too!  All you have to do is embrace the terrible programmer that resides within each and every one of you.&lt;/p&gt;
&lt;p&gt;
  The key to milking quality code out of a terrible programmer is that you first, before all else, have to
  &lt;b&gt;admit you are a terrible programmer.&lt;/b&gt;
  Only through that admission and the terrible fear of being discovered can you, a terrible programmer, create some fantastic systems.  The idea is that by simply
  &lt;i&gt;knowing&lt;/i&gt;
  this, you've already taken the first step on the long path to not being a terrible programmer.  It's kind of like the twelve steps of crappy code addiction, where you first have to admit that you have a problem.
&lt;/p&gt;
&lt;p&gt;
  This philosophy is kind of an offshoot of what I like to call the
  &lt;a href='http://www.sfgate.com/cgi-bin/article.cgi?file=/chronicle/archive/2000/01/18/MN73840.DTL'&gt;Dunning Effect&lt;/a&gt;.
  This is the idea that dumb people tend to overestimate their intelligence or ability, and smart people tend to underestimate it.  Therefore, if I am smart enough to realize that I am dumb, I am actually smart.  Applied to programming,
  &lt;i&gt;If I am a good enough programmer to realize that I am a terrible programmer, I am actually a good programmer.&lt;/i&gt;
  But this isn't all hocus-pocus "believing will make it happen" bullshit.  This isn't The Secret&amp;reg;.  This is serious stuff we're talking about here.  Without further ado, let me introduce:
&lt;/p&gt;
&lt;h3&gt;Erik's Terrible Guide to Not Being a Terrible Programmer By Realizing You're Terrible&lt;/h3&gt;
&lt;p&gt;
  &lt;b&gt;Part 1:  Paranoia&lt;/b&gt;
  &lt;br /&gt;
  The first step toward exploiting your mediocrity in order to achieve greatness is to be really, incredibly paranoid about it.  What if my wife finds out that I'm a terrible programmer?  She'll divorce me.  I can't let that happen!  I must test my code!  I must refactor obsessively!  I must fix bugs before they're found!
&lt;/p&gt;
&lt;p&gt;
  The most important part of this is to test your code, and I don't mean "run through it once before you commit."  I mean create some killer unit tests (and functional and integration tests, if your
  &lt;a href='http://www.rubyonrails.org'&gt;framework&lt;/a&gt;
  is in to that sort of thing).  I'm so afraid that my coworkers will find out that I'm an awful programmer that I always run greater than a 1:1 code to test ratio.  For stuff that would surely out me if it were to break (accounting software,
  &lt;a href='http://www.cardiomems.com'&gt;life-saving medical devices&lt;/a&gt;,
  etc), I usually run at about 1:2.  Believe me, these tests
  &lt;a href='http://cruisecontrolrb.thoughtworks.com/'&gt;get run&lt;/a&gt;
  &lt;a href='http://www.zenspider.com/ZSS/Products/ZenTest/'&gt;quite often&lt;/a&gt;.
  Beyond that, I always, always use
  &lt;a href='http://weblog.jamisbuck.org/2007/7/23/capistrano-multistage'&gt;multistage deployment&lt;/a&gt;.
  This means that I stand up a staging environment that is mirror image of the production setting, and I go to town.  I do shit that would shame a two-year-old.  I try to break stuff so hard you'd think I was
  &lt;a href='http://en.wikipedia.org/wiki/Rick_Allen_%28drummer%29'&gt;Rick Allen&lt;/a&gt;
  doing a drive-by high five.  If anything breaks, I fix it and write a test that duplicates the bug to make sure it never comes back.  Why? Because I'm so terrible, I'd probably let it happen again!
&lt;/p&gt;
&lt;p&gt;
  It goes beyond just testing though, buddyboy.  I'm such an awful programmer, I don't even have confidence that my exhaustive automated testing practices will prevent a bug from ever reaching a user.  I make sure that
  &lt;a href='http://agilewebdevelopment.com/plugins/exception_notifier'&gt;I know about any errors&lt;/a&gt;
  the same instant that my users do.  That way, by the time Frank shoots me an email saying "Man, Erik, this shit is broken.  You're such a terrible programmer!" The bug will already have been fixed and deployed, and I can tell Frank that he must be seeing things, because it works fine now.  He'll never suspect that underneath these clothes, I'm actually wearing Terrible Programmer underwear.  It'll just be my dirty little secret.
&lt;/p&gt;
&lt;p&gt;
  &lt;b&gt;Part 2:  Grand Theft Code&lt;/b&gt;
&lt;/p&gt;
&lt;p&gt;
  The second part of Being a Great Programmer by Being a Terrible Programmer&amp;trade; is realizing that pretty much anyone else out there who has ever written any code is a better programmer than you.  That means you must copy code like mad.  Lucky for you, people freaking
  &lt;i&gt;love&lt;/i&gt;
  sharing code.  They call this shared code "Libraries", "Frameworks", or maybe even "Plugins".  If you're doing anything, you have to first realize that somebody's probably already done it, and that it's probably been done better.  You just have to find out if they've published it, find it and then mercilessly rape and pillage that code until they're crying themselves to sleep at night.
&lt;/p&gt;
&lt;p&gt;By using other people's code wherever possible, you can rest assured knowing that you've completely minimized your chances of fucking up.  And rest well you will, my friend.  Like your dog who sleeps so soundly because he spent 36 minutes chasing his own tail, you will sleep like a baby due to your overwhelming incompetence.&lt;/p&gt;
&lt;p&gt;Well, that's it for today.  I hope you've learned that you are an awful programmer, and it is the most important thing you know.&lt;/p&gt;
</description>
      <pubDate>Sun, 13 Jan 2008 10:44:34 GMT</pubDate>
      <guid>http://subwindow.com/articles/2</guid>
      <author>erik@subwindow.com (Erik Peterson)</author>
    </item>
  </channel>
</rss>
