<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/rss2full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
  <channel>
    <title>PaulBarry.com</title>
    <link>http://paulbarry.com/</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>My thoughts, ideas, questions and concerns on technology, sports, music and life</description>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/paulbarry" type="application/rss+xml" /><feedburner:emailServiceId>676128</feedburner:emailServiceId><feedburner:feedburnerHostname>http://www.feedburner.com</feedburner:feedburnerHostname><item><title>Links for 2008-09-30 [del.icio.us]</title><link>http://feeds.feedburner.com/~r/paulbarry/~3/407920272/paulbarry</link><pubDate>Wed, 01 Oct 2008 00:00:00 -0500</pubDate><guid isPermaLink="false">http://del.icio.us/paulbarry#2008-09-30</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<ul>
<li><a href="http://clojure.googlegroups.com/web/ants.clj?gda=hYoBxzoAAABoLitVpBTEcNIQc_NHg39S3ktrIN1HaghvROCePDY5Lu9OU0NQiFWgQuhmPR7veGf97daDQaep90o7AOpSKHW0">http://clojure.googlegroups.com/web/ants.clj?gda=hYoBxzoAAABoLitVpBTEcNIQc_NHg39S3ktrIN1HaghvROCePDY5Lu9OU0NQiFWgQuhmPR7veGf97daDQaep90o7AOpSKHW0</a><br/>
Rich Hickey&#039;s Clojure ants demo</li>
</ul>]]></content:encoded><description>&lt;ul&gt;
&lt;li&gt;&lt;a href="http://clojure.googlegroups.com/web/ants.clj?gda=hYoBxzoAAABoLitVpBTEcNIQc_NHg39S3ktrIN1HaghvROCePDY5Lu9OU0NQiFWgQuhmPR7veGf97daDQaep90o7AOpSKHW0"&gt;http://clojure.googlegroups.com/web/ants.clj?gda=hYoBxzoAAABoLitVpBTEcNIQc_NHg39S3ktrIN1HaghvROCePDY5Lu9OU0NQiFWgQuhmPR7veGf97daDQaep90o7AOpSKHW0&lt;/a&gt;&lt;br/&gt;
Rich Hickey&amp;#039;s Clojure ants demo&lt;/li&gt;
&lt;/ul&gt;</description><feedburner:origLink>http://del.icio.us/paulbarry#2008-09-30</feedburner:origLink></item><item><title>Links for 2008-09-26 [del.icio.us]</title><link>http://feeds.feedburner.com/~r/paulbarry/~3/404407841/paulbarry</link><pubDate>Sat, 27 Sep 2008 00:00:00 -0500</pubDate><guid isPermaLink="false">http://del.icio.us/paulbarry#2008-09-26</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<ul>
<li><a href="http://clojure-log.n01se.net/date/2008-07-18.html">#clojure - Jul 18 2008 - Discussion about clojure maps implementing java.util.Map</a></li>
</ul>]]></content:encoded><description>&lt;ul&gt;
&lt;li&gt;&lt;a href="http://clojure-log.n01se.net/date/2008-07-18.html"&gt;#clojure - Jul 18 2008 - Discussion about clojure maps implementing java.util.Map&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><feedburner:origLink>http://del.icio.us/paulbarry#2008-09-26</feedburner:origLink></item><item>
      <title>Story Driven Development with Rails - Part I: Up and Running</title>
      <description>&lt;p&gt;Earlier this year at the &lt;a href="http://2008.goruco.com"&gt;Gotham Ruby Conference&lt;/a&gt;, &lt;a href="http://www.brynary.com"&gt;Brian Helmkamp&lt;/a&gt; gave a &lt;a href="http://goruco2008.confreaks.com/01_helmkamp.html"&gt;talk on Story Driven Development&lt;/a&gt;.  It's a fantastic talk, I suggest that everyone view it.  In it he explains how to use &lt;a href="http://rspec.info/documentation/stories.html"&gt;RSpec Users Stories&lt;/a&gt; combined with &lt;a href="http://github.com/brynary/webrat/tree/master"&gt;Webrat&lt;/a&gt; for full-stack executable scenario testing.  He also covers what value executable scenarios provide and where executable scenarios fit into your overall web application testing strategy.  This article provides you with a more detailed how-to of the steps required to get up and running with Story Driven Development with &lt;a href="http://www.rubyonrails.org"&gt;Rails&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article I don't go to much into the details of how story runner works, so I would say another prerequisite for this article is &lt;a href="http://peepcode.com/products/rspec-user-stories"&gt;Geoffrey Grosenbach's &lt;em&gt;RSpec User Stories&lt;/em&gt;&lt;/a&gt; available on &lt;a href="http://peepcode.com"&gt;PeepCode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So let's say we are building a rails version of this blog.  First steps are the basic rails setup:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rails blog
$ cd blog
$ script/plugin install git://github.com/dchelimsky/rspec.git -r 1.1.4
$ script/plugin install git://github.com/dchelimsky/rspec-rails.git -r 1.1.4
$ script/generate rspec      
$ script/plugin install git://github.com/ratnikov/fixture_replacement2.git
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That gives us an rspecified Rails app, which has a &lt;code&gt;stories&lt;/code&gt; directory, all ready for us to put our stories in.  &lt;a href="http://replacefixtures.rubyforge.org"&gt;Fixture Replacement&lt;/a&gt; is an implementation of the &lt;a href="http://www.dcmanges.com/blog/38"&gt;factory pattern for creating test data&lt;/a&gt;, which is an alternative to using &lt;a href="http://ar.rubyonrails.com/classes/Fixtures.html"&gt;fixtures&lt;/a&gt; data in &lt;a href="http://www.yaml.org"&gt;yaml&lt;/a&gt; files.  So let's create the first story in &lt;code&gt;stories/view_articles_story.txt&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Story: View Articles

As a reader of the blog
I want to view articles
So that I can read what you have to say

  Scenario: display most recently published articles

    Given 5 articles have been published
    When I visit the homepage
    Then I should see the articles
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A couple of things to point out.  First, the naming convention I'm going with is to put the plain text story in &lt;code&gt;&amp;lt;story_name&amp;gt;_story.txt&lt;/code&gt; and the story runner file in &lt;code&gt;&amp;lt;story_name&amp;gt;_story.rb&lt;/code&gt;.  Next I'd like to talk a little bit about this story.  I choose to write this story first because it is the most important feature in the application.  Generally I like to try to follow this rule.  When writing a stories, always write a story for the next most important feature.&lt;/p&gt;

&lt;p&gt;So in order to use this story, we need a runner.  So let's put this into &lt;code&gt;stories/view_articles_story.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require File.dirname(__FILE__) + "/helper"

run_story :view_articles
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is about concise as it gets for this file.  The magic happens in &lt;code&gt;stories/helper.rb&lt;/code&gt;.  Add this to the bottom of that file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def run_story(story_name, options={})
  with_steps_for(story_name) do
    run(
      File.join(File.dirname(__FILE__), "#{story_name}_story.txt"), 
      { :type =&amp;gt; RailsStory }.merge(options)
    )
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What this does is allow you to call &lt;code&gt;run_story&lt;/code&gt; to run a story.  It assumes by default that you will want to use the steps that are defined in the same file with the same name as the story.  We'll probably enhance &lt;code&gt;run_story&lt;/code&gt; in the future to do more stuff, but this is all we need for now.  Now you can run either &lt;code&gt;stories/all.rb&lt;/code&gt; or &lt;code&gt;stories/view_articles_story.rb&lt;/code&gt; and you should get:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Running 1 scenarios

Story: View Articles

  As a reader of the blog
  I want to view articles
  So that I can read what you have to say

  Scenario: Homepage

    Given 5 articles have been published (PENDING)

    When I visit the homepage (PENDING)

    Then I should see the articles (PENDING)

1 scenarios: 0 succeeded, 0 failed, 1 pending

Pending Steps:
1) View Articles (Homepage): 5 articles have been published
2) View Articles (Homepage): I visit the homepage
3) View Articles (Homepage): I should see all 5 articles
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So we now have yellow (a.k.a pending) scenarios, so step 1 is done.  The next step is to write step matchers, which should make our scenario fail.  So we add this to &lt;code&gt;stories/view_articles_story.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;steps_for(:view_articles) do
  Given "$count articles have been published" do |count|
    @count = count.to_i
    @count.times {|n| create_article(:title =&amp;gt; "Article ##{n}") }
  end
  When "I visit $path" do |path|
    get path
  end
  Then "I should see the articles" do
    @count.times do |n|
      response.should have_tag("h2", "Article ##{n}")
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make sure to add that &lt;em&gt;before&lt;/em&gt; the &lt;code&gt;run_story :view_articles&lt;/code&gt; line.  Now you should have red (a.k.a failing) scenarios, that look something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;FAILURES:
    1) View Articles (Homepage) FAILED
    NoMethodError: undefined method `create_article' for   
    #&amp;lt;ActionController::Integration::Session:0x208bfe0&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make sure they are failing for the right reason.  For example, when I first wrote this, I forgot that all the of variables are captured as strings and I left off the call to &lt;code&gt;.to_i&lt;/code&gt; on count.  I get failing specs, but not because I haven't implemented the code, because my steps are wrong.  Remember, tests can have bugs too.&lt;/p&gt;

&lt;p&gt;Let's take a look at each of these steps.  Inside the given step, we are calling &lt;code&gt;create_article&lt;/code&gt;.   Now as you noticed from the failure message, there is no &lt;code&gt;create_article&lt;/code&gt; method yet.  Fixture Replacement will take care of defining that for us once we have the articles model created and &lt;code&gt;example_data.rb&lt;/code&gt; created.  In the when step, we are capturing the path the user is trying to access, and then using the same rspec controller helper method we would use if this were a controller spec.  &lt;code&gt;get /some_path&lt;/code&gt; goes through the routing to call our controller with an HTTP get.  Finally, in the then step, we just check that the HTML in our response has an H2 element with the title of the article in it, for each of the articles we created in the given step.&lt;/p&gt;

&lt;p&gt;So now on to step 3, which is to write the code to make it go green.  To make this easy, we'll use the rspec scaffold generator:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ script/generate rspec_scaffold Article title:string body:text
$ rake db:migrate
$ rake db:test:prepare
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So if we run &lt;code&gt;stories/view_articles_story.rb&lt;/code&gt; again, we get the same error.  That's because we still haven't told Fixture Replacement about our article model.  So create the file &lt;code&gt;db/example_data.rb&lt;/code&gt; and put this in it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;module FixtureReplacement
  attributes_for :article do |a|
    a.title = "First Post!"
    a.body = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit..."
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This defines the minimum set of data required to create a valid article.  You can override any value by simply passing it in the hash, as we are doing in the given step.  Fixture Replacement gives you two methods for each model, which are in this case, &lt;code&gt;create_article&lt;/code&gt; and &lt;code&gt;new_article&lt;/code&gt;, which do pretty much what you would expect.  You can read more about Fixture Replacements in the &lt;a href="http://replacefixtures.rubyforge.org/files/README.html"&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Last but not least, we have to include &lt;code&gt;FixtureReplacement&lt;/code&gt; in our RSpec Story Runner, which is as simple as adding the line &lt;code&gt;include FixtureReplacement&lt;/code&gt; somewhere near the top of &lt;code&gt;stories/helper.rb&lt;/code&gt;, after all the other require statements.&lt;/p&gt;

&lt;p&gt;So now if we run our story, we get something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1) View Articles (Homepage) FAILED
    Spec::Expectations::ExpectationNotMetError: 
    Expected at least 1 element matching "h2", found 0.
    &amp;lt;false&amp;gt; is not true.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which is still failing, but it's progress.  Obviously the problem now is that our view isn't outputting H2 elements for the article titles.  That's an easy fix which I'll leave as an exercise to the reader (hint: modify the default route, edit the articles index ERB template).  &lt;/p&gt;

&lt;p&gt;One might argue that checking for H2s is checking the implementation details, which could be fragile if our designer decides to use DIVs instead, for example.  That decision is up to you, personally I like to produce the correct semantic markup, have my tests validate that and apply CSS from there.  If you'd rather your tests be less coupled to the DOM structure, you could just change your expectation in the then step to &lt;code&gt;response.should have_text("Article ##{n}")&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Hopefully that gets you up and running with Fixture Replacement and RSpec User Stories.  Tune in for Part II of the series, where we cover more complex interactions with &lt;a href="http://github.com/brynary/webrat/tree/master"&gt;Webrat&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=rlMqL"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=rlMqL" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=NaEml"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=NaEml" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=bEwml"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=bEwml" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=fVvML"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=fVvML" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=ksnzL"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=ksnzL" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=1C8UL"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=1C8UL" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
      <pubDate>Tue, 16 Sep 2008 17:20:47 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:fa98ea62-e0e0-4e94-be2b-032f8e4bef5c</guid>
      <author>mail@paulbarry.com (Paul Barry)</author>
      <link>http://feeds.feedburner.com/~r/paulbarry/~3/394563148/story-driven-development-with-rails-part-i-up-and-running</link>
      <category>Technology</category>
            <category>RSpec</category>
      <category>Rails</category>
      <category>FixtureReplacement</category>
      <category>Ruby</category>
      <category>StoryRunner</category>
          <feedburner:origLink>http://paulbarry.com/articles/2008/09/16/story-driven-development-with-rails-part-i-up-and-running</feedburner:origLink></item>
    <item>
      <title>Ruby on Struts</title>
      <description>&lt;p&gt;Once upon a time, there was a web MVC framework called &lt;a href="http://struts.apache.org"&gt;Struts&lt;/a&gt;.  Struts was one of the original catalysts of web frameworks based on the &lt;a href="http://en.wikipedia.org/wiki/Model-view-controller"&gt;MVC pattern&lt;/a&gt;, but it was written in &lt;a href="http://java.sun.com"&gt;Java&lt;/a&gt; and required copious amounts of &lt;a href="http://en.wikipedia.org/wiki/XML"&gt;XML&lt;/a&gt; to configure your application.  One of the many things defined in the XML were the "&lt;a href="http://struts.apache.org/1.x/userGuide/building_controller.html#actionmapping"&gt;Action Mappings&lt;/a&gt;".  An Action Mapping essentially mapped a specific URL pattern to a specific Java class that would be responsible for handling that request.&lt;/p&gt;

&lt;p&gt;Then came &lt;a href="http://rubyonrails.com"&gt;Ruby on Rails&lt;/a&gt;, which eliminated the need for these XML configuration files by using &lt;a href="http://en.wikipedia.org/wiki/Convention_over_Configuration"&gt;Convention Over Configuration&lt;/a&gt;.  The way this works in Rails is that if a request is sent to the url &lt;code&gt;/users/new&lt;/code&gt;, Rails will call the &lt;code&gt;new&lt;/code&gt; method of the &lt;code&gt;UsersController&lt;/code&gt; class to handle the request.  Rails has a feature called routing that is used to map unconventional url patterns to specific controller actions.  Then came RESTful Rails and the convention over configuration was gone.&lt;/p&gt;

&lt;p&gt;With RESTful Rails, the request path simply represents what you want to perform an operation on, the Resource, and the HTTP method specifies what you want to do.  This all makes sense, but one problem is that RESTful urls do not conform to the url convention.  This means every action must be defined in the routing.  A shortcut was added to the routing to allow one expression to define the 7 typical methods the controller for a resource will have, but any additional actions must be explicitly defined.&lt;/p&gt;

&lt;p&gt;I've been recently working with an experienced Java developer with some familiarity Rails.  He was confused by the way RESTful routing works and what paths the named route methods would generate.  When he asked what benefits all this provides over the original &lt;code&gt;/controller/action/id&lt;/code&gt; pattern, where only non-standard routes had to be mapped, I struggled to find any.  I realized that I was doing this just because it was now the "Rails Way".  He said this reminds him of Struts, and after some arguing and thinking about it, I realized he was right.&lt;/p&gt;

&lt;p&gt;So it seemed unbelievably coincidental that we both found ourselves together today in a talk titled &lt;a href="http://lonestarrubyconf.com/speakers.html#uw"&gt;Unconvental Wisdom by Bruce Tate&lt;/a&gt;.  In today's talk, Bruce pointed out that RESTful Rails adds complexity to Rails, which makes it harder to explain how Rails works to newcomers to Rails.  I'm very interested to see how this talk is received by the Rails community, so if you weren't at the talk today, look for it on &lt;a href="http://confreaks.com"&gt;Confreaks&lt;/a&gt; in the next few weeks.&lt;/p&gt;

&lt;p&gt;So looking back on RESTful Rails applications that I've developed, if they reach even a level of medium complexity, you end up with 50 lines or so in the &lt;code&gt;routes.rb&lt;/code&gt;, with at least a handful of nested and custom routes, in addition to all of the resources.  Imagine this as the admin interface for a simple blog:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;map.namespace(:admin) do |admin| 
  admin.resources :articles, :has_many =&amp;gt; :comments, :member =&amp;gt; { :publish =&amp;gt; :post }
  admin.resources :categories, :has_many =&amp;gt; :articles
  admin.resources :comments, :belongs_to =&amp;gt; :article
  admin.resources :tags, :has_many =&amp;gt; :articles
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you are an experienced Rails developer, you can decipher this right away.  But is this really easy understand?  Or wouldn't convention over configuration be easier:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;map.connect "/admin/:controller/:action/:id"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And what about named routes like &lt;code&gt;new_admin_article_comment_path(@article)&lt;/code&gt;?  Is that really more clear than &lt;code&gt;admin_path("/comments/new", :article_id =&amp;gt; @article)"&lt;/code&gt;?  Do you really care if the urls are &lt;code&gt;/comments/new?article_id=1&lt;/code&gt; or &lt;code&gt;/articles/1/comments&lt;/code&gt;?  Maybe I missing something, but I'm starting to like the sound of idea of having almost nothing in my routes and not calling dynamically generated methods everywhere to just to build simple paths.  After all, no code is faster than no code. &lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=UcTjL"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=UcTjL" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=XY5Tl"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=XY5Tl" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=MZkNl"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=MZkNl" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=r2ZPL"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=r2ZPL" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=B0U6L"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=B0U6L" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=I14pL"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=I14pL" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
      <pubDate>Sat, 06 Sep 2008 00:37:24 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:15a5b377-2821-402e-a764-0d0f6e4387b7</guid>
      <author>mail@paulbarry.com (Paul Barry)</author>
      <link>http://feeds.feedburner.com/~r/paulbarry/~3/384774480/ruby-on-struts</link>
      <category>Technology</category>
            <category>Rails</category>
      <category>Ruby</category>
      <category>REST</category>
          <feedburner:origLink>http://paulbarry.com/articles/2008/09/06/ruby-on-struts</feedburner:origLink></item>
    <item>
      <title>JavaScript: Global By Default</title>
      <description>&lt;p&gt;Here's a very simple JavaScript function that prints the sum of its arguments:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function sum() {
  s = 0;
  for(i=0; i &amp;lt; arguments.length; i++) {
    s += arguments[i];
  }
  return s;
}
document.write('sum = '+sum(1, 2, 3));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Looks simple enough.  Translated directly into non-idiomatic Ruby, that would be:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def sum(*args)
  s = 0
  i = 0
  while i &amp;lt; args.length
    s += args[i]
    i += 1
  end
  s
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So let's say you now want your sum function to return the sum of the factorial of each number.  No problem:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function sum() {
  s = 0;
  for(i=0; i &amp;lt; arguments.length; i++) {
    s += factorial(arguments[i]);
  }
  return s;
}
function factorial(n) {
  f = 1;
  for(i=1; i &amp;lt;= n; i++) {
    f *= i;
  }
  return f;
}
document.write('sum = '+sum(1, 2, 3));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Oops.  That prints &lt;code&gt;1&lt;/code&gt;, but the answer we were looking for was &lt;code&gt;9&lt;/code&gt;.  Hmmm, let's translate it to ruby and see what we get:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def sum(*args)
  s = 0
  i = 0
  while i &amp;lt; args.length
    s += factorial(args[i])
    i += 1
  end
  s
end
def factorial(n)
  s = 1
  i = 1
  while i &amp;lt;= n
    s = s * i
    i += 1
  end
  s
end
puts sum(1, 2, 3)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ok, so Ruby gives us &lt;code&gt;9&lt;/code&gt;.  So what's up?  The truth is that is not a direct translation.  Here's the correct translation:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def sum(*args)
  $s = 0
  $i = 0
  while $i &amp;lt; args.length
    $s += factorial(args[$i])
    $i += 1
  end
  $s
end
def factorial(n)
  $s = 1
  $i = 1
  while $i &amp;lt;= n
    $s = $s * $i
    $i += 1
  end
  $s
end
puts sum(1, 2, 3)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This gives us the same result as the flawed JavaScript, which is &lt;code&gt;1&lt;/code&gt;.  As you can see, in both functions, the variables &lt;code&gt;s&lt;/code&gt; and &lt;code&gt;i&lt;/code&gt; are declared as global variables, which you can tell by the &lt;code&gt;$&lt;/code&gt; &lt;a href="http://en.wikipedia.org/wiki/Sigil_" title="computer_programming"&gt;sigil&lt;/a&gt;.  But in JavaScript, &lt;strong&gt;variables are global by default&lt;/strong&gt;.  That's right, the simple little innocuous-looking &lt;code&gt;i=0&lt;/code&gt; in our JavaScript for loop defines a global variable.  Here is the corrected JavaScript version:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function sum() {
  var s = 0;
  for(var i=0; i &amp;lt; arguments.length; i++) {
    s += factorial(arguments[i]);
  }
  return s;
}
function factorial(n) {
  var f = 1;
  for(var i=1; i &amp;lt;= n; i++) {
    f *= i;
  }
  return f;
}
document.write('sum = '+sum(1, 2, 3));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The moral of the story is always prefix your variable declarations with &lt;code&gt;var&lt;/code&gt;.  If you are a web developer who writes JavaScript and this is news to you, stop what you are doing an read &lt;a href="http://www.crockford.com"&gt;Douglas Crockford&lt;/a&gt;'s &lt;em&gt;&lt;a href="http://www.amazon.com/exec/obidos/ASIN/0596517742"&gt;JavaScript: The Good Parts&lt;/a&gt;&lt;/em&gt;. &lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=PrpnuK"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=PrpnuK" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=iqKZHk"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=iqKZHk" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=2rlL3k"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=2rlL3k" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=s1MuFK"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=s1MuFK" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=AnXnfK"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=AnXnfK" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=4g0CGK"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=4g0CGK" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
      <pubDate>Mon, 01 Sep 2008 00:44:46 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:4d168ca6-7e1b-4864-8f84-3d3fbe9b624a</guid>
      <author>mail@paulbarry.com (Paul Barry)</author>
      <link>http://feeds.feedburner.com/~r/paulbarry/~3/380181466/javascript-global-by-default</link>
      <category>Technology</category>
            <category>Javascript</category>
          <feedburner:origLink>http://paulbarry.com/articles/2008/09/01/javascript-global-by-default</feedburner:origLink></item>
    <item><title>Links for 2008-08-30 [del.icio.us]</title><link>http://feeds.feedburner.com/~r/paulbarry/~3/379412855/paulbarry</link><pubDate>Sun, 31 Aug 2008 00:00:00 -0500</pubDate><guid isPermaLink="false">http://del.icio.us/paulbarry#2008-08-30</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<ul>
<li><a href="http://iterativedesigns.com/2008/8/29/concurrency-or-how-i-learned-to-love-the-stache">Iterative Designs - Concurrency or How I Learned to Love the 'Stache</a></li>
</ul>]]></content:encoded><description>&lt;ul&gt;
&lt;li&gt;&lt;a href="http://iterativedesigns.com/2008/8/29/concurrency-or-how-i-learned-to-love-the-stache"&gt;Iterative Designs - Concurrency or How I Learned to Love the 'Stache&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><feedburner:origLink>http://del.icio.us/paulbarry#2008-08-30</feedburner:origLink></item><item>
      <title>Concerned With: Skinny Controller, Skinny Model</title>
      <description>&lt;p&gt;A small Rails technique that I've been using lately is &lt;code&gt;concerned_with&lt;/code&gt;.  I first saw &lt;a href="http://matthewbass.com"&gt;Matthew Bass&lt;/a&gt; use it on the &lt;a href="http://paulbarry.com/articles/2008/08/15/my-guest-appearance-on-a-hashrocket-3-2-1-project"&gt;=&gt; 3-2-1&lt;/a&gt;, but I've gleaned from a &lt;a href="http://blog.hasmanythrough.com/2008/5/6/a-simple-alternative-to-namespaced-models"&gt;couple&lt;/a&gt; of &lt;a href="http://blog.skiptree.com/?p=12"&gt;articles&lt;/a&gt; on the web that it was invented by &lt;a href="http://pastie.org/pastes/129144"&gt;Rick Olsen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The purpose of &lt;code&gt;concerned_with&lt;/code&gt; is to break up larger models up into concerns.  Let's say you have a User model, and you have several macros, includes, validations, callbacks, class methods and instance methods all related to authentication, maybe sort of like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class User &amp;lt;&amp;lt; ActiveRecord::Base
  include AuthenticationSupport
  validates_presence_of :login
  validates_uniqueness_of :login
  before_create :set_default_password
  class &amp;lt;&amp;lt; self
    def authenticate(login, password)
      login == "root" &amp;amp;&amp;amp; password == "too_many_secrets"
    end
  end
  def set_default_password
    self.password = "pa$$w0rd"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As your model grows in complexity, eventually what will happens is that you will have more validations, callbacks, class methods, instance methods, etc.  They will get mixed in with the authentication ones, &lt;code&gt;app/models/user.rb&lt;/code&gt; will grow to become 500 lines of code and it will be hard to keep track of what's what.  Instead, just put this into &lt;code&gt;app/models/user.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class User &amp;lt;&amp;lt; ActiveRecord::Base
  concerned_with :authentication
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then throw all of that into &lt;code&gt;app/models/user/authentication.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class User
  include AuthenticationSupport
  validates_presence_of :login
  validates_uniqueness_of :login
  before_create :set_default_password
  class &amp;lt;&amp;lt; self
    def authenticate(login, password)
      login == "root" &amp;amp;&amp;amp; password == "too_many_secrets"
    end
  end
  def set_default_password
    self.password = "pa$$w0rd"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So now your &lt;code&gt;app/models/user.rb&lt;/code&gt; is clean and can have other stuff in there, and all the authentication related stuff gets loaded from &lt;code&gt;app/model/user/authentication.rb&lt;/code&gt;. All you have to do to enable this is is throw this snippet into an initializer, like &lt;code&gt;config/initializers/concerned_with.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class &amp;lt;&amp;lt; ActiveRecord::Base
  def concerned_with(*concerns)
    concerns.each do |concern|
      require_dependency "#{name.underscore}/#{concern}"
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=ReD9kK"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=ReD9kK" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=t9tn7k"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=t9tn7k" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=9WvSYk"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=9WvSYk" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=MnuwFK"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=MnuwFK" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=IKRYbK"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=IKRYbK" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=uwjaTK"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=uwjaTK" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
      <pubDate>Sat, 30 Aug 2008 14:33:18 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:06cac024-02eb-497d-b45b-2c0b35d95c90</guid>
      <author>mail@paulbarry.com (Paul Barry)</author>
      <link>http://feeds.feedburner.com/~r/paulbarry/~3/379098031/concerned-with-skinny-controller-skinny-model</link>
      <category>Technology</category>
            <category>Rails</category>
      <category>Ruby</category>
          <feedburner:origLink>http://paulbarry.com/articles/2008/08/30/concerned-with-skinny-controller-skinny-model</feedburner:origLink></item>
    <item><title>Links for 2008-08-23 [del.icio.us]</title><link>http://feeds.feedburner.com/~r/paulbarry/~3/373210048/paulbarry</link><pubDate>Sun, 24 Aug 2008 00:00:00 -0500</pubDate><guid isPermaLink="false">http://del.icio.us/paulbarry#2008-08-23</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<ul>
<li><a href="http://orderedlist.com/articles/content-management-survey-results">Content Management Survey Results // Ordered List // We Make The Web Beautifully Simple</a></li>
</ul>]]></content:encoded><description>&lt;ul&gt;
&lt;li&gt;&lt;a href="http://orderedlist.com/articles/content-management-survey-results"&gt;Content Management Survey Results // Ordered List // We Make The Web Beautifully Simple&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><feedburner:origLink>http://del.icio.us/paulbarry#2008-08-23</feedburner:origLink></item><item><title>Links for 2008-08-20 [del.icio.us]</title><link>http://feeds.feedburner.com/~r/paulbarry/~3/370617565/paulbarry</link><pubDate>Thu, 21 Aug 2008 00:00:00 -0500</pubDate><guid isPermaLink="false">http://del.icio.us/paulbarry#2008-08-20</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<ul>
<li><a href="http://developer.yahoo.com/search/boss/">Yahoo! Search BOSS - YDN</a><br/>
Build your Own Search Search.  I would have called it BYOS, but that&#039;s just me.</li>
</ul>]]></content:encoded><description>&lt;ul&gt;
&lt;li&gt;&lt;a href="http://developer.yahoo.com/search/boss/"&gt;Yahoo! Search BOSS - YDN&lt;/a&gt;&lt;br/&gt;
Build your Own Search Search.  I would have called it BYOS, but that&amp;#039;s just me.&lt;/li&gt;
&lt;/ul&gt;</description><feedburner:origLink>http://del.icio.us/paulbarry#2008-08-20</feedburner:origLink></item><item><title>Links for 2008-08-15 [del.icio.us]</title><link>http://feeds.feedburner.com/~r/paulbarry/~3/366257616/paulbarry</link><pubDate>Sat, 16 Aug 2008 00:00:00 -0500</pubDate><guid isPermaLink="false">http://del.icio.us/paulbarry#2008-08-15</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<ul>
<li><a href="http://www.smashingmagazine.com/2008/04/15/60-more-ajax-and-javascript-solutions-for-professional-coding/">60 More AJAX- and Javascript Solutions For Professional Coding | Developer's Toolbox | Smashing Magazine</a></li>
</ul>]]></content:encoded><description>&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.smashingmagazine.com/2008/04/15/60-more-ajax-and-javascript-solutions-for-professional-coding/"&gt;60 More AJAX- and Javascript Solutions For Professional Coding | Developer's Toolbox | Smashing Magazine&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><feedburner:origLink>http://del.icio.us/paulbarry#2008-08-15</feedburner:origLink></item><item>
      <title>My Guest Appearance on a Hashrocket 3-2-1 Project</title>
      <description>&lt;p&gt;This week I took a brief hiatus from my normal job at &lt;a href="http://browsermedia.com"&gt;BrowserMedia&lt;/a&gt; to spend a week on a 3-2-1 project with &lt;a href="http://hashrocket.com"&gt;Hashrocket&lt;/a&gt; with fellow guest stars &lt;a href="http://hamptoncatlin.com"&gt;Hampton Catlin&lt;/a&gt; and &lt;a href="http://matthewbass.com"&gt;Matthew Bass&lt;/a&gt;.  It was a great experience for many reasons.&lt;/p&gt;

&lt;p&gt;One of the key factors that made this 3-2-1 possible is that not only do we all use good tools, but we all use the &lt;em&gt;same&lt;/em&gt; good tools.  All of us use Macs, Textmate, Git, Rails, RSpec, Restful Authentication and Haml.  This allowed us to hit the ground running.  All the basic infrastructure needed for building a web app was in place, so were able to get going and focus on the core functionality of the app.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blog.obiefernandez.com/content/2008/08/the-hashrocket-way-pair-programming.html"&gt;Pair programming&lt;/a&gt;, at least to the degree that it is done at Hashrocket, was a new experience for me.  Every developer works in a pair programming setup, with a laptop hooked up to a &lt;a href="http://www.apple.com/displays"&gt;30-inch display&lt;/a&gt; and two &lt;a href="http://www.apple.com/keyboard"&gt;keyboards&lt;/a&gt; and &lt;a href="http://www.apple.com/mightymouse"&gt;two mice&lt;/a&gt;.  Having a large monitor, two keyboards and two mice may seem like a luxury, but it really helps you get into the flow of pair programming.&lt;/p&gt;

&lt;p&gt;Another core philosophy at the heart of the Hashrocket methodology is test-driven development (TDD).  TDD is something that most agile development shops strive to do, but sometimes fall short.  I believe that two of the main reasons many teams struggle getting TDD right are tight deadlines and lack of developer expertise and discipline.  All developers who try to do TDD have experienced this at some point.  You've got a feature that needs to be cranked out ASAP, you're not exactly sure how test it, so you just dive into the code and pound it out.  My experience at Hashrocket has taught me that that all too common scenario can be avoided by practicing pair programming and TDD together.&lt;/p&gt;

&lt;p&gt;One pair programming technique that makes TDD easier is ping pong pair programming.  When doing ping pong pair programming, when you sit down to build a feature, the first person in the pair writes the test.  Next, the second person in the pair writes the code to make the test pass.  Then the second person writes a test for the next feature, and the first person in the pair implements it.  By repeating this process throughout day, you have several benefits.  First of all, it avoids the scenario where one developer does most of the work and the other developer just zones out and gets distracted by something else, because you are constantly switching back and forth.  Also, like two people who go on a diet together to help each other stick to it, one developer doesn't let the other developer get lazy and skimp out on the tests for a specific feature.  That doesn't apply just to the test.  As long as you have a pair of two experienced programmers, one of them it's going to let the other get away with writing a nasty piece of code.  It's like a real-time code review.&lt;/p&gt;

&lt;p&gt;My favorite aspect of the 3-2-1 experience was having the time to spend with other good software developers and just discuss the process of software development.  TDD was a topic of conversation all week.  Some believe in a strict adherence to TDD, "&lt;a href="http://smartic.us/2008/8/5/smarticus-rubynation-slides"&gt;Test all the fucking time&lt;/a&gt;", if you will.  Others believe TDD can be a warm blanket.  It's easy to fall into a mentality that the tests pass, therefore the code works.  Human testing can be a more effective process for discovering bugs in software, but human testing gets much less emphasis than TDD.  I believe there is a lot of value in functional testing with tools like Selenium and I would like to see more emphasis on functional testing in the community.  For my next project, I'm considering just specing the models and testing the rest of it with Selenium.  If you are following the best practice of &lt;a href="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model"&gt;SCFM&lt;/a&gt;, you controllers should be mostly glue code anyway, so you get more out of testing from the browser via an automated, repeatable test than unit testing your controllers.  It was a healthy debate and great to have a chance to be part of it.&lt;/p&gt;

&lt;p&gt;I suggest all developers, whether you are an independent developer or a full-time employee for an organization, find an opportunity to participate in a 3-2-1.  The amount of knowledge sharing that goes on is invaluable, exceeding what you get out of attending any conference.  You spend the entire day pair programming with other top-notch developers and the evenings discussing all aspects of software development.  This is the type of environment &lt;a href="http://evan.tiggerpalace.com"&gt;Evan Light&lt;/a&gt; envisions for &lt;a href="http://rubydcamp.org"&gt;DCamp&lt;/a&gt;.  Developers working together, one-on-one, sharing knowledge and trading best practices.  &lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=HYXIcK"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=HYXIcK" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=DqJZUk"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=DqJZUk" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=IBi4yk"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=IBi4yk" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=SEOIoK"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=SEOIoK" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=30x2uK"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=30x2uK" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=0mm3HK"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=0mm3HK" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
      <pubDate>Fri, 15 Aug 2008 14:08:19 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:c62be0e6-1752-4508-969b-5684bca096f0</guid>
      <author>mail@paulbarry.com (Paul Barry)</author>
      <link>http://feeds.feedburner.com/~r/paulbarry/~3/365877534/my-guest-appearance-on-a-hashrocket-3-2-1-project</link>
      <category>Technology</category>
            <category>Rails</category>
      <category>Ruby</category>
          <feedburner:origLink>http://paulbarry.com/articles/2008/08/15/my-guest-appearance-on-a-hashrocket-3-2-1-project</feedburner:origLink></item>
    <item><title>Links for 2008-08-13 [del.icio.us]</title><link>http://feeds.feedburner.com/~r/paulbarry/~3/364507029/paulbarry</link><pubDate>Thu, 14 Aug 2008 00:00:00 -0500</pubDate><guid isPermaLink="false">http://del.icio.us/paulbarry#2008-08-13</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<ul>
<li><a href="http://www.cmsbox.com/">CMS BOX - CONTENT MANAGEMENT SYSTEM</a></li>
</ul>]]></content:encoded><description>&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.cmsbox.com/"&gt;CMS BOX - CONTENT MANAGEMENT SYSTEM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><feedburner:origLink>http://del.icio.us/paulbarry#2008-08-13</feedburner:origLink></item><item>
      <title>Top 10 Things That Suck About the iPhone 3G</title>
      <description>&lt;p&gt;&lt;img src="http://img.skitch.com/20080723-de3ithsjpp434ei8bmrn82n4i8.jpg" alt="Top Ten Things That Suck About the iPhone 3G"/&gt;&lt;/p&gt;

&lt;p&gt;For the most part I'm pretty happy with my new iPhone 3G, so this is meant to be constructive criticism.  I don't even compare my iPhone to the simple run-of-the-mill phone I was using before.  That was a true cell phone, this is a handle-held computer that you can make phone calls with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. No Internet Connection Sharing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It would be nice to be able to connect to the internet on a Macbook that has an iPhone connected to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. No Finder&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The standard web file upload capability is disabled.  It would be nice if there were an &lt;a href="http://ifapp.wordpress.com/2008/07/03/idisk-is-the-new-finder-finder-in-iphone-4/"&gt;iPhone Finder app&lt;/a&gt;, that let you browse through a hierarchy of all files saved on the iPhone, including photos, mp3s, apps, mail attachments, etc.  Also maybe have some way of transferring documents like PDFs to the iPhone, and some way of viewing them as well as being able to upload them to websites.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Video Recording&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It would be really nice to be able to record and upload videos, even have live video chat, but I'm sure that's a hardware/bandwidth limitation for now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Lack of Openness&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The iPhone SDK NDA has some pretty silly restrictions in it, as you can see from &lt;a href="http://pragdave.blogs.pragprog.com/pragdave/2008/07/if-you-work-for.html"&gt;Prag Dave's recent fiasco&lt;/a&gt;.  Also, to distribute apps on the App Store, you must pay $99 for a developer's license, even if you plan to give away your app for free.  The fact is the Apple often behaves in the same monopolistic ways the Microsoft does, but we generally give them a pass on it because they make great products.  It's like the typical story of the young upstart revolutionist gaining the support of the people to overthrow the evil dictator, only to become the next evil dictator himself.  Expect the anit-mac fanboy, free as in speech, Slashdot crew to jump all over this once the &lt;a href="http://code.google.com/android/"&gt;Android project&lt;/a&gt; gets some traction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Stability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Apps crash with some regularity and the whole OS will crash and restart as well sometimes.  The iPhone needs less crashes and when it does crash, crashing a little more gracefully would be nice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Flash&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you could install Flash, you could watch embedded videos from Vimeo, Blip.TV, ESPN Videos, etc.  Also, if you could install Silverlight, you could watch MLB.TV, but that one is MLB.TV's fault, they should have a native iPhone App.  3G probably doesn't give you enough bandwidth, but it would still be nice to watch live baseball games when connected to WiFi.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Real GPS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I don't know if it's a technical limitation or not, but we need a TomTom/Garmin/VZ Navigator quality turn-by-turn, voice narrated GPS directions app.  My crappy samsung phone had that:&lt;/p&gt;

&lt;p&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/yurqFgTIm3c&amp;amp;hl=en&amp;amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/yurqFgTIm3c&amp;amp;hl=en&amp;amp;fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. AT&amp;amp;T&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I switched to the iPhone from Verizon.  Quality of service is much worse, I barely get a signal in my own house, which is a mile from downtown Baltimore.  So much for more bars in more places.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Copy &amp;amp; Paste&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The biggest reason I need copy &amp;amp; paste is for passwords.  For password management, I use Password Gorilla, which totally sucks, but runs on Windows and Mac.  A password manager is a trivial app to write, so I would create one myself, but without cut &amp;amp; paste, there's no way to make it work.  Even if you had cut &amp;amp; paste, you would need the next item.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. No Multitasking&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, I want to have Pandora running in the background while I check my email.  I want to be logged into AIM so people can IM me while I'm doing something else.  I want to be notified when I have new mail.  I want the Twittervision and NetNewsWire icons to update to show me there are unread items, but they can't until you can run more than one app at the same time.&lt;/p&gt;

&lt;p&gt;What I want is Command+Tab.  You know how in Safari you press the bottom right button and you can see all your pages, switch from one to the next?  That's what you need for the whole OS.  You press the home button (the actual only real button, is that called the home button?) and instead of closing the app you are running and taking you back to the home screen, it gives you the Command+Tab/Safari like view of all running apps.  You slide from one app to the next and pick which one to bring to the foreground.  You press the home button again, then it takes you home.  Combine that with Cut &amp;amp; Paste and the result would be a serious workflow/usability improvement.&lt;/p&gt;

&lt;p&gt;I realize this means the OS needs better memory management and the hardware needs more RAM, but I confident Apple can make that happen.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=mvYYnJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=mvYYnJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=zNZEqj"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=zNZEqj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=00xBWj"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=00xBWj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=EyG6zJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=EyG6zJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=Ealm5J"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=Ealm5J" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=FaU9uJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=FaU9uJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
      <pubDate>Wed, 23 Jul 2008 18:32:01 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:92d7c58a-9021-4edc-9bb5-a8df29383cc4</guid>
      <author>mail@paulbarry.com (Paul Barry)</author>
      <link>http://feeds.feedburner.com/~r/paulbarry/~3/343988951/top-10-things-that-suck-about-the-iphone-3g</link>
      <category>Technology</category>
            <category>iPhone</category>
          <feedburner:origLink>http://paulbarry.com/articles/2008/07/23/top-10-things-that-suck-about-the-iphone-3g</feedburner:origLink></item>
    <item>
      <title>Stripes Book from the Pragmatic Programmers</title>
      <description>&lt;p&gt;It's really good to see a &lt;a href="http://www.pragprog.com/titles/fdstr/stripes"&gt;book&lt;/a&gt; being published by the &lt;a href="http://www.pragprog.com"&gt;Pragmatic Programmers&lt;/a&gt; on the &lt;a href="http://stripesframework.org"&gt;Stripes&lt;/a&gt;, even if it is too little, too late.  I wrote &lt;a href="http://paulbarry.com/articles/2006/07/11/stripes-is-the-new-rails"&gt;an article on Stripes&lt;/a&gt; a few years ago, and actually, I still like how Stripes handles mapping request parameters to an object graph, but since then I have discovered the benefits of dynamic languages like Ruby, so I would never consider using a framework like Stripes, just because it means programming in Java.  When I was doing Java, I felt that Stripes was a much better framework than WebWork/Struts, but for some reason flew under the radar.  In fact, this blog is built using Stripes.  Stripes' creator Tim Fennell always did an excellent job answering questions on the mailing list and deserved more recognition from the Java community as a whole.&lt;/p&gt;

&lt;p&gt;It's surprising to see this book coming out now.  Frankly, straight Java as a web development language is yesterday's technology, the Cobol of our generation.  The JVM is still alive an well, with many great options, such as JRuby, Groovy, Scala and the best of them all, &lt;a href="http://clojure.org"&gt;Clojure&lt;/a&gt;.  I would love to see a Pragmatic Programmer book on Clojure, just to give some more attention to Clojure, because it is such a great language.  Anyway, congrats to Tim Fennell, Frederic Daoud and the Stripes team on finally getting some acknowledgment for building a great framework and a great community. &lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=XewByJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=XewByJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=1E5Klj"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=1E5Klj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=OJttjj"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=OJttjj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=MkCJ9J"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=MkCJ9J" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=baWQjJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=baWQjJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=F8wHMJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=F8wHMJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
      <pubDate>Wed, 23 Jul 2008 12:17:58 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:e32e1076-5b48-45ed-b2ea-f884472d44f6</guid>
      <author>mail@paulbarry.com (Paul Barry)</author>
      <link>http://feeds.feedburner.com/~r/paulbarry/~3/343700252/stripes-book-from-the-pragmatic-programmers</link>
      <category>Technology</category>
            <category>Clojure</category>
      <category>Java</category>
      <category>Stripes</category>
          <feedburner:origLink>http://paulbarry.com/articles/2008/07/23/stripes-book-from-the-pragmatic-programmers</feedburner:origLink></item>
    <item>
      <title>Person.new :name =&gt; "Brighid"</title>
      <description>&lt;p&gt;You probably won't be seeing to many technical rants about Ruby or Lisp on my blog for a few weeks.  To find out why, visit &lt;a href="http://brighidbarry.blogspot.com"&gt;http://brighidbarry.blogspot.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://picasaweb.google.com/pauljbarry3"&gt;&lt;img src="http://lh6.ggpht.com/pauljbarry3/SIOAnud8cOI/AAAAAAAAE0U/KxW5ZulNbrQ/IMG_4993.JPG?imgmax=720" width="640"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=yyJSoJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=yyJSoJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=nMw81j"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=nMw81j" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=6xEBbj"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=6xEBbj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=gpw1lJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=gpw1lJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=L48N9J"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=L48N9J" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=BMucbJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=BMucbJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
      <pubDate>Sun, 20 Jul 2008 19:23:30 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:69b88444-0f65-402f-a953-e28a09a15e7b</guid>
      <author>mail@paulbarry.com (Paul Barry)</author>
      <link>http://feeds.feedburner.com/~r/paulbarry/~3/341023608/person-new-name-brighid</link>
      <category>General</category>
            <category>Brighid</category>
          <feedburner:origLink>http://paulbarry.com/articles/2008/07/20/person-new-name-brighid</feedburner:origLink></item>
    <item>
      <title>The Trendy World of Ruby</title>
      <description>&lt;p&gt;Just in case your having as hard a time keeping up as I am:&lt;/p&gt;

&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;Old and Busted&lt;/th&gt;
    &lt;th&gt;The New Hotness&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;MRI&lt;/td&gt;
    &lt;td&gt;Rubinius&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Rails&lt;/td&gt;
    &lt;td&gt;Merb&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;ActiveRecord&lt;/td&gt;
    &lt;td&gt;DataMapper&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Prototype/Scriptaculous&lt;/td&gt;
    &lt;td&gt;jQuery&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Plugins&lt;/td&gt;
    &lt;td&gt;Gems&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;test/unit&lt;/td&gt;
    &lt;td&gt;Rspec&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;ERB&lt;/td&gt;
    &lt;td&gt;HAML&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Ferret&lt;/td&gt;
    &lt;td&gt;Sphinx&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Apache&lt;/td&gt;
    &lt;td&gt;Nginx&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Mongrel&lt;/td&gt;
    &lt;td&gt;Thin&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Capistrano&lt;/td&gt;
    &lt;td&gt;Vlad&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;SVN&lt;/td&gt;
    &lt;td&gt;Git&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Twitter&lt;/td&gt;
    &lt;td&gt;FriendFeed&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;Did I leave anything out?&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=1WfK8J"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=1WfK8J" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=fBPZNj"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=fBPZNj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=V7Gbpj"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=V7Gbpj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=eSJBtJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=eSJBtJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=VBHFFJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=VBHFFJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=6h7YGJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=6h7YGJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
      <pubDate>Sun, 13 Jul 2008 23:33:01 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:b27671db-1625-4c43-8ed2-3ec72740bf2d</guid>
      <author>mail@paulbarry.com (Paul Barry)</author>
      <link>http://feeds.feedburner.com/~r/paulbarry/~3/334752402/the-trendy-world-of-ruby</link>
      <category>Technology</category>
            <category>Ruby</category>
          <feedburner:origLink>http://paulbarry.com/articles/2008/07/13/the-trendy-world-of-ruby</feedburner:origLink></item>
    <item>
      <title>Interactive Development with Clojure</title>
      <description>&lt;p&gt;A screencast on interactive development with Clojure.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blip.tv/file/get/Pjb3-InteractiveDevelopmentWithClojure373.mov"&gt;Download original 18MB quicktime file&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;object width="640" height="483"&gt;   &lt;param name="allowfullscreen" value="true" /&gt;   &lt;param name="allowscriptaccess" value="always" /&gt;   &lt;param name="movie" value="http://www.vimeo.com/moogaloop.swf?clip_id=1324685&amp;amp;server=www.vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" /&gt; &lt;embed src="http://www.vimeo.com/moogaloop.swf?clip_id=1324685&amp;amp;server=www.vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="640" height="483"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=u6SvIJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=u6SvIJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=A7OlBj"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=A7OlBj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=8kIqGj"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=8kIqGj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=9bO7OJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=9bO7OJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=ScchlJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=ScchlJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/paulbarry?a=NZ6AzJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/paulbarry?i=NZ6AzJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
      <pubDate>Fri, 11 Jul 2008 21:46:04 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:82db6356-02b8-46c6-9bb7-c03146e21487</guid>
      <author>mail@paulbarry.com (Paul Barry)</author>
      <link>http://feeds.feedburner.com/~r/paulbarry/~3/333183761/interactive-development-with-clojure</link>
      <category>Technology</category>
            <category>Clojure</category>
      <category>lisp</category>
          <feedburner:origLink>http://paulbarry.com/articles/2008/07/11/interactive-development-with-clojure</feedburner:origLink></item>
      <lastBuildDate>Wed, 01 Oct 2008 00:00:00 -0500</lastBuildDate></channel>
</rss>
