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

<channel>
	<title>Harry Love &#187; Portfolio</title>
	<atom:link href="http://harrylove.org/tags/portfolio/feed" rel="self" type="application/rss+xml" />
	<link>http://harrylove.org</link>
	<description>Neat wheelie, Mrs. Schmouse, don't lose it!</description>
	<lastBuildDate>Thu, 25 Jun 2009 05:22:19 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>The Building of Jetrecord: Episode 4: Cucumbers and Webrats!</title>
		<link>http://harrylove.org/2009/01/19/the-building-of-jetrecord-episode-4-cucumbers-and-webrats.html</link>
		<comments>http://harrylove.org/2009/01/19/the-building-of-jetrecord-episode-4-cucumbers-and-webrats.html#comments</comments>
		<pubDate>Mon, 19 Jan 2009 22:07:26 +0000</pubDate>
		<dc:creator>Harry Love</dc:creator>
				<category><![CDATA[Portfolio]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[Video]]></category>
		<category><![CDATA[cucumber]]></category>
		<category><![CDATA[rspec]]></category>
		<category><![CDATA[webrat]]></category>

		<guid isPermaLink="false">http://harrylove.org/?p=659</guid>
		<description><![CDATA[The message here is that with automated tests, we don’t have to worry about code being fragile. We can try out some stuff we think might work, and test it in seconds. We can be more courageous with our coding, and not have to code with cotton gloves on.  &#8212; Test Driven Development
In the [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>The message here is that with automated tests, we don’t have to worry about code being fragile. We can try out some stuff we think might work, and test it in seconds. We can be more courageous with our coding, and not have to code with cotton gloves on.  &#8212; <a href="http://www.byte-vision.com/TestDrivenDevelopmentArticle.aspx">Test Driven Development</a></p></blockquote>
<p>In <a href="/2009/01/12/the-building-of-jetrecord-episode-3-git-capistrano-and-a-test-release">the last episode</a> I started my Rails code, committed it to a Git repository, and released my first deployment with Capistrano. In this episode I&#8217;m going to start my first feature by writing some tests and then implementing the code.</p>
<p>Cue film.</p>
<p><object width="601" height="453"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=2884566&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" /><embed src="http://vimeo.com/moogaloop.swf?clip_id=2884566&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="601" height="453"></embed></object></p>
<p>Download (right-click and save) <a href="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-4.mov">QuickTime MOV</a> <small>(72 Mb)</small> | <a href="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-4.mp4">QuickTime iPod MP4</a> <small>(19 Mb)</small> | <a href="http://jetrecord.bingodisk.com/public/videos/jetrecord/jetrecord-episode4.qt.smil">QuickTime SMIL with captions</a> <small>(72 Mb) [Note: you must choose to open the SMIL file with QuickTime]</small></p>
<h2>Adding RSpec, Cucumber, and Webrat to our Rails App</h2>
<p>With the <a href="http://rspec.info/">RSpec-Rails</a>, <a href="http://github.com/aslakhellesoy/cucumber">Cucumber</a>, and <a href="http://github.com/brynary/webrat">Webrat</a> gems installed, let&#8217;s start a new branch in our Git repository and set up our app for testing:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> git branch cucumber
$<span style="color: #000000; font-weight: bold;">&gt;</span> git checkout cucumber
$<span style="color: #000000; font-weight: bold;">&gt;</span> script<span style="color: #000000; font-weight: bold;">/</span>generate cucumber
$<span style="color: #000000; font-weight: bold;">&gt;</span> script<span style="color: #000000; font-weight: bold;">/</span>generate rspec</pre></div></div>

<p>Now edit RAILS_ROOT/features/support/env.rb. This file was created when we ran <code>script/generate cucumber</code>. We need to change a few lines. Here is what my env.rb file looks like after editing.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># Sets up the Rails environment for Cucumber</span>
ENV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;RAILS_ENV&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span> = <span style="color:#996600;">&quot;test&quot;</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#9900CC;">expand_path</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#9900CC;">dirname</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#0000FF; font-weight:bold;">__FILE__</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">+</span> <span style="color:#996600;">'/../../config/environment'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'cucumber/rails/world'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'cucumber/formatters/unicode'</span> <span style="color:#008000; font-style:italic;"># Comment out this line if you don't want Cucumber Unicode support</span>
<span style="color:#6666ff; font-weight:bold;">Cucumber::Rails</span>.<span style="color:#9900CC;">use_transactional_fixtures</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Comment out the next two lines if you're not using RSpec's matchers (should / should_not) in your steps.</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'webrat/rails'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'cucumber/rails/rspec'</span></pre></div></div>

<p>We can add autotesting capability by installing the <a href="http://zentest.rubyforge.org/ZenTest/">ZenTest gem</a>. Autotest re-runs our tests after every code change, greatly speeding up our testing process. After getting the gem installed, we need to set some global variables so autotest knows to run our RSpec and Cucumber tests. Open up your .bash_profile file (usually located in your user&#8217;s home directory on your system). Add the following lines to the bottom of the file.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">export</span> <span style="color: #007800;">RSPEC</span>=<span style="color: #c20cb9; font-weight: bold;">true</span>
<span style="color: #7a0874; font-weight: bold;">export</span> <span style="color: #007800;">AUTOFEATURE</span>=<span style="color: #c20cb9; font-weight: bold;">true</span></pre></div></div>

<p>Additionally, if you&#8217;re on a Mac, I think it&#8217;s worth setting up <a href="http://growl.info/">Growl</a> notifications for autotest. It&#8217;s not a requirement for testing or even using autotest but it&#8217;s a nice enhancement. <a href="http://www.google.com/search?q=autotest+growl">Search Google for &#8220;autotest+growl&#8221;</a> and you&#8217;ll find a number sites that will show you how to set it up.</p>
<p>Note: Growl notifications currently don&#8217;t work with Cucumber but they do work with RSpec and we will get into using RSpec eventually.</p>
<p>Let&#8217;s write a feature test to check our output from the last episode. If you remember, all we did was set up our home page to say &#8220;Hi.&#8221; I&#8217;m going to write a test to make sure it says that.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> script<span style="color: #000000; font-weight: bold;">/</span>generate feature Page
      exists  features<span style="color: #000000; font-weight: bold;">/</span>step_definitions
      create  features<span style="color: #000000; font-weight: bold;">/</span>manage_pages.feature
      create  features<span style="color: #000000; font-weight: bold;">/</span>step_definitions<span style="color: #000000; font-weight: bold;">/</span>page_steps.rb</pre></div></div>

<p>Open up the RAILS_ROOT/features/manage_pages.feature file. I&#8217;m writing my test like so:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">Feature: First <span style="color:#CC00FF; font-weight:bold;">Test</span>
  <span style="color:#9966CC; font-weight:bold;">In</span> order to prove that I can write a quick feature
  Harry
  wants to write a simple test
&nbsp;
  Scenario: Friendly home page
    Given I am on the home page
    <span style="color:#9966CC; font-weight:bold;">Then</span> I should see <span style="color:#996600;">&quot;Hi.&quot;</span></pre></div></div>

<p>Now I save that and run the test with rake:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> rake features
<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000; font-weight: bold;">in</span> <span style="color: #000000; font-weight: bold;">/</span>Users<span style="color: #000000; font-weight: bold;">/</span>harry<span style="color: #000000; font-weight: bold;">/</span>Sites<span style="color: #000000; font-weight: bold;">/</span>jetrecord<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Feature: First Test  <span style="color: #666666; font-style: italic;"># features/manage_pages.feature</span>
  In order to prove that I can <span style="color: #c20cb9; font-weight: bold;">write</span> a quick feature
  Harry
  wants to <span style="color: #c20cb9; font-weight: bold;">write</span> a simple <span style="color: #7a0874; font-weight: bold;">test</span>
  Scenario: Friendly home page  <span style="color: #666666; font-style: italic;"># features/manage_pages.feature:6</span>
    Given I am on the home page <span style="color: #666666; font-style: italic;"># features/manage_pages.feature:7</span>
    Then I should see <span style="color: #ff0000;">&quot;Hi.&quot;</span>     <span style="color: #666666; font-style: italic;"># features/step_definitions/webrat_steps.rb:83</span>
&nbsp;
<span style="color: #000000;">1</span> scenario
<span style="color: #000000;">1</span> step skipped
<span style="color: #000000;">1</span> step pending <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000;">1</span> with no step definition<span style="color: #7a0874; font-weight: bold;">&#41;</span>
&nbsp;
You can use these snippets to implement pending steps <span style="color: #c20cb9; font-weight: bold;">which</span> have no step definition:
&nbsp;
Given <span style="color: #000000; font-weight: bold;">/</span>^I am on the home page$<span style="color: #000000; font-weight: bold;">/</span> <span style="color: #000000; font-weight: bold;">do</span>
end</pre></div></div>

<p>So the test ran but didn&#8217;t pass because I don&#8217;t have a step defined that tells Cucumber how to get to the home page. Thankfully, it gives me a snippet of code at the end of the output there. I open up RAILS_ROOT/features/step_definitions/page_steps.rb and add that in, filling it in with the code it needs to fulfill that step.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">Given <span style="color:#006600; font-weight:bold;">/</span>^I am on the home page$<span style="color:#006600; font-weight:bold;">/</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  visit <span style="color:#996600;">'/'</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Quite simply, I&#8217;m telling Cucumber to visit the URL that looks like &#8216;/&#8217;, which is the home page. I run the test again.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> rake features
<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000; font-weight: bold;">in</span> <span style="color: #000000; font-weight: bold;">/</span>Users<span style="color: #000000; font-weight: bold;">/</span>harry<span style="color: #000000; font-weight: bold;">/</span>Sites<span style="color: #000000; font-weight: bold;">/</span>jetrecord<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Feature: First Test  <span style="color: #666666; font-style: italic;"># features/manage_pages.feature</span>
  In order to prove that I can <span style="color: #c20cb9; font-weight: bold;">write</span> a quick feature
  Harry
  wants to <span style="color: #c20cb9; font-weight: bold;">write</span> a simple <span style="color: #7a0874; font-weight: bold;">test</span>
  Scenario: Friendly home page  <span style="color: #666666; font-style: italic;"># features/manage_pages.feature:6</span>
    Given I am on the home page <span style="color: #666666; font-style: italic;"># features/step_definitions/page_steps.rb:1</span>
      No route matches <span style="color: #ff0000;">&quot;/&quot;</span> with <span style="color: #7a0874; font-weight: bold;">&#123;</span>:<span style="color: #007800;">method</span>=<span style="color: #000000; font-weight: bold;">&gt;</span>:get<span style="color: #7a0874; font-weight: bold;">&#125;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>ActionController::RoutingError<span style="color: #7a0874; font-weight: bold;">&#41;</span>
      <span style="color: #7a0874; font-weight: bold;">&#91;</span>stacktrace info<span style="color: #7a0874; font-weight: bold;">&#93;</span>
&nbsp;
<span style="color: #000000;">1</span> scenario
<span style="color: #000000;">1</span> step failed
<span style="color: #000000;">1</span> step skipped
rake aborted<span style="color: #000000; font-weight: bold;">!</span></pre></div></div>

<p>Okay, we&#8217;re getting somewhere but we didn&#8217;t move very far. Cucumber barfed because it was looking for a route defined by the Rails application. Cucumber runs in the context of the Rails application, not of the whole site architecture. It doesn&#8217;t care that I have an index file in the public directory because it&#8217;s not actually crawling my web site like a browser would.</p>
<p>This is not necessarily a bad thing. If I wanted I could install <a href="http://seleniumhq.org/">Selenium</a>, hook into it via Cucumber and re-run the tests. This would catch my index.html page because Selenium acts like a web browser. Eventually, I can see myself doing that because I&#8217;m going to need to test some Ajax interactions. But right now it&#8217;s not necessary.</p>
<p>In order to run my application I need to remove the index file anyway, so the real solution is to define my home page in Rails and then test again. To do that I need to add a route to my RAILS_ROOT/config/routes.rb file. Then I need to define the controller and a view for the home page.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># config/routes.rb</span>
<span style="color:#6666ff; font-weight:bold;">ActionController::Routing::Routes</span>.<span style="color:#9900CC;">draw</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>map<span style="color:#006600; font-weight:bold;">|</span>
  map.<span style="color:#9900CC;">root</span> <span style="color:#ff3333; font-weight:bold;">:controller</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'pages'</span>, <span style="color:#ff3333; font-weight:bold;">:action</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'home'</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>


<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># app/controllers/pages_controller.rb</span>
<span style="color:#9966CC; font-weight:bold;">class</span> PagesController <span style="color:#006600; font-weight:bold;">&lt;</span> ApplicationController
  <span style="color:#9966CC; font-weight:bold;">def</span> home
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>


<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># app/views/pages/home.html.erb</span>
Hi.</pre></div></div>


<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"># app/views/layouts/application.html.erb
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;
        &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta http-equiv=&quot;Content-type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;
    &lt;title&gt;Hello&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    <span style="color:#006600; font-weight:bold;">&lt;%</span>= <span style="color:#9966CC; font-weight:bold;">yield</span> <span style="color:#006600; font-weight:bold;">%&gt;</span>
  &lt;/body&gt;
&lt;/html&gt;</pre></div></div>

<p>Alright. One last thing. We need to rename or remove the public/index.html file. I&#8217;m just going to remove it. Then we try the test again.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> git <span style="color: #c20cb9; font-weight: bold;">rm</span> public<span style="color: #000000; font-weight: bold;">/</span>index.html
$<span style="color: #000000; font-weight: bold;">&gt;</span> rake features
<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000; font-weight: bold;">in</span> <span style="color: #000000; font-weight: bold;">/</span>Users<span style="color: #000000; font-weight: bold;">/</span>harry<span style="color: #000000; font-weight: bold;">/</span>Sites<span style="color: #000000; font-weight: bold;">/</span>jetrecord<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Feature: First Test  <span style="color: #666666; font-style: italic;"># features/manage_pages.feature</span>
  In order to prove that I can <span style="color: #c20cb9; font-weight: bold;">write</span> a quick feature
  Harry
  wants to <span style="color: #c20cb9; font-weight: bold;">write</span> a simple <span style="color: #7a0874; font-weight: bold;">test</span>
  Scenario: Friendly home page  <span style="color: #666666; font-style: italic;"># features/manage_pages.feature:6</span>
    Given I am on the home page <span style="color: #666666; font-style: italic;"># features/step_definitions/page_steps.rb:1</span>
    Then I should see <span style="color: #ff0000;">&quot;Hi.&quot;</span>     <span style="color: #666666; font-style: italic;"># features/step_definitions/webrat_steps.rb:83</span>
&nbsp;
<span style="color: #000000;">1</span> scenario
<span style="color: #000000;">2</span> steps passed</pre></div></div>

<p>Bingo! Now let&#8217;s try running autotest.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> autotest
loading autotest<span style="color: #000000; font-weight: bold;">/</span>cucumber_rails_rspec
<span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>ruby-enterprise-1.8.6-<span style="color: #000000;">20080810</span><span style="color: #000000; font-weight: bold;">/</span>bin<span style="color: #000000; font-weight: bold;">/</span>ruby <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>ruby-enterprise-1.8.6-<span style="color: #000000;">20080810</span><span style="color: #000000; font-weight: bold;">/</span>lib<span style="color: #000000; font-weight: bold;">/</span>ruby<span style="color: #000000; font-weight: bold;">/</span>gems<span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">1.8</span><span style="color: #000000; font-weight: bold;">/</span>gems<span style="color: #000000; font-weight: bold;">/</span>cucumber-0.1.15<span style="color: #000000; font-weight: bold;">/</span>bin<span style="color: #000000; font-weight: bold;">/</span>cucumber features <span style="color: #660033;">--format</span> progress <span style="color: #660033;">--format</span> autotest <span style="color: #660033;">--color</span> <span style="color: #660033;">--out</span> <span style="color: #000000; font-weight: bold;">/</span>var<span style="color: #000000; font-weight: bold;">/</span>folders<span style="color: #000000; font-weight: bold;">/</span>f1<span style="color: #000000; font-weight: bold;">/</span>f1HkdqHjFe43ByUhxU-FO++++TI<span style="color: #000000; font-weight: bold;">/</span>-Tmp-<span style="color: #000000; font-weight: bold;">/</span>autotest-cucumber.5265.0 
..</pre></div></div>

<p>Great! It&#8217;s working. Two passing steps (the two dots at the end) and autotest is still running, waiting for me to do something stupid. I&#8217;m going to introduce an error into home.html.erb.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># app/views/pages/home.html.erb</span>
Hello.</pre></div></div>

<p>And then back to the terminal.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>ruby-enterprise-1.8.6-<span style="color: #000000;">20080810</span><span style="color: #000000; font-weight: bold;">/</span>bin<span style="color: #000000; font-weight: bold;">/</span>ruby <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>ruby-enterprise-1.8.6-<span style="color: #000000;">20080810</span><span style="color: #000000; font-weight: bold;">/</span>lib<span style="color: #000000; font-weight: bold;">/</span>ruby<span style="color: #000000; font-weight: bold;">/</span>gems<span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">1.8</span><span style="color: #000000; font-weight: bold;">/</span>gems<span style="color: #000000; font-weight: bold;">/</span>cucumber-0.1.15<span style="color: #000000; font-weight: bold;">/</span>bin<span style="color: #000000; font-weight: bold;">/</span>cucumber features <span style="color: #660033;">--format</span> progress <span style="color: #660033;">--format</span> autotest <span style="color: #660033;">--color</span> <span style="color: #660033;">--out</span> <span style="color: #000000; font-weight: bold;">/</span>var<span style="color: #000000; font-weight: bold;">/</span>folders<span style="color: #000000; font-weight: bold;">/</span>f1<span style="color: #000000; font-weight: bold;">/</span>f1HkdqHjFe43ByUhxU-FO++++TI<span style="color: #000000; font-weight: bold;">/</span>-Tmp-<span style="color: #000000; font-weight: bold;">/</span>autotest-cucumber.5265.1 
.F
&nbsp;
Failed:
&nbsp;
<span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>
expected: <span style="color: #000000; font-weight: bold;">/</span>Hi.<span style="color: #000000; font-weight: bold;">/</span>m,
     got: <span style="color: #ff0000;">&quot;&lt;!DOCTYPE html PUBLIC <span style="color: #000099; font-weight: bold;">\&quot;</span>-//W3C//DTD XHTML 1.0 Transitional//EN<span style="color: #000099; font-weight: bold;">\&quot;</span><span style="color: #000099; font-weight: bold;">\n</span>        <span style="color: #000099; font-weight: bold;">\&quot;</span>http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd<span style="color: #000099; font-weight: bold;">\&quot;</span>&gt;<span style="color: #000099; font-weight: bold;">\n</span>&lt;html&gt;<span style="color: #000099; font-weight: bold;">\n</span>  &lt;head&gt;<span style="color: #000099; font-weight: bold;">\n</span>    &lt;meta http-equiv=<span style="color: #000099; font-weight: bold;">\&quot;</span>Content-type<span style="color: #000099; font-weight: bold;">\&quot;</span> content=<span style="color: #000099; font-weight: bold;">\&quot;</span>text/html; charset=utf-8<span style="color: #000099; font-weight: bold;">\&quot;</span> /&gt;<span style="color: #000099; font-weight: bold;">\n</span>    &lt;title&gt;Hello&lt;/title&gt;<span style="color: #000099; font-weight: bold;">\n</span>    <span style="color: #000099; font-weight: bold;">\n</span>  &lt;/head&gt;<span style="color: #000099; font-weight: bold;">\n</span>  &lt;body&gt;<span style="color: #000099; font-weight: bold;">\n</span>    Hello.<span style="color: #000099; font-weight: bold;">\n</span>  &lt;/body&gt;<span style="color: #000099; font-weight: bold;">\n</span>&lt;/html&gt;&quot;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>using =~<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Diff:
<span style="color: #000000; font-weight: bold;">@@</span> -<span style="color: #000000;">1</span>,<span style="color: #000000;">2</span> +<span style="color: #000000;">1</span>,<span style="color: #000000;">2</span> <span style="color: #000000; font-weight: bold;">@@</span>
-<span style="color: #000000; font-weight: bold;">/</span>Hi.<span style="color: #000000; font-weight: bold;">/</span>m
+<span style="color: #ff0000;">&quot;&lt;!DOCTYPE html PUBLIC <span style="color: #000099; font-weight: bold;">\&quot;</span>-//W3C//DTD XHTML 1.0 Transitional//EN<span style="color: #000099; font-weight: bold;">\&quot;</span><span style="color: #000099; font-weight: bold;">\n</span>        <span style="color: #000099; font-weight: bold;">\&quot;</span>http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd<span style="color: #000099; font-weight: bold;">\&quot;</span>&gt;<span style="color: #000099; font-weight: bold;">\n</span>&lt;html&gt;<span style="color: #000099; font-weight: bold;">\n</span>  &lt;head&gt;<span style="color: #000099; font-weight: bold;">\n</span>    &lt;meta http-equiv=<span style="color: #000099; font-weight: bold;">\&quot;</span>Content-type<span style="color: #000099; font-weight: bold;">\&quot;</span> content=<span style="color: #000099; font-weight: bold;">\&quot;</span>text/html; charset=utf-8<span style="color: #000099; font-weight: bold;">\&quot;</span> /&gt;<span style="color: #000099; font-weight: bold;">\n</span>    &lt;title&gt;Hello&lt;/title&gt;<span style="color: #000099; font-weight: bold;">\n</span>    <span style="color: #000099; font-weight: bold;">\n</span>  &lt;/head&gt;<span style="color: #000099; font-weight: bold;">\n</span>  &lt;body&gt;<span style="color: #000099; font-weight: bold;">\n</span>    Hello.<span style="color: #000099; font-weight: bold;">\n</span>  &lt;/body&gt;<span style="color: #000099; font-weight: bold;">\n</span>&lt;/html&gt;&quot;</span>
&nbsp;
<span style="color: #7a0874; font-weight: bold;">&#91;</span>stacktrace info<span style="color: #7a0874; font-weight: bold;">&#93;</span>
&nbsp;
.<span style="color: #000000; font-weight: bold;">/</span>features<span style="color: #000000; font-weight: bold;">/</span>step_definitions<span style="color: #000000; font-weight: bold;">/</span>webrat_steps.rb:<span style="color: #000000;">84</span>:<span style="color: #000000; font-weight: bold;">in</span> <span style="color: #000000; font-weight: bold;">`</span>Then <span style="color: #000000; font-weight: bold;">/</span>^I should see <span style="color: #ff0000;">&quot;(.*)&quot;</span>$<span style="color: #000000; font-weight: bold;">/</span><span style="color: #ff0000;">'
features/manage_pages.feature:8:in `Then I should see &quot;Hi.&quot;'</span></pre></div></div>

<p>And that&#8217;s how it works. We write a test, it fails, we write some code to pass the test, it passes.</p>
<p>I have to apologize. This post has already become too long. I like the direction it took but I don&#8217;t think moving on to writing an actual Jetrecord feature would be wise right now. So, really, next time I&#8217;m going to write tests for the first feature. I&#8217;ll even include some RSpec in there.</p>
<h2>Coming Up in the Next Episode</h2>
<p>No more rhymes now, I mean it. I&#8217;m going to write some tests for the first feature. Until next time, cheers and happy flying.</p>
<h2>Material You May Find Useful Related to This Episode</h2>
<p><a href="http://cukes.info/">Cucumber: Behaviour Driven Development with elegance and joy</a></p>
<p><a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/"><img style="border-width:0" src="http://i.creativecommons.org/l/by-sa/3.0/us/88x31.png" alt="Creative Commons License" /></a> <span><a href="http://harrylove.org/the-building-of-jetrecord">The Building of Jetrecord</a></span> by <a rel="cc:attributionURL" href="http://harrylove.org/">Harry Love</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>. When code, text, or media in this series is not created by me and is not in the public domain I will provide links to their sources from which you can find their respective licenses and terms of use.</p>
]]></content:encoded>
			<wfw:commentRss>http://harrylove.org/2009/01/19/the-building-of-jetrecord-episode-4-cucumbers-and-webrats.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
<enclosure url="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-4.mov" length="75423427" type="video/quicktime" />
<enclosure url="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-4.mp4" length="19373425" type="video/mp4" />
		</item>
		<item>
		<title>The Building of Jetrecord: Episode 3: Git, Capistrano, and A Test Release</title>
		<link>http://harrylove.org/2009/01/12/the-building-of-jetrecord-episode-3-git-capistrano-and-a-test-release.html</link>
		<comments>http://harrylove.org/2009/01/12/the-building-of-jetrecord-episode-3-git-capistrano-and-a-test-release.html#comments</comments>
		<pubDate>Mon, 12 Jan 2009 16:08:48 +0000</pubDate>
		<dc:creator>Harry Love</dc:creator>
				<category><![CDATA[Portfolio]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[Video]]></category>
		<category><![CDATA[Capistrano]]></category>
		<category><![CDATA[Git]]></category>

		<guid isPermaLink="false">http://harrylove.org/?p=598</guid>
		<description><![CDATA[What Mayer thinks will be essential for continued innovation is for Google to keep its sense of fearlessness. &#8220;I like to launch [products] early and often. That has become my mantra,&#8221; she says. She mentions Apple Computer and Madonna. &#8220;Nobody remembers the Sex Book or the Newton. Consumers remember your average over time. That philosophy [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>What Mayer thinks will be essential for continued innovation is for Google to keep its sense of fearlessness. &#8220;I like to launch [products] early and often. That has become my mantra,&#8221; she says. She mentions Apple Computer and Madonna. &#8220;Nobody remembers the Sex Book or the Newton. Consumers remember your average over time. That philosophy frees you from fear.&#8221; &#8212; <a href="http://www.businessweek.com/magazine/content/05_40/b3953093.htm">Managing Google&#8217;s Idea Factory</a></p>
</blockquote>
<p>In <a href="/2008/10/29/the-building-of-jetrecord-episode-2-tell-me-a-story">the last episode</a> I briefly sketched out what I want to build. In this episode I&#8217;m going to start the project by creating a Git repository and deploying a test release with Capistrano.</p>
<p>Cue film.</p>
<p><object width="601" height="453"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=2791065&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" /><embed src="http://vimeo.com/moogaloop.swf?clip_id=2791065&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="601" height="453"></embed></object></p>
<p>Download (right-click and save) <a href="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-3.mov">QuickTime MOV</a> <small>(74 Mb)</small> | <a href="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-3.mp4">QuickTime iPod MP4</a> <small>(9 Mb)</small> | <a href="http://jetrecord.bingodisk.com/public/videos/jetrecord/jetrecord-episode3.qt.smil">QuickTime SMIL with captions</a> <small>(74 Mb) [Note: you must choose to open the SMIL file with QuickTime]</small></p>
<h2>A Few Quick Changes</h2>
<p>First, I changed my mind about setting up the complicated user management system from the beginning. I realized that I would be violating one of the tenets of Agile/XP: <abbr title="You Ain't Gonna Need It">YAGNI</abbr>. It reminds us to keep focused on the current feature and save details of any related features for the stories that describe them. Flights don&#8217;t need to know about the implementation details of users. Therefore, I&#8217;m only going to worry about flights for the time being. When called for I will stub out a User model, authentication, et cetera.</p>
<p>Second, I forgot to add a couple gems that will help us write stories and integration tests: Webrat and FactoryGirl. <a href="http://github.com/brynary/webrat/tree/master">Webrat</a> is a library for writing tests that crawl your site in memory, visiting links, testing inputs, submitting forms, and so on. <a href="http://www.thoughtbot.com/projects/factory_girl">FactoryGirl</a> is a library that replaces test fixtures with factories. You can install both with RubyGems. We&#8217;re not going to need them until the next episode; I just wanted to mention them now.</p>
<h2>Some Conventions</h2>
<ul>
<li><code>$&gt;</code> is the command line prompt. Anything coming after it is a command that I&#8217;m typing into my terminal.</li>
<li>RAILS_ROOT is the root directory of the Rails app.</li>
<li>REMOTE is my remote server</li>
</ul>
<h2>Starting the Rails App</h2>
<p>Depending on your personality this is either the best part of the project or the worst: the blank canvas. You installed Rails and all of the prerequisites I listed in <a href="/2008/10/29/the-building-of-jetrecord-episode-2-tell-me-a-story">episode 2</a>. All of our hopes and dreams for a perfect app begin with this command:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> rails jetrecord</pre></div></div>

<p>I have set up my development environment to use Apache + <a href="http://www.modrails.com/">Passenger</a> to serve the site and have configured a virtualhost on my local machine to use &#8220;ld.com&#8221; for the site. (It doesn&#8217;t matter if ld.com actually exists or not. As far as my machine is concerned, it points to my local Rails app.) If you&#8217;re using Mongrel, Webrick, Thin, or something else as your development server you can run <code>script/server</code> from the RAILS_ROOT.</p>
<p>Since I&#8217;m using Apache and I&#8217;ve set it to launch at boot time I don&#8217;t need to use <code>script/server</code>. I can just browse to ld.com. When I do, I see this.</p>
<p><img src="http://jetrecord.bingodisk.com/public/images/jetrecord/jetrecord-beginning.png" alt="Rails app home screen for Jetrecord" /></p>
<p>Great! We&#8217;re off and running.</p>
<h2>Setting Up Our Repository with Git</h2>
<p>Git is installed on my machine. To make a repository, I <code title="change directory">cd</code> to RAILS_ROOT and do this:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> git init
Initialized empty Git repository <span style="color: #000000; font-weight: bold;">in</span> <span style="color: #000000; font-weight: bold;">/</span>Users<span style="color: #000000; font-weight: bold;">/</span>harry<span style="color: #000000; font-weight: bold;">/</span>Sites<span style="color: #000000; font-weight: bold;">/</span>jetrecord<span style="color: #000000; font-weight: bold;">/</span>.git<span style="color: #000000; font-weight: bold;">/</span>
&nbsp;
$<span style="color: #000000; font-weight: bold;">&gt;</span> git status
<span style="color: #666666; font-style: italic;"># On branch master</span>
<span style="color: #666666; font-style: italic;">#</span>
<span style="color: #666666; font-style: italic;"># Initial commit</span>
<span style="color: #666666; font-style: italic;">#</span>
<span style="color: #666666; font-style: italic;"># Untracked files:</span>
<span style="color: #666666; font-style: italic;">#   (use &quot;git add ...&quot; to include in what will be committed)</span>
<span style="color: #666666; font-style: italic;">#</span>
<span style="color: #666666; font-style: italic;">#	README</span>
<span style="color: #666666; font-style: italic;">#	Rakefile</span>
<span style="color: #666666; font-style: italic;">#	app/</span>
<span style="color: #666666; font-style: italic;">#	config/</span>
<span style="color: #666666; font-style: italic;">#	doc/</span>
<span style="color: #666666; font-style: italic;">#	log/</span>
<span style="color: #666666; font-style: italic;">#	public/</span>
<span style="color: #666666; font-style: italic;">#	script/</span>
<span style="color: #666666; font-style: italic;">#	test/</span>
nothing added to commit but untracked files present <span style="color: #7a0874; font-weight: bold;">&#40;</span>use <span style="color: #ff0000;">&quot;git add&quot;</span> to track<span style="color: #7a0874; font-weight: bold;">&#41;</span></pre></div></div>

<p>If I run <code>git add</code> right now, Git will recursively add everything in RAILS_ROOT to the repository. But I don&#8217;t want that. There are a few items I don&#8217;t want to track. To have Git automatically ignore those items, I will create a .gitignore file. Each line tells Git what to ignore.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #c20cb9; font-weight: bold;">nano</span> <span style="color: #660033;">-w</span> .gitignore                                                                 
&nbsp;
config<span style="color: #000000; font-weight: bold;">/</span>database.yml
<span style="color: #000000; font-weight: bold;">*</span>~
<span style="color: #000000; font-weight: bold;">*</span>.cache
<span style="color: #000000; font-weight: bold;">*</span>.log
<span style="color: #000000; font-weight: bold;">*</span>.pid
tmp<span style="color: #000000; font-weight: bold;">/**/*</span>
.DS\_Store
db<span style="color: #000000; font-weight: bold;">/</span>cstore<span style="color: #000000; font-weight: bold;">/**</span>
doc<span style="color: #000000; font-weight: bold;">/*</span>.dot
doc<span style="color: #000000; font-weight: bold;">/</span>api
doc<span style="color: #000000; font-weight: bold;">/</span>app
doc<span style="color: #000000; font-weight: bold;">/</span>plugins
coverage<span style="color: #000000; font-weight: bold;">/*</span>
db<span style="color: #000000; font-weight: bold;">/*</span>.sqlite3
<span style="color: #000000; font-weight: bold;">*</span>.tmproj
Capfile</pre></div></div>

<p>I save that file. Now I add everything to the repository and make the initial commit.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> git add .
$<span style="color: #000000; font-weight: bold;">&gt;</span> git commit <span style="color: #660033;">-m</span> <span style="color: #ff0000;">&quot;First commit&quot;</span>
&nbsp;
Created initial commit 4d55351: First commit
 <span style="color: #000000;">46</span> files changed, <span style="color: #000000;">8525</span> insertions<span style="color: #7a0874; font-weight: bold;">&#40;</span>+<span style="color: #7a0874; font-weight: bold;">&#41;</span>, <span style="color: #000000;">0</span> deletions<span style="color: #7a0874; font-weight: bold;">&#40;</span>-<span style="color: #7a0874; font-weight: bold;">&#41;</span>
 create mode <span style="color: #000000;">100644</span> .gitignore
 create mode <span style="color: #000000;">100644</span> README
 create mode <span style="color: #000000;">100644</span> Rakefile
 create mode <span style="color: #000000;">100644</span> app<span style="color: #000000; font-weight: bold;">/</span>controllers<span style="color: #000000; font-weight: bold;">/</span>application.rb
 create mode <span style="color: #000000;">100644</span> app<span style="color: #000000; font-weight: bold;">/</span>helpers<span style="color: #000000; font-weight: bold;">/</span>application_helper.rb
 create mode <span style="color: #000000;">100644</span> config<span style="color: #000000; font-weight: bold;">/</span>boot.rb
 create mode <span style="color: #000000;">100644</span> config<span style="color: #000000; font-weight: bold;">/</span>environment.rb
 create mode <span style="color: #000000;">100644</span> config<span style="color: #000000; font-weight: bold;">/</span>environments<span style="color: #000000; font-weight: bold;">/</span>development.rb
 create mode <span style="color: #000000;">100644</span> config<span style="color: #000000; font-weight: bold;">/</span>environments<span style="color: #000000; font-weight: bold;">/</span>production.rb
 create mode <span style="color: #000000;">100644</span> config<span style="color: #000000; font-weight: bold;">/</span>environments<span style="color: #000000; font-weight: bold;">/</span>test.rb
 create mode <span style="color: #000000;">100644</span> config<span style="color: #000000; font-weight: bold;">/</span>initializers<span style="color: #000000; font-weight: bold;">/</span>inflections.rb
 create mode <span style="color: #000000;">100644</span> config<span style="color: #000000; font-weight: bold;">/</span>initializers<span style="color: #000000; font-weight: bold;">/</span>mime_types.rb
 create mode <span style="color: #000000;">100644</span> config<span style="color: #000000; font-weight: bold;">/</span>initializers<span style="color: #000000; font-weight: bold;">/</span>new_rails_defaults.rb
 create mode <span style="color: #000000;">100644</span> config<span style="color: #000000; font-weight: bold;">/</span>locales<span style="color: #000000; font-weight: bold;">/</span>en.yml
 create mode <span style="color: #000000;">100644</span> config<span style="color: #000000; font-weight: bold;">/</span>routes.rb
 create mode <span style="color: #000000;">100644</span> doc<span style="color: #000000; font-weight: bold;">/</span>README_FOR_APP
 create mode <span style="color: #000000;">100644</span> public<span style="color: #000000; font-weight: bold;">/</span>404.html
 create mode <span style="color: #000000;">100644</span> public<span style="color: #000000; font-weight: bold;">/</span>422.html
 create mode <span style="color: #000000;">100644</span> public<span style="color: #000000; font-weight: bold;">/</span>500.html
 create mode <span style="color: #000000;">100755</span> public<span style="color: #000000; font-weight: bold;">/</span>dispatch.cgi
 create mode <span style="color: #000000;">100755</span> public<span style="color: #000000; font-weight: bold;">/</span>dispatch.fcgi
 create mode <span style="color: #000000;">100755</span> public<span style="color: #000000; font-weight: bold;">/</span>dispatch.rb
 create mode <span style="color: #000000;">100644</span> public<span style="color: #000000; font-weight: bold;">/</span>favicon.ico
 create mode <span style="color: #000000;">100644</span> public<span style="color: #000000; font-weight: bold;">/</span>images<span style="color: #000000; font-weight: bold;">/</span>rails.png
 create mode <span style="color: #000000;">100644</span> public<span style="color: #000000; font-weight: bold;">/</span>index.html
 create mode <span style="color: #000000;">100644</span> public<span style="color: #000000; font-weight: bold;">/</span>javascripts<span style="color: #000000; font-weight: bold;">/</span>application.js
 create mode <span style="color: #000000;">100644</span> public<span style="color: #000000; font-weight: bold;">/</span>javascripts<span style="color: #000000; font-weight: bold;">/</span>controls.js
 create mode <span style="color: #000000;">100644</span> public<span style="color: #000000; font-weight: bold;">/</span>javascripts<span style="color: #000000; font-weight: bold;">/</span>dragdrop.js
 create mode <span style="color: #000000;">100644</span> public<span style="color: #000000; font-weight: bold;">/</span>javascripts<span style="color: #000000; font-weight: bold;">/</span>effects.js
 create mode <span style="color: #000000;">100644</span> public<span style="color: #000000; font-weight: bold;">/</span>javascripts<span style="color: #000000; font-weight: bold;">/</span>prototype.js
 create mode <span style="color: #000000;">100644</span> public<span style="color: #000000; font-weight: bold;">/</span>robots.txt
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>about
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>console
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>dbconsole
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>destroy
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>generate
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>performance<span style="color: #000000; font-weight: bold;">/</span>benchmarker
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>performance<span style="color: #000000; font-weight: bold;">/</span>profiler
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>performance<span style="color: #000000; font-weight: bold;">/</span>request
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>plugin
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>process<span style="color: #000000; font-weight: bold;">/</span>inspector
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>process<span style="color: #000000; font-weight: bold;">/</span>reaper
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>process<span style="color: #000000; font-weight: bold;">/</span>spawner
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>runner
 create mode <span style="color: #000000;">100755</span> script<span style="color: #000000; font-weight: bold;">/</span>server
 create mode <span style="color: #000000;">100644</span> test<span style="color: #000000; font-weight: bold;">/</span>performance<span style="color: #000000; font-weight: bold;">/</span>browsing_test.rb
 create mode <span style="color: #000000;">100644</span> test<span style="color: #000000; font-weight: bold;">/</span>test_helper.rb</pre></div></div>

<p>The application is now being tracked by Git. One last thing before we move on to Capistrano. I&#8217;m going to change the default index page to show a simple &#8220;Hello, World.&#8221; I&#8217;m using <a href="http://macromates.com/">TextMate</a>, which is the reason for the <code>mate</code> command.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> mate public<span style="color: #000000; font-weight: bold;">/</span>index.html</pre></div></div>

<p>All I did was replace the dynamic stuff on the home page with &#8220;Hi.&#8221; You&#8217;ll see it in a minute.  Alright. That&#8217;s all I&#8217;m going to do for the first release. Now I need to move the repository to a remote location so I can pull from it when I release new code.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #c20cb9; font-weight: bold;">ssh</span> REMOTE
REMOTE $<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #c20cb9; font-weight: bold;">mkdir</span> etc<span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>jetrecord.git
REMOTE $<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #7a0874; font-weight: bold;">cd</span> etc<span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>jetrecord.git
REMOTE $<span style="color: #000000; font-weight: bold;">&gt;</span> git init <span style="color: #660033;">--bare</span>
Initialized empty Git repository <span style="color: #000000; font-weight: bold;">in</span> <span style="color: #000000; font-weight: bold;">/</span>home<span style="color: #000000; font-weight: bold;">/</span>user<span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>jetrecord.git<span style="color: #000000; font-weight: bold;">/</span>
REMOTE $<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #7a0874; font-weight: bold;">exit</span>
Connection closed
&nbsp;
$<span style="color: #000000; font-weight: bold;">&gt;</span> git remote add origin <span style="color: #c20cb9; font-weight: bold;">ssh</span>:<span style="color: #000000; font-weight: bold;">//</span>user<span style="color: #000000; font-weight: bold;">@</span>REMOTE<span style="color: #000000; font-weight: bold;">/</span>home<span style="color: #000000; font-weight: bold;">/</span>user<span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>jetrecord.git
$<span style="color: #000000; font-weight: bold;">&gt;</span> git push origin master
Counting objects: <span style="color: #000000;">68</span>, done.
Compressing objects: <span style="color: #000000;">100</span><span style="color: #000000; font-weight: bold;">%</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000;">61</span><span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">61</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>, done.
Writing objects: <span style="color: #000000;">100</span><span style="color: #000000; font-weight: bold;">%</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000;">68</span><span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">68</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>, <span style="color: #000000;">80.67</span> KiB, done.
Total <span style="color: #000000;">68</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>delta <span style="color: #000000;">17</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>, reused <span style="color: #000000;">0</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>delta <span style="color: #000000;">0</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>
To <span style="color: #c20cb9; font-weight: bold;">ssh</span>:<span style="color: #000000; font-weight: bold;">//</span>user<span style="color: #000000; font-weight: bold;">@</span>REMOTE<span style="color: #000000; font-weight: bold;">/</span>home<span style="color: #000000; font-weight: bold;">/</span>user<span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>jetrecord.git
 <span style="color: #000000; font-weight: bold;">*</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>new branch<span style="color: #7a0874; font-weight: bold;">&#93;</span>      master -<span style="color: #000000; font-weight: bold;">&gt;</span> master</pre></div></div>

<p>Good. Now I have a remote Git repository. Any changes that I push will go to this remote location that will also be accessible to Capistrano. And so we move on to Capistrano.</p>
<h2>Capistrano</h2>
<p>I have the Capistrano gem installed. I just need to initialize it for my app and then fix up my deployment recipe.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> capify .
<span style="color: #7a0874; font-weight: bold;">&#91;</span>add<span style="color: #7a0874; font-weight: bold;">&#93;</span> writing <span style="color: #ff0000;">'./Capfile'</span>
<span style="color: #7a0874; font-weight: bold;">&#91;</span>add<span style="color: #7a0874; font-weight: bold;">&#93;</span> writing <span style="color: #ff0000;">'./config/deploy.rb'</span>
<span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #000000; font-weight: bold;">done</span><span style="color: #7a0874; font-weight: bold;">&#93;</span> capified<span style="color: #000000; font-weight: bold;">!</span>
&nbsp;
$<span style="color: #000000; font-weight: bold;">&gt;</span> mate config<span style="color: #000000; font-weight: bold;">/</span>deploy.rb</pre></div></div>

<p>Here&#8217;s my deploy.rb</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">set <span style="color:#ff3333; font-weight:bold;">:use_sudo</span>, <span style="color:#0000FF; font-weight:bold;">false</span>
set <span style="color:#ff3333; font-weight:bold;">:application</span>, <span style="color:#996600;">&quot;jetrecord&quot;</span>
set <span style="color:#ff3333; font-weight:bold;">:repository</span>,  <span style="color:#996600;">&quot;ssh://user@REMOTE/home/user/etc/git/jetrecord.git&quot;</span>
set <span style="color:#ff3333; font-weight:bold;">:deploy_to</span>, <span style="color:#996600;">&quot;/var/www/apps/#{application}&quot;</span>
set <span style="color:#ff3333; font-weight:bold;">:user</span>, <span style="color:#996600;">'user'</span>
set <span style="color:#ff3333; font-weight:bold;">:runner</span>, user
set <span style="color:#ff3333; font-weight:bold;">:scm</span>, <span style="color:#ff3333; font-weight:bold;">:git</span>
set <span style="color:#ff3333; font-weight:bold;">:domain</span>, <span style="color:#996600;">'REMOTE'</span>
role <span style="color:#ff3333; font-weight:bold;">:app</span>, domain
role <span style="color:#ff3333; font-weight:bold;">:web</span>, domain
role <span style="color:#ff3333; font-weight:bold;">:db</span>,  domain, <span style="color:#ff3333; font-weight:bold;">:primary</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">true</span>
set <span style="color:#ff3333; font-weight:bold;">:server_name</span>, <span style="color:#996600;">&quot;landingdeparting.com&quot;</span>
set <span style="color:#ff3333; font-weight:bold;">:server_alias</span>, <span style="color:#996600;">&quot;*.landingdeparting.com&quot;</span>
depend <span style="color:#ff3333; font-weight:bold;">:remote</span>, <span style="color:#ff3333; font-weight:bold;">:command</span>, <span style="color:#ff3333; font-weight:bold;">:gem</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Allow ssh to use ssh keys</span>
set <span style="color:#ff3333; font-weight:bold;">:ssh_options</span>, <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#ff3333; font-weight:bold;">:forward_agent</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">true</span> <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
deploy.<span style="color:#9900CC;">task</span> <span style="color:#ff3333; font-weight:bold;">:restart</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#008000; font-style:italic;"># Restart Passenger</span>
  run <span style="color:#996600;">&quot;touch #{current_path}/tmp/restart.txt&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
deploy.<span style="color:#9900CC;">task</span> <span style="color:#ff3333; font-weight:bold;">:symlinks</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  run <span style="color:#996600;">&quot;ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
after <span style="color:#ff3333; font-weight:bold;">:deploy</span>, <span style="color:#996600;">'deploy:cleanup'</span>
after <span style="color:#996600;">'deploy:update_code'</span>, <span style="color:#996600;">'deploy:symlinks'</span></pre></div></div>

<p>Cool. Commit the changes.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> git add .
$<span style="color: #000000; font-weight: bold;">&gt;</span> git commit <span style="color: #660033;">-a</span> <span style="color: #660033;">-m</span> <span style="color: #ff0000;">&quot;First stab at Capistrano deployment recipe&quot;</span>
Created commit e80f9dc: First stab at Capistrano deployment recipe
 <span style="color: #000000;">1</span> files changed, <span style="color: #000000;">28</span> insertions<span style="color: #7a0874; font-weight: bold;">&#40;</span>+<span style="color: #7a0874; font-weight: bold;">&#41;</span>, <span style="color: #000000;">0</span> deletions<span style="color: #7a0874; font-weight: bold;">&#40;</span>-<span style="color: #7a0874; font-weight: bold;">&#41;</span>
 create mode <span style="color: #000000;">100644</span> config<span style="color: #000000; font-weight: bold;">/</span>deploy.rb
&nbsp;
$<span style="color: #000000; font-weight: bold;">&gt;</span> git push
Counting objects: <span style="color: #000000;">6</span>, done.
Compressing objects: <span style="color: #000000;">100</span><span style="color: #000000; font-weight: bold;">%</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000;">4</span><span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">4</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>, done.
Writing objects: <span style="color: #000000;">100</span><span style="color: #000000; font-weight: bold;">%</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000;">4</span><span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">4</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>, <span style="color: #000000;">775</span> bytes, done.
Total <span style="color: #000000;">4</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>delta <span style="color: #000000;">2</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>, reused <span style="color: #000000;">0</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>delta <span style="color: #000000;">0</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>
To <span style="color: #c20cb9; font-weight: bold;">ssh</span>:<span style="color: #000000; font-weight: bold;">//</span>user<span style="color: #000000; font-weight: bold;">@</span>REMOTE<span style="color: #000000; font-weight: bold;">/</span>home<span style="color: #000000; font-weight: bold;">/</span>user<span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>jetrecord.git
   ba68814..e80f9dc  master -<span style="color: #000000; font-weight: bold;">&gt;</span> master</pre></div></div>

<p>Okay, one more thing on the remote end. In my deploy recipe I added a symlink from the database.yml file in the shared directory to the RAILS_ROOT/config directory. I&#8217;m doing this because I don&#8217;t want to track database.yml in my Git repository and it&#8217;s cheaper to create a symlink than to copy an actual file from one release to the next.  But I still need to create the file manually. I&#8217;ll do that now.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #c20cb9; font-weight: bold;">ssh</span> REMOTE
REMOTE $<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #c20cb9; font-weight: bold;">mkdir</span> <span style="color: #000000; font-weight: bold;">/</span>var<span style="color: #000000; font-weight: bold;">/</span>www<span style="color: #000000; font-weight: bold;">/</span>apps<span style="color: #000000; font-weight: bold;">/</span>jetrecord
REMOTE $<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #c20cb9; font-weight: bold;">mkdir</span> <span style="color: #000000; font-weight: bold;">/</span>var<span style="color: #000000; font-weight: bold;">/</span>www<span style="color: #000000; font-weight: bold;">/</span>apps<span style="color: #000000; font-weight: bold;">/</span>jetrecord<span style="color: #000000; font-weight: bold;">/</span>shared
REMOTE $<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #c20cb9; font-weight: bold;">mkdir</span> <span style="color: #000000; font-weight: bold;">/</span>var<span style="color: #000000; font-weight: bold;">/</span>www<span style="color: #000000; font-weight: bold;">/</span>apps<span style="color: #000000; font-weight: bold;">/</span>jetrecord<span style="color: #000000; font-weight: bold;">/</span>shared<span style="color: #000000; font-weight: bold;">/</span>config
REMOTE $<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #c20cb9; font-weight: bold;">nano</span> <span style="color: #660033;">-w</span> <span style="color: #000000; font-weight: bold;">/</span>var<span style="color: #000000; font-weight: bold;">/</span>apps<span style="color: #000000; font-weight: bold;">/</span>jetrecord<span style="color: #000000; font-weight: bold;">/</span>shared<span style="color: #000000; font-weight: bold;">/</span>config<span style="color: #000000; font-weight: bold;">/</span>database.yml
&nbsp;
production:
  adapter: postgresql
  database: jetrecord_production
  user: dbuser
  password: pass
  pool: <span style="color: #000000;">5</span>
  timeout: <span style="color: #000000;">5000</span></pre></div></div>

<p>Save and exit. Don&#8217;t forget to set up the actual database on the remote server with the correct credentials!</p>
<p>With that done, all that&#8217;s left to do is make sure my domain name is set up and ready to be seen by the world. This will depend on a great number of things, such as where your site is hosted, what operating system it&#8217;s on, what DNS services you&#8217;re using, and so on.</p>
<p>I&#8217;m not a sysadmin and I&#8217;m not going to pretend to be one. The focus of this series is the application so I&#8217;ll move on assuming that we&#8217;ve got our domain set up and we&#8217;ve tested that we can reach the site with a text index page of some kind.</p>
<p>Now we&#8217;ll run the setup command, check our deployment recipe, and then do a deploy. For the deploy I&#8217;m going to run cap:deploy instead of cap:deploy:cold because I don&#8217;t have any database migrations to run yet.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> cap deploy:setup
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">'deploy:setup'</span>
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;mkdir -p /var/www/apps/jetrecord /var/www/apps/jetrecord/releases /var/www/apps/jetrecord/shared /var/www/apps/jetrecord/shared/system /var/www/apps/jetrecord/shared/log /var/www/apps/jetrecord/shared/pids &amp;#038;&amp;  chmod g+w /var/www/apps/jetrecord /var/www/apps/jetrecord/releases /var/www/apps/jetrecord/shared /var/www/apps/jetrecord/shared/system /var/www/apps/jetrecord/shared/log /var/www/apps/jetrecord/shared/pids&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
&nbsp;
$<span style="color: #000000; font-weight: bold;">&gt;</span> cap deploy:check
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">'deploy:check'</span>
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;test -d /var/www/apps/jetrecord/releases&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;test -w /var/www/apps/jetrecord&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;test -w /var/www/apps/jetrecord/releases&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;which git&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;which gem&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
You appear to have all necessary dependencies installed
&nbsp;
$<span style="color: #000000; font-weight: bold;">&gt;</span> cap deploy
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">'deploy'</span>
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">'deploy:update'</span>
 <span style="color: #000000; font-weight: bold;">**</span> transaction: start
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">'deploy:update_code'</span>
    executing locally: <span style="color: #ff0000;">&quot;git ls-remote ssh://user@REMOTE.joyent.us/home/user/etc/git/jetrecord.git HEAD&quot;</span>
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;git clone -q ssh://user@REMOTE/home/user/etc/git/jetrecord.git /var/www/apps/jetrecord/releases/20090108224035 &amp;#038;&amp; cd /var/www/apps/jetrecord/releases/20090108224035 &amp;#038;&amp; git checkout -q -b deploy dbd8430254cd5bd2860c2b73349c149b1038512f &amp;#038;&amp; (echo dbd8430254cd5bd2860c2b73349c149b1038512f &gt; /var/www/apps/jetrecord/releases/20090108224035/REVISION)&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">'deploy:finalize_update'</span>
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;chmod -R g+w /var/www/apps/jetrecord/releases/20090108224035&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;rm -rf /var/www/apps/jetrecord/releases/20090108224035/log /var/www/apps/jetrecord/releases/20090108224035/public/system /var/www/apps/jetrecord/releases/20090108224035/tmp/pids &amp;#038;&amp;\\<span style="color: #000099; font-weight: bold;">\n</span>      mkdir -p /var/www/apps/jetrecord/releases/20090108224035/public &amp;#038;&amp;\\<span style="color: #000099; font-weight: bold;">\n</span>      mkdir -p /var/www/apps/jetrecord/releases/20090108224035/tmp &amp;#038;&amp;\\<span style="color: #000099; font-weight: bold;">\n</span>      ln -s /var/www/apps/jetrecord/shared/log /var/www/apps/jetrecord/releases/20090108224035/log &amp;#038;&amp;\\<span style="color: #000099; font-weight: bold;">\n</span>      ln -s /var/www/apps/jetrecord/shared/system /var/www/apps/jetrecord/releases/20090108224035/public/system &amp;#038;&amp;\\<span style="color: #000099; font-weight: bold;">\n</span>      ln -s /var/www/apps/jetrecord/shared/pids /var/www/apps/jetrecord/releases/20090108224035/tmp/pids&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;find /var/www/apps/jetrecord/releases/20090108224035/public/images /var/www/apps/jetrecord/releases/20090108224035/public/stylesheets /var/www/apps/jetrecord/releases/20090108224035/public/javascripts -exec touch -t 200901082240.37 {} ';'; true&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
    triggering after callbacks <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #ff0000;">'deploy:update_code'</span>
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">'deploy:symlinks'</span>
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;ln -nfs /var/www/apps/jetrecord/shared/config/database.yml /var/www/apps/jetrecord/releases/20090108224035/config/database.yml&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">'deploy:symlink'</span>
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;rm -f /var/www/apps/jetrecord/current &amp;#038;&amp; ln -s /var/www/apps/jetrecord/releases/20090108224035 /var/www/apps/jetrecord/current&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
 <span style="color: #000000; font-weight: bold;">**</span> transaction: commit
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">'deploy:restart'</span>
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;touch /var/www/apps/jetrecord/current/tmp/restart.txt&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
    triggering after callbacks <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #ff0000;">'deploy'</span>
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">'deploy:cleanup'</span>
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;ls -xt /var/www/apps/jetrecord/releases&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished
 <span style="color: #000000; font-weight: bold;">**</span> keeping <span style="color: #000000;">5</span> of <span style="color: #000000;">6</span> deployed releases
  <span style="color: #000000; font-weight: bold;">*</span> executing <span style="color: #ff0000;">&quot;rm -rf /var/www/apps/jetrecord/releases/20090108222452&quot;</span>
    servers: <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #ff0000;">&quot;REMOTE&quot;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>
    <span style="color: #7a0874; font-weight: bold;">&#91;</span>REMOTE<span style="color: #7a0874; font-weight: bold;">&#93;</span> executing <span style="color: #7a0874; font-weight: bold;">command</span>
    <span style="color: #7a0874; font-weight: bold;">command</span> finished</pre></div></div>

<p>Okay, done. Now we check to see if <a href="http://landingdeparting.com/">landingdeparting.com</a> shows my Rails app.  </p>
<p><img class="photo" src="http://jetrecord.bingodisk.com/public/images/jetrecord/landingdeparting-hello.png" alt="LD says 'hi'" /></p>
<p>So that&#8217;s it. We have our Rails project started. We have our source code in a Git repository. We can deploy the app to our server with Capistrano. And we can view our handiwork live on the web.  Now it&#8217;s time to build the real application. We&#8217;ll take it bird by bird.</p>
<h2>Epilogue</h2>
<p>From now on when I type something into the terminal I&#8217;m only going to show the output of the command if I think it&#8217;s relevant for the topic of the episode. In this episode, for instance, I set up Git and Capistrano and I thought it would be good to see how these programs respond. In future episodes I&#8217;m not going to show output from Git or Capistrano unless it&#8217;s relevant.</p>
<p>I&#8217;m going to follow the same general rule for other commands. If it&#8217;s relevant to the topic at hand or if a special case needs to be addressed, I&#8217;ll show it. Otherwise, I&#8217;ll only show the commands I type in. You can also assume that I am making regular commits to git and pushing those changes live with Capistrano. I don&#8217;t intend to show you every commit message and the result of every deployment here in the text unless there&#8217;s a special case. Some of these things may show up in the videos, but they get a little tedious here.  Does that make sense? Each episode will build on previous episodes with regards to the minutiae that I share.</p>
<h2>Coming Up in the Next Episode</h2>
<p>I&#8217;m going to start building the first feature by writing some tests and then writing the code that implements the feature. Until next time, cheers and happy flying.</p>
<h2>Material You May Find Useful Related to This Episode</h2>
<p><a href="http://git-scm.com/">Git: the fast version control system</a></p>
<p><a href="http://www.capify.org/">Capistrano</a></p>
<p><a href="http://www.modrails.com/">Phusion Passenger</a></p>
<p><a href="http://www.amazon.com/gp/product/1934356158?ie=UTF8&amp;tag=loveoirs-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=1934356158"><img src="http://jetrecord.bingodisk.com/public/images/books/pragmaticversioncontrol.jpg" border="0" alt="" /></a><img style="border:none !important; margin:0px !important;" src="http://www.assoc-amazon.com/e/ir?t=loveoirs-20&amp;l=as2&amp;o=1&amp;a=1934356158" border="0" alt="" width="1" height="1" /> <a href="hhttp://www.amazon.com/Pragmatic-Version-Control-Using-Git/dp/1934356158/?tag=loveoirs-20">Pragmatic Version Control Using Git</a> by Travis Swicegood</p>
<p><a href="http://www.amazon.com/gp/product/0978739205?ie=UTF8&amp;tag=loveoirs-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0978739205"><img src="http://jetrecord.bingodisk.com/public/images/books/deployingrails.jpg" border="0" alt="" /></a><img style="border:none !important; margin:0px !important;" src="http://www.assoc-amazon.com/e/ir?t=loveoirs-20&amp;l=as2&amp;o=1&amp;a=0978739205" border="0" alt="" width="1" height="1" /> <a href="http://www.amazon.com/Deploying-Rails-Applications-Step-Step/dp/0978739205/?tag=loveoirs-20">Deploying Rails Applications</a> by Ezra Zygmuntowicz, Bruce Tate, and Clinton Begin</p>
<p><a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/"><img style="border-width:0" src="http://i.creativecommons.org/l/by-sa/3.0/us/88x31.png" alt="Creative Commons License" /></a> <span><a href="http://harrylove.org/the-building-of-jetrecord">The Building of Jetrecord</a></span> by <a rel="cc:attributionURL" href="http://harrylove.org/">Harry Love</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>. When code, text, or media in this series is not created by me and is not in the public domain I will provide links to their sources from which you can find their respective licenses and terms of use.</p>
]]></content:encoded>
			<wfw:commentRss>http://harrylove.org/2009/01/12/the-building-of-jetrecord-episode-3-git-capistrano-and-a-test-release.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
<enclosure url="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-3.mov" length="78055138" type="video/quicktime" />
<enclosure url="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-3.mp4" length="9668445" type="video/mp4" />
		</item>
		<item>
		<title>The Building of Jetrecord: Episode 2: Tell Me a Story</title>
		<link>http://harrylove.org/2008/10/29/the-building-of-jetrecord-episode-2-tell-me-a-story.html</link>
		<comments>http://harrylove.org/2008/10/29/the-building-of-jetrecord-episode-2-tell-me-a-story.html#comments</comments>
		<pubDate>Wed, 29 Oct 2008 21:08:29 +0000</pubDate>
		<dc:creator>Harry Love</dc:creator>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Portfolio]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[Startups]]></category>
		<category><![CDATA[Video]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[screencasts]]></category>
		<category><![CDATA[tutorials]]></category>
		<category><![CDATA[web applications]]></category>

		<guid isPermaLink="false">http://harrylove.org/?p=561</guid>
		<description><![CDATA[Sketches are quick, dirty, and cheap and that&#8217;s exactly how you want to start out. Draw stuff. Scrawl stuff. Boxes, circles, lines. Get your ideas out of your head and onto paper. The goal at this point should be to convert concepts into rough interface designs. This step is all about experimentation. There are no [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>Sketches are quick, dirty, and cheap and that&#8217;s exactly how you want to start out. Draw stuff. Scrawl stuff. Boxes, circles, lines. Get your ideas out of your head and onto paper. The goal at this point should be to convert concepts into rough interface designs. This step is all about experimentation. There are no wrong answers. &#8212; <a href="http://gettingreal.37signals.com/ch06_From_Idea_to_Implementation.php">From Idea to Implementation</a> &#8212; Getting Real</p></blockquote>
<p>In <a href="/2008/10/15/the-building-of-jetrecord-episode-1-the-tabula-rasa-of-doom">the last episode</a> I talked about high level goals for the project and I created a framework of rules for myself in order to help me make decisions. In this episode I&#8217;m going to follow one of the rules I created for myself:</p>
<blockquote><p>11. Each feature must begin with a story discussing the feature and its impact, followed by sketches of APIs and GUIs, followed by test coverage, followed by implementation.</p></blockquote>
<p>Cue film.</p>
<p><object width="600" height="450"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=2101167&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" /><embed src="http://vimeo.com/moogaloop.swf?clip_id=2101167&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="600" height="450"></embed></object></p>
<p>Download (right-click and save) <a href="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-2.mov">QuickTime MOV</a> <small>(39 Mb)</small> | <a href="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-2.mp4">QuickTime iPod MP4</a> <small>(8 Mb)</small> | <a href="http://jetrecord.bingodisk.com/public/videos/jetrecord/jetrecord-episode2.qt.smil">QuickTime SMIL with captions</a> <small>(39 Mb) [Note: you must choose to open the SMIL file with QuickTime]</small></p>
<h2>Am I Building the Right Thing?</h2>
<p>One of the questions we ended with last time was, &#8220;How do I know that what I&#8217;m building is what people really want?&#8221; The answer is: ask them.</p>
<p>I&#8217;m going to follow a principle from <a href="http://www.google.com/search?q=extreme+programming">Extreme Programming</a> that says you should have a customer on site with you as you build. If you can&#8217;t get a real customer, one of you has to play the role of Customer. That means I&#8217;m going to play the customer for my own application.</p>
<p>Is this legal? Yes and no. Under ideal circumstances the customer should be someone who is not a developer because features are a business decision, not a programming decision. Really, it should be the client paying for the engagement or, in the case of retail software, someone from the business side of your organization who is familiar with the domain and the needs of your customers. By playing both roles there&#8217;s a possibility that competing demands will cloud the judgment either of the Developer role or the Customer role.</p>
<p>At the same time, I&#8217;m a customer of Jetrecord. At least, I plan to be. So in a sense I&#8217;m <a href="http://gettingreal.37signals.com/ch02_Whats_Your_Problem.php">building software for myself</a>, so perhaps the two conflicts cancel each other out.</p>
<p>In any case I don&#8217;t have a choice.</p>
<h2>Why Agile (or something like it)?</h2>
<p>In my limited experience as a developer I&#8217;ve come to discover that programming is simply a conversation you have with your code. Just like writing an article, it may begin on one topic and end up on something completely different. Thinking about programming this way fits my personality and the way I learn new things. I love things that grow and take on a life of their own. I also like to look at the big picture several times during the course of a project. If I keep my head down for too long it&#8217;s easy to get lost in the details and pretty soon I&#8217;ve veered off course.</p>
<p>Certainly every circumstance is different and you have to adjust for the needs of the project. Agile, Extreme Programming, and the like don&#8217;t work for everything. When the choice is up to me, though, and the type of engagement is a good fit, I like the communication, freedom, and feeling of progress Agile affords.</p>
<p>And, of course, let&#8217;s remember that I&#8217;m not following Agile methodologies to the letter. I&#8217;m using methods that will help me get the project done reasonably and responsibly. I believe this follows the spirit of Agile, if nothing else.</p>
<p><a class="photo right" href="http://www.flickr.com/photos/library_of_congress/2179070073/"><img title="Assembling B-25 bombers at North American Aviation" src="http://farm3.static.flickr.com/2107/2179070073_98c0d9e401_m_d.jpg" alt="Assembling B-25 bombers at North American Aviation" width="240" height="197" /></a></p>
<h2>The Basic Features</h2>
<p>The customer&#8217;s job is to define the features of the application. Here they are in broad terms:</p>
<ol>
<li>Log flights</li>
<li>Visualize flights and hours</li>
<li>Export flights</li>
</ol>
<p>I&#8217;m not going to worry about advanced and/or paying features yet. We&#8217;ll save those for later. If I don&#8217;t get these basic features right, there&#8217;s no point moving forward. These are the features that I, the Customer, believe bring the most value to the application at this point in the project.</p>
<h2>Creating Stories</h2>
<p>An Agile feature story is a &#8220;promise for conversation&#8221; according to <a href="http://alistair.cockburn.us/">Alistair Cockburn</a>. That means, it&#8217;s important to get it down on paper so that we, the customer and developer, can talk about it at length later on. I&#8217;ll make sketches and write tests based on the stories I create. As for detail, I&#8217;m going to shoot for making each story completable in the span of one to two weeks, including tests.</p>
<h3>Logbook Feature Stories</h3>
<ul>
<li>Users can log flights via a simple form</li>
<li>Users can log flights via a table/spreadsheet form, similar to paper logbooks</li>
<li>Users can log flights by importing CSV files from other logbook software</li>
<li>Users can log flights via authenticated API</li>
<li>Users can see tallies of their flights and hours, separated into categories of experience and aircraft</li>
<li>Users can export flights via RSS, JSON, XML, and a number of CSV formats based on the same logbook software supported by the import feature</li>
</ul>
<h4>Discussion</h4>
<p>That&#8217;s enough to start. None of these have enough detail to tell me everything I need to know about the feature but they help me start conversations with the customer. They also tell me that there are some fundamental features that need to be developed in order support the ones listed. I would look at this list and begin discussing the following:</p>
<ul>
<li>What fields are included in a flight? Are any of them required?</li>
<li>Please attach copies of example pages from a traditional paper logbook.</li>
<li>Which third-party logbook software should be supported? Please attach examples of the CSV output created by the software.</li>
</ul>
<p>Also, we&#8217;ve talked about flights, but flights belong to users. That means we need a system in place for managing users and their needs.</p>
<ul>
<li>What is required for someone to become a registered user?</li>
<li>How are users identified? By ID, by login, by real name, by nickname?</li>
<li>Do users manage themselves for things like registration, password changes and recoveries? How much intervention is required by admins?</li>
<li>Can users delete their own accounts? And what happens to everything they&#8217;ve created on the site when they do?</li>
<li>What about multi-user accounts? How are they managed? What happens to a user if the account they were associated with closes down?</li>
</ul>
<p><a class="photo right" href="http://www.flickr.com/photos/library_of_congress/2179069727/"><img title="New B-25 bombers lined up for final inspection" src="http://farm3.static.flickr.com/2274/2179069727_c94d8c7b48_m.jpg" alt="New B-25 bombers lined up for final inspection" width="240" height="195" /></a></p>
<h3>User Feature Stories</h3>
<ul>
<li>Users can self-register and manage every aspect of their account, including paying for services and deleting the account, without intervention by an admin. Since email is the only method used to communicate with members, email is required to signup. Users must verify their email in order to begin using their accounts. Users are identified on the web site by their nickname. Users should receive email notification upon successful signup.</li>
<li>Users can login from the web site or from third party applications.</li>
<li>When users delete their accounts, all of their flights are deleted, followed by any aircraft used (assuming they&#8217;re the only pilot of the aircraft), followed by any routes used (assuming they&#8217;re the only pilot of that route), followed by their user account and any associated files. Users should receive email notification that their account has been deleted.</li>
<li>Accounts with more than one user have certain admin users who manage the account, including payment details. Admins can manage permissions for other members and invite and kick members.</li>
<li>When multi-user accounts are deleted by an admin, associated users will have the option to continue paying for features at the same level, upgrade to higher paying options, downgrade to lower paying or free options with features removed, or have their accounts deleted.</li>
</ul>
<h4>Discussion</h4>
<p>It looks like we need a fairly complicated user management system in addition to being able to log flights. Ideally, with a larger team, we could work on both at the same time. However, since it&#8217;s only me, and since I&#8217;m also the customer, I&#8217;ve decided that the user management system is the first thing I&#8217;ll do after setting up my development and production environments.</p>
<h2>Sketches</h2>
<p>Again, these are sketches. Not blueprints or wireframes. Not schematics or Photoshop comps. They have just enough detail to let me know what I&#8217;m building so I can begin creating HTML mockups, acceptance tests, and unit tests. Ideally, as the developer, I&#8217;m drawing these while seated next to the customer so we can make changes quickly and throw things away if need be.</p>
<h3>Flight Forms</h3>
<p><a href="http://www.flickr.com/photos/jetrecord/2983419518/"><img title="Flight forms sketch" src="http://farm3.static.flickr.com/2204/2983419518_d4bae2668e.jpg" alt="Flight forms sketch" width="500" height="376" /></a></p>
<h3>User Forms</h3>
<p><a href="http://www.flickr.com/photos/jetrecord/2982559049/"><img title="User forms sketch" src="http://farm4.static.flickr.com/3042/2982559049_9bc4a8c89d.jpg" alt="User forms sketch" width="500" height="423" /></a></p>
<h3>User API</h3>
<p><a href="http://www.flickr.com/photos/jetrecord/2982555443/"><img title="User API sketch" src="http://farm4.static.flickr.com/3271/2982555443_3208a74b70.jpg" alt="User API sketch" width="500" height="407" /></a></p>
<h2>Inventory</h2>
<p>At this point I should be able to take the sketches and start building the application using whatever language I choose. I had a great time using the Ruby on Rails framework to build version 1 and I&#8217;ve decided to use it again for version 2. It&#8217;s well-suited to handle an application of this type and the Ruby language is, for lack of a better word, fun.</p>
<p>These are the things I need to get going (note: gems are installed with RubyGems):</p>
<ul>
<li><a href="http://www.ruby-lang.org/">Ruby</a></li>
<li><a href="http://rubyforge.org/projects/rubygems/">RubyGems</a></li>
<li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li>
<li><a href="http://www.postgresql.org/">PostgreSQL</a> (you can also use <a href="http://www.mysql.com/">MySQL</a> if you wish)<a href="http://www.postgresql.org/"><br />
</a></li>
<li><a href="http://rubyforge.org/projects/ruby-pg">ruby-pg</a> gem (Ruby PostgreSQL interface)
<ul>
<li>Note: I had to install with &#8220;gem install pg&#8221; instead of &#8220;gem install ruby-pg&#8221; to get the most recent version</li>
<li>Find a gem for MySQL if you&#8217;re using that database</li>
</ul>
</li>
<li><a href="http://github.com/dchelimsky/rspec-rails/wikis/home">RSpec Rails</a> gem</li>
<li><a href="http://github.com/aslakhellesoy/cucumber/wikis">Cucumber</a> gem</li>
<li><a href="http://www.capify.org/">Capistrano</a> gem</li>
<li><a href="http://git.or.cz/">Git</a></li>
</ul>
<p>Optional: Ruby on Rails comes with a simple web server that we can use for development purposes. However, I&#8217;m going to set up and practice using the server that I will use in production, even though the OS will be different:</p>
<ul>
<li><a href="http://httpd.apache.org/">Apache</a></li>
<li><a href="http://www.rubyenterpriseedition.com/">Ruby Enterprise Edition</a></li>
<li><a href="http://www.modrails.com/">Phusion Passenger</a> (aka ModRails)</li>
</ul>
<p>I&#8217;m not going to cover installation. I assume you can follow the instructions given on their respective web sites and that you know how to use Google. Assuming you&#8217;re going to follow along as I write code, if you&#8217;ve never worked with Rails before I recommend installing everything and working through any of the simple tutorials you can find on the web before continuing on.</p>
<p>Also, please note, from here on out I&#8217;m not going to talk about the topic of programming per se. If you&#8217;ve never programmed before, the rest of the series might be interesting to watch but otherwise might go over your head. Let me say, though, that there are hundreds of web-based tutorials that can teach you how to program. There are even <a href="http://www.google.com/search?q=learn+to+program+with+ruby">some that use Ruby</a>.</p>
<h2>Coming Up in the Next Episode</h2>
<p>I&#8217;m going to set up my Rails project and my Git source code repository. Then I&#8217;m going to make sure I can do an initial release. Until next time, cheers and happy flying.</p>
<h2>Material You May Find Useful Related to This Episode</h2>
<p><a href="http://www.amazon.com/gp/product/0201708426?ie=UTF8&#038;tag=loveoirs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0201708426"><img border="0" style="vertical-align:top" alt="Extreme Programming Installed book cover" src="http://jetrecord.bingodisk.com/public/images/books/extremeprogramming.jpg"></a><img src="http://www.assoc-amazon.com/e/ir?t=loveoirs-20&#038;l=as2&#038;o=1&#038;a=0201708426" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> <a href="http://www.amazon.com/gp/product/0201708426?ie=UTF8&#038;tag=loveoirs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0201708426">Extreme Programming Installed</a> by Ron Jeffries</p>
<p><a href="http://www.amazon.com/gp/product/0596008031?ie=UTF8&#038;tag=loveoirs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0596008031"><img border="0" style="vertical-align:top" alt="Designing Interfaces book cover" src="http://jetrecord.bingodisk.com/public/images/books/designinginterfaces.jpg"></a><img src="http://www.assoc-amazon.com/e/ir?t=loveoirs-20&#038;l=as2&#038;o=1&#038;a=0596008031" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> <a href="http://www.amazon.com/gp/product/0596008031?ie=UTF8&#038;tag=loveoirs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0596008031">Designing Interfaces</a> by Jenifer Tidwell</p>
<p><a href="http://www.amazon.com/gp/product/1592530079?ie=UTF8&#038;tag=loveoirs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=1592530079"><img border="0" style="vertical-align:top" alt="Universal Principles of Design book cover" src="http://jetrecord.bingodisk.com/public/images/books/universalprinciples.jpg"></a><img src="http://www.assoc-amazon.com/e/ir?t=loveoirs-20&#038;l=as2&#038;o=1&#038;a=1592530079" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> <a href="http://www.amazon.com/gp/product/1592530079?ie=UTF8&#038;tag=loveoirs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=1592530079">Universal Principles of Design</a> by William Lidwell</p>
<p><a href="http://www.amazon.com/gp/product/8883705386?ie=UTF8&#038;tag=loveoirs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=8883705386"><img border="0" style="vertical-align:top" alt="Moleskine notebook" src="http://jetrecord.bingodisk.com/public/images/books/moleskinestoryboard.jpg"></a><img src="http://www.assoc-amazon.com/e/ir?t=loveoirs-20&#038;l=as2&#038;o=1&#038;a=8883705386" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> <a href="http://www.amazon.com/gp/product/8883705386?ie=UTF8&#038;tag=loveoirs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=8883705386">Moleskine Storyboard Notebook Pocket</a></p>
<p><a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/"><img style="border-width:0" src="http://i.creativecommons.org/l/by-sa/3.0/us/88x31.png" alt="Creative Commons License" /></a><br />
<span><a href="http://harrylove.org/the-building-of-jetrecord">The Building of Jetrecord</a></span> by <a rel="cc:attributionURL" href="http://harrylove.org/">Harry Love</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>. When code, text, or media in this series is not created by me and is not in the public domain I will provide links to their sources from which you can find their respective licenses and terms of use.</p>
]]></content:encoded>
			<wfw:commentRss>http://harrylove.org/2008/10/29/the-building-of-jetrecord-episode-2-tell-me-a-story.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
<enclosure url="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-2.mp4" length="7960506" type="video/mp4" />
<enclosure url="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-2.mov" length="40866915" type="video/quicktime" />
		</item>
		<item>
		<title>The Building of Jetrecord: Episode 1: The Tabula Rasa of Doom</title>
		<link>http://harrylove.org/2008/10/15/the-building-of-jetrecord-episode-1-the-tabula-rasa-of-doom.html</link>
		<comments>http://harrylove.org/2008/10/15/the-building-of-jetrecord-episode-1-the-tabula-rasa-of-doom.html#comments</comments>
		<pubDate>Wed, 15 Oct 2008 13:58:34 +0000</pubDate>
		<dc:creator>Harry Love</dc:creator>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Portfolio]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[Startups]]></category>
		<category><![CDATA[Video]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[screencasts]]></category>
		<category><![CDATA[tutorials]]></category>
		<category><![CDATA[web applications]]></category>

		<guid isPermaLink="false">http://harrylove.org/?p=516</guid>
		<description><![CDATA[We were out at our family cabin in Bolinas, and he was at the kitchen table close to tears, surrounded by binder paper and pencils and unopened books on birds, immobilized by the hugeness of the task ahead. Then my father sat down beside him, put his arm around my brother&#8217;s shoulder, and said, &#8220;Bird [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>We were out at our family cabin in Bolinas, and he was at the kitchen table close to tears, surrounded by binder paper and pencils and unopened books on birds, immobilized by the hugeness of the task ahead. Then my father sat down beside him, put his arm around my brother&#8217;s shoulder, and said, &#8220;Bird by bird, buddy. Just take it bird by bird.&#8221; &#8211;Anne Lamott</p></blockquote>
<p>I don&#8217;t think any of us ever realizes the hugeness of the task until we sit down with our materials and attempt to make sense of it all, even though the whole making-order-out-of-chaos thing is highly overrated in my opinion. Nevertheless, that&#8217;s what I&#8217;m going to do here in this series. I&#8217;m going to take a seemingly simple task&#8211;building an online logbook for pilots&#8211;expose the underbelly, cut it apart, and then put it all together into a fantastic, cohesive product that everyone will love, including me and you and every pilot you know.</p>
<p>Why? Because I believe we are created to do two things: experience pain and build things. Sometimes these are one and the same. I hate to admit right from the start that this is a task to which I feel compelled, by which I feel repulsed, and about which I feel nonplussed but apparently we&#8217;re in a new age of transparency so I&#8217;m starting with me, as Michael Jackson would say. I both love and hate computers and projects. And it always happens that about the time I&#8217;ve finished a web project I&#8217;ve forgotten all about this crummy beginning stage and am strangely excited to do it all over again.</p>
<p>Cue film.</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="600" height="450" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://vimeo.com/moogaloop.swf?clip_id=1970217&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" /><embed type="application/x-shockwave-flash" width="600" height="450" src="http://vimeo.com/moogaloop.swf?clip_id=1970217&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p>Download (right-click and save) <a href="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-1.mov">QuickTime MOV</a> <small>(49 Mb)</small> | <a href="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-1.mp4">QuickTime iPod MP4</a> <small>(7 Mb)</small> | <a href="http://jetrecord.bingodisk.com/public/videos/jetrecord/jetrecord-episode1.qt.smil">QuickTime SMIL with captions</a> <small>(49 Mb) [Note: you must choose to open the SMIL file with QuickTime]</small></p>
<h2>It Begins Like This</h2>
<ol>
<li>I&#8217;m building version 2 of an <a href="http://jetrecord.com/">online logbook for pilots called Jetrecord</a>. This is my attempt to record and share the process.</li>
<li>This is not an exercise (please place your hands inside the yellow circles); it&#8217;s the process of building a genuine application that is already live on the web. What you see here is what will be released for version 2. I&#8217;m hoping that by sharing something real you&#8217;ll be inspired to create your own projects and you&#8217;ll see what it&#8217;s like for one developer to wade through this stuff.</li>
<li>As far as sharing goes you are free to take the code, the text of this series, and the videos and reuse, repurpose, and remix them under the terms of the <a href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>. More details are at the end of this post.</li>
<li>My other hope is that we can learn from each other. I am not an expert, as will become more apparent as the series goes on. Please contribute your knowledge in the comments. Can something be improved? Do you have a suggestion or a critique? I want to hear it. Please keep it constructive, though. <a href="http://en.wikipedia.org/wiki/Shuriken">Shuriken</a> are not allowed.</li>
<li>If you&#8217;d like to contact me by email concerning the series, use harry@harrylove.org.</li>
</ol>
<h2>The Scope of the Series</h2>
<p>The process of creating a web application from start to finish involves several subjects and my plan is to explore breadth rather than depth. That is, I&#8217;m going to talk about whatever will help me complete the project and I&#8217;ll mostly concentrate on the places where subjects overlap. If I don&#8217;t explain things in enough detail, I apologize. The truth is I may not know enough about a subject to explore it in depth. I&#8217;ll try to point you to the places I&#8217;m getting my information. Don&#8217;t hesitate to speak up if you&#8217;d like me to say more about a particular topic. If I have something more to say, something worth creating a new episode for, I may just do it. Otherwise, of course, I&#8217;ll point you to my sources.</p>
<h2>Let&#8217;s Start with Questions, Goals, and Rules</h2>
<p>I&#8217;m going to start this project the same way I would approach an essay, article, or presentation, by asking questions and then stating goals and rules. You don&#8217;t have to do it this way. This is how I do it because it works for me. I&#8217;m going to say that just once. It applies to everything I&#8217;m going to do from here on out but I don&#8217;t want to bore you to death by repeating myself every episode (there&#8217;s an important programming principle in there somewhere).</p>
<p><a class="photo right" href="http://www.flickr.com/photos/library_of_congress/2179073681/"><img title="Nose wheel and landing gear assembly" src="http://farm3.static.flickr.com/2077/2179073681_8d8c0b146e_m.jpg" alt="Nose wheel and landing gear assembly" width="192" height="240" /></a></p>
<h3>Questions</h3>
<ul>
<li>What do I want to build?
<ul>
<li>I want to build a web-based logbook for pilots.</li>
</ul>
</li>
<li>Why do I want to build it?
<ul>
<li>I love aviation, data, and the connections made when data cross paths.</li>
<li>I am building this because it&#8217;s a product that I myself will use.</li>
<li>I have surveyed the available logbook software, both desktop- and web-based, and have not seen any that use the same approach to building logbooks that I&#8217;m using to build Jetrecord.</li>
<li>I believe this is a product that can work in concert with existing desktop- and web-based products.</li>
</ul>
</li>
<li>Is this an art project or do I need to consider my audience?
<ul>
<li>It&#8217;s both, but I am building this product for public consumption so I need to consider my audience.</li>
</ul>
</li>
<li>Who are the primary people groups in my audience?
<ul>
<li>Pilots</li>
<li>Air and ground crew</li>
<li>Affiliates</li>
<li>Aviation buffs</li>
<li>Their family and friends</li>
</ul>
</li>
<li>Do these people need this software? Why should they care?
<ul>
<li>Pilots must show proof to governing authorities that they have obtained any necessary knowledge and flying experience. Logbooks have been the standard method for recording flying experience for several decades.</li>
<li>The second two groups may have business concerns with the logbooks but will otherwise not need a logbook of their own.</li>
<li>The last group is important to consider because the entry of each flight will be made public, suitable for sharing with family and friends in the same way that family and friends share news, pictures, and video through blogs.</li>
</ul>
</li>
<li>Can I afford to build it?
<ul>
<li>The operating system, programming language, web framework, web server, and database server I am using are all available under open source licenses.</li>
<li>I have a <a href="http://www.apple.com/macbook/">computer</a> that is well suited for the purpose of building a web application using this software.</li>
<li>I have some <a href="http://macromates.com/">good software</a> to edit the code and <a href="http://www.adobe.com/products/creativesuite/web/">some to create and edit graphics</a> for the web site.</li>
<li>The web application is currently hosted at <a href="http://joyent.com/">Joyent</a> using the <a href="http://www.joyent.com/accelerator/pricing/">$125 per month Accelerator option</a> and there is room to grow using the current resource limits; there are both horizontal and vertical scaling options available at Joyent should the application outgrow its resources.</li>
<li>My company bank account is free but I may need to switch to a paying account if Jetrecord becomes very popular to the point of making more than a few hundred deposits per month.</li>
<li>The merchant account and credit card processing fees per month have been quoted from a reputable business at less than $50 per month.</li>
<li>The cost of doing business in Longmont, Colorado as a Sole Proprietor is a small annual fee.</li>
<li>I have access to help in preparing my taxes.</li>
</ul>
</li>
<li>Can I afford to maintain it and keep it running for years to come?
<ul>
<li>If the current ongoing fees are any indication, the answer is yes. Obviously, in the best case scenario, if Jetrecord suddenly jumps from 1,000 users to 100,000 I will need to upgrade the servers a bit. I may also need additional partners or staff to help manage the workload.</li>
</ul>
</li>
<li>Do I have enough domain knowledge to build it?
<ul>
<li>I have enough knowledge about aviation and logbooks to make a good product.</li>
<li>I have already built version 1 and it has been well received by the pilots who use it, including both general and commercial pilots.</li>
</ul>
</li>
<li>Do I have access to information about the domain?
<ul>
<li>I have access to the <a href="http://www.faa.gov/regulations_policies/faa_regulations/">FAA&#8217;s regulations</a> concerning logbooks through their web site and also via the <a href="http://www.google.com/search?q=far/aim">FAR/AIM</a> book.</li>
<li>There are many web sites, magazines, and books that focus on issues concerning pilots and aviation.</li>
</ul>
</li>
<li>Do I have access to customers and are they willing to teach me about the domain?
<ul>
<li>I have access to the current users of the product.</li>
<li>I am in contact with several pilots through <a href="http://twitter.com/jetrecord">Twitter</a> and <a href="http://mytransponder.com/">myTransponder</a>.</li>
<li>I have spoken with several pilots over the phone who have expressed interest in helping me.</li>
</ul>
</li>
</ul>
<h3>Goals</h3>
<p>Succinctly and broadly, what is the desired end state? For example, the goal of basketball is to have more points than your opponent at the end of the allotted time for the game.</p>
<ul>
<li>The logbook is the key application, regardless of any related features.</li>
<li>It must be as easy as possible to get data into and out of the system both from within the Jetrecord web site and from the developer <abbr title="Application Programming Interface">API</abbr>.</li>
<li>It must be as useful as possible to as many classifications of pilots as possible, but focus on general and commercial aviation.</li>
</ul>
<p><a class="photo right" href="http://www.flickr.com/photos/library_of_congress/2179916794/in/photostream/"><img title="P-51 Mustang" src="http://farm3.static.flickr.com/2008/2179916794_e614c29cc7_m.jpg" alt="P-51 Mustang" width="240" height="195" /></a></p>
<h3>Mantra</h3>
<p>Pilots Aren&#8217;t Robots Yet!</p>
<h3>Rules</h3>
<p>I&#8217;m writing some rules for myself that will serve as a guide for the decisions I make. They are written with this project in mind only.</p>
<ol>
<li>Rules should help us make decisions; this is the only criteria for adding a rule.</li>
<li>Modify or discard any rules that are not helpful.</li>
<li>Use the best design principles we know how to use and spend time researching concepts we are unfamiliar with.</li>
<li>Make forward progress; no product is perfect.</li>
<li>Follow the spirit of Agile methods for software development; remember rule #2 above</li>
<li>Keep documentation in the source code except where sensitive information may compromise security or business secrets.</li>
<li>Documentation is for developers and is an interface into the system; design it well.</li>
<li>Everything should be added to the source control repository unless there is a good reason not to.</li>
<li>Code that can be extracted into a shared library (<a href="http://rubyforge.org/projects/rubygems/">gem</a>) or plugin should be.</li>
<li>Favor gems, plugins, other libraries, and framework code to writing our own unless there is a good reason not to.</li>
<li>Each feature must begin with a story discussing the feature and its impact, followed by sketches of APIs and <abbr title="Graphical User Interface">GUI</abbr>s, followed by test coverage, followed by implementation.</li>
<li>Metrics should be built into the project early and often.</li>
<li>Questions drive the need for answers which drive the need to record data, not the other way around.</li>
<li>Build for data access first and GUI display second.</li>
<li>Build for universal access.</li>
<li>Build for progressive enhancement.</li>
<li>Browsers must prove their worth to be included in our support; mainstream use is not proof in and of itself.</li>
<li>See also Matthew Moore&#8217;s <a href="http://www.matthewpaulmoore.com/articles/1276-ruby-on-rails-code-quality-checklist">Ruby on Rails Code Quality Checklist</a>; remember rule #2 above</li>
</ol>
<h2>Coming Up in the Next Episode</h2>
<p>Next I&#8217;m going to write a brief outline of the basic application. I&#8217;m also going to sketch the application on paper and make a list of parts I need to get started. I hope you&#8217;ll join me. You can <a href="http://feeds.feedburner.com/harrylove">subscribe to the RSS feed</a> in order to be notified when the next episode is up. I have also created a <a href="/the-building-of-jetrecord">table of contents page</a> for this series. Until next time, cheers and happy flying.</p>
<h2>Material You May Find Useful Related to This Episode</h2>
<p><a href="http://www.amazon.com/gp/product/1591840562?ie=UTF8&amp;tag=loveoirs-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=1591840562"><img style="border:0;vertical-align:top;" src="http://jetrecord.bingodisk.com/public/images/books/artofthestart.jpg" alt="Art of the Start book cover" /></a> <a href="http://www.amazon.com/gp/product/1591840562?ie=UTF8&amp;tag=loveoirs-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=1591840562">The Art of the Start</a> by Guy Kawasaki</p>
<p><a href="http://www.amazon.com/gp/product/0385480016?ie=UTF8&amp;tag=loveoirs-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0385480016"><img style="border:0;vertical-align:top;" src="http://jetrecord.bingodisk.com/public/images/books/birdbybird.jpg" alt="Bird by Bird book cover" /></a> <a href="http://www.amazon.com/gp/product/0385480016?ie=UTF8&amp;tag=loveoirs-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0385480016">Bird by Bird</a> by Anne Lamott</p>
<p><a href="http://www.amazon.com/gp/product/0743237056?ie=UTF8&amp;tag=loveoirs-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0743237056"><img style="border:0;vertical-align:top;" src="http://jetrecord.bingodisk.com/public/images/books/spiritofstlouis.jpg" alt="The Spirit of St. Louis book cover" /></a> <a href="http://www.amazon.com/gp/product/0743237056?ie=UTF8&amp;tag=loveoirs-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0743237056">The Spirit of St. Louis</a> by Charles Lindbergh</p>
<p><a href="http://www.amazon.com/gp/product/0596102356?ie=UTF8&amp;tag=loveoirs-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596102356"><img style="border:0;vertical-align:top;" src="http://jetrecord.bingodisk.com/public/images/books/buildingscalablewebsites.jpg" alt="Building Scalable Web Sites book cover" /></a> <a href="http://www.amazon.com/gp/product/0596102356?ie=UTF8&amp;tag=loveoirs-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596102356">Building Scalable Web Sites</a> by Cal Henderson</p>
<p><a href="http://gettingreal.37signals.com/">Getting Real</a> by 37signals</p>
<p><a href="http://en.wikipedia.org/wiki/List_of_software_development_philosophies">List of software development philosophies</a> on Wikipedia</p>
<p><a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/"><img style="border-width:0" src="http://i.creativecommons.org/l/by-sa/3.0/us/88x31.png" alt="Creative Commons License" /></a><br />
<span><a href="http://harrylove.org/the-building-of-jetrecord">The Building of Jetrecord</a></span> by <a rel="cc:attributionURL" href="http://harrylove.org/">Harry Love</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>. When code, text, or media in this series is not created by me and is not in the public domain I will provide links to their sources from which you can find their respective licenses and terms of use.</p>
]]></content:encoded>
			<wfw:commentRss>http://harrylove.org/2008/10/15/the-building-of-jetrecord-episode-1-the-tabula-rasa-of-doom.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
<enclosure url="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-1.mp4" length="7250449" type="video/mp4" />
<enclosure url="http://jetrecord.bingodisk.com/public/videos/jetrecord/Building-of-Jetrecord-Episode-1.mov" length="51728629" type="video/quicktime" />
		</item>
		<item>
		<title>Google Sitemaps with Ruby on Rails, Capistrano, and Cron</title>
		<link>http://harrylove.org/2008/06/10/google-sitemaps-with-ruby-on-rails-capistrano-and-cron.html</link>
		<comments>http://harrylove.org/2008/06/10/google-sitemaps-with-ruby-on-rails-capistrano-and-cron.html#comments</comments>
		<pubDate>Tue, 10 Jun 2008 23:51:15 +0000</pubDate>
		<dc:creator>Harry Love</dc:creator>
				<category><![CDATA[Google]]></category>
		<category><![CDATA[Portfolio]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[Capistrano]]></category>
		<category><![CDATA[sitemaps]]></category>

		<guid isPermaLink="false">http://harrylove.org/?p=374</guid>
		<description><![CDATA[This is a slight modification of code originally written by Alastair Brunton. I recently implemented this for Jetrecord and since Alastair was so generous, I decided to share the love as well. I have changed Alastair&#8217;s code to generate a sitemap index file plus sitemap files for each model, all of them gzipped to save [...]]]></description>
			<content:encoded><![CDATA[<p>This is a slight modification of code <a href="http://scoop.cheerfactory.co.uk/2008/02/26/google-sitemap-generator/">originally written by Alastair Brunton</a>. I recently implemented this for <a href="http://jetrecord.com/">Jetrecord</a> and since Alastair was so generous, I decided to share the love as well. I have changed Alastair&#8217;s code to generate a sitemap index file plus sitemap files for each model, all of them gzipped to save on bandwidth.</p>
<p>I have also added Capistrano code to copy sitemap files from the previous release to the current release so we don&#8217;t lose our sitemap files when we deploy a new release.</p>
<p>Remember, Google sitemaps are for publicly available URLs. They&#8217;re for pages that you <strong>want</strong> Google to find and index. If you don&#8217;t want Google to find your CIA Operatives records, don&#8217;t tell Google about it!</p>
<p>Let&#8217;s just go straight to the code. I am going from the top down in my application&#8217;s root directory.</p>
<h2>app/models/your_model.rb</h2>
<p>You must add this code to each model that you want to generate a sitemap for. Here is an example for <a href="http://jetrecord.com/airports">Airports on Jetrecord</a>.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># put this inside app/models/airport.rb</span>
<span style="color:#9966CC; font-weight:bold;">def</span> <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">get_paths</span>
  path_ar = <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006600; font-weight:bold;">&#93;</span>
  <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">find</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:all</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>model<span style="color:#006600; font-weight:bold;">|</span>
    path_ar <span style="color:#006600; font-weight:bold;">&lt;&lt;</span> <span style="color:#006600; font-weight:bold;">&#123;</span>:url <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">&quot;/airports/#{model.to_param}&quot;</span>, <span style="color:#ff3333; font-weight:bold;">:last_mod</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> model.<span style="color:#9900CC;">updated_at</span>.<span style="color:#9900CC;">strftime</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'%Y-%m-%d'</span><span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#125;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
  path_ar
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<h2>config/sitemap/sitemap_tasks.rb</h2>
<p>This is for Capistrano. You probably don&#8217;t have a config/sitemap directory. I created one and put my Capistrano sitemap task in it. This tells Capistrano, &#8220;After deploying my new release, copy the sitemap files from the previous release and store them in the same location in the current release.&#8221;</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#6666ff; font-weight:bold;">Capistrano::Configuration</span>.<span style="color:#9900CC;">instance</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:must_exist</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#CC0066; font-weight:bold;">load</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  namespace <span style="color:#ff3333; font-weight:bold;">:sitemap</span> <span style="color:#9966CC; font-weight:bold;">do</span>
&nbsp;
    desc <span style="color:#996600;">&quot;Copy the sitemap files after deploy&quot;</span>
    task <span style="color:#ff3333; font-weight:bold;">:copy_sitemap</span>, <span style="color:#ff3333; font-weight:bold;">:roles</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:app</span> <span style="color:#9966CC; font-weight:bold;">do</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;copying Rails sitemap files&quot;</span>
      sudo <span style="color:#996600;">&quot;cp #{previous_release}/public/sitemaps/* #{current_release}/public/sitemaps/&quot;</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
    after <span style="color:#ff3333; font-weight:bold;">:deploy</span>, <span style="color:#996600;">'sitemap:copy_sitemap'</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<h2>config/deploy.rb</h2>
<p>This file usually contains your typical Capistrano recipes. All you have to do is require the sitemap_tasks file we created above.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># At the top of the file, after any other required files</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'config/sitemap/sitemap_tasks'</span></pre></div></div>

<h2>lib/google_sitemap.rb</h2>
<p>This is the meat of the whole thing. Kudos to Alastair for setting this up. The reason I modified it into using a sitemap index with sitemaps for each model is because Google allows a total of 50,000 links per sitemap. I have 48,000 navigation fixes, 20,000 airports, and 3,000 navaids in Jetrecord. By necessity I have to split my sitemap into many sitemaps.</p>
<p>I&#8217;m also gzipping the sitemap files because Google can read them and it saves bandwidth. Oh, and the URL to ping Google has changed, as has the XML namespace for their sitemap tags.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'net/http'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'uri'</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># A class specific to the application which generates a google sitemap from the contents of the database.</span>
<span style="color:#008000; font-style:italic;"># Author: Alastair Brunton</span>
<span style="color:#008000; font-style:italic;"># Modified: Harry Love 2008-06-09</span>
<span style="color:#9966CC; font-weight:bold;">class</span> GoogleSitemapGenerator
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> initialize<span style="color:#006600; font-weight:bold;">&#40;</span>base_url, sources<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#0066ff; font-weight:bold;">@base_url</span> = base_url
    <span style="color:#0066ff; font-weight:bold;">@sources</span> = sources
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># 1. Iterate through each model's #get_paths method</span>
  <span style="color:#008000; font-style:italic;"># 2. Create sitemap file for each model</span>
  <span style="color:#008000; font-style:italic;"># 3. Create sitemap index file</span>
  <span style="color:#008000; font-style:italic;"># 4. Ping Google</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> generate
    path_ar = <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006600; font-weight:bold;">&#93;</span>
    sitemaps = <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006600; font-weight:bold;">&#93;</span>
    <span style="color:#0066ff; font-weight:bold;">@sources</span>.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>source<span style="color:#006600; font-weight:bold;">|</span>
      <span style="color:#008000; font-style:italic;"># initialize the class and call the get_paths method on it.</span>
      path_ar = <span style="color:#CC0066; font-weight:bold;">eval</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;#{source}.get_paths&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      xml = generate_sitemap<span style="color:#006600; font-weight:bold;">&#40;</span>path_ar<span style="color:#006600; font-weight:bold;">&#41;</span>
      save_file<span style="color:#006600; font-weight:bold;">&#40;</span>source, xml<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
    index = generate_sitemap_index<span style="color:#006600; font-weight:bold;">&#40;</span>@sources<span style="color:#006600; font-weight:bold;">&#41;</span>
    save_file<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'index'</span>, index<span style="color:#006600; font-weight:bold;">&#41;</span>
    update_google
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Create a sitemap document for a model</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> generate_sitemap<span style="color:#006600; font-weight:bold;">&#40;</span>path_ar<span style="color:#006600; font-weight:bold;">&#41;</span>
    xml_str = <span style="color:#996600;">&quot;&quot;</span>
    xml = <span style="color:#6666ff; font-weight:bold;">Builder::XmlMarkup</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:target</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> xml_str<span style="color:#006600; font-weight:bold;">&#41;</span>
    xml.<span style="color:#9900CC;">instruct</span>!
    xml.<span style="color:#9900CC;">urlset</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:xmlns</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'http://www.sitemaps.org/schemas/sitemap/0.9'</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
      path_ar.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>path<span style="color:#006600; font-weight:bold;">|</span>
        xml.<span style="color:#9900CC;">url</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
      	  xml.<span style="color:#9900CC;">loc</span><span style="color:#006600; font-weight:bold;">&#40;</span>@base_url <span style="color:#006600; font-weight:bold;">+</span> path<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:url</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      	  xml.<span style="color:#9900CC;">lastmod</span><span style="color:#006600; font-weight:bold;">&#40;</span>path<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:last_mod</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      	  xml.<span style="color:#9900CC;">changefreq</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'weekly'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
        <span style="color:#006600; font-weight:bold;">&#125;</span>
      <span style="color:#9966CC; font-weight:bold;">end</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
    xml_str
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Create a sitemap index document</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> generate_sitemap_index<span style="color:#006600; font-weight:bold;">&#40;</span>sitemaps<span style="color:#006600; font-weight:bold;">&#41;</span>
    xml_str = <span style="color:#996600;">&quot;&quot;</span>
    xml = <span style="color:#6666ff; font-weight:bold;">Builder::XmlMarkup</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:target</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> xml_str<span style="color:#006600; font-weight:bold;">&#41;</span>
    xml.<span style="color:#9900CC;">instruct</span>!
    xml.<span style="color:#9900CC;">sitemapindex</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:xmlns</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'http://www.sitemaps.org/schemas/sitemap/0.9'</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
      sitemaps.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>site<span style="color:#006600; font-weight:bold;">|</span>
        xml.<span style="color:#9900CC;">sitemap</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
      	  xml.<span style="color:#9900CC;">loc</span><span style="color:#006600; font-weight:bold;">&#40;</span>@base_url <span style="color:#006600; font-weight:bold;">+</span> <span style="color:#996600;">&quot;/sitemaps/sitemap_#{site}.xml.gz&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      	  xml.<span style="color:#9900CC;">lastmod</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#CC00FF; font-weight:bold;">Time</span>.<span style="color:#9900CC;">now</span>.<span style="color:#9900CC;">strftime</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'%Y-%m-%d'</span><span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
   	<span style="color:#006600; font-weight:bold;">&#125;</span>
      <span style="color:#9966CC; font-weight:bold;">end</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
    xml_str
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Save the xml file (gzipped) to disk</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> save_file<span style="color:#006600; font-weight:bold;">&#40;</span>source, xml<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#CC0066; font-weight:bold;">open</span><span style="color:#006600; font-weight:bold;">&#40;</span>RAILS_ROOT <span style="color:#006600; font-weight:bold;">+</span> <span style="color:#996600;">&quot;/public/sitemaps/sitemap_#{source}.xml.gz&quot;</span>, <span style="color:#996600;">'w+'</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>f<span style="color:#006600; font-weight:bold;">|</span>
      gz = <span style="color:#6666ff; font-weight:bold;">Zlib::GzipWriter</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>f<span style="color:#006600; font-weight:bold;">&#41;</span>
      gz.<span style="color:#9900CC;">write</span> xml
      gz.<span style="color:#9900CC;">close</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Notify Google of the new sitemap index file</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> update_google
    sitemap_uri = <span style="color:#0066ff; font-weight:bold;">@base_url</span> <span style="color:#006600; font-weight:bold;">+</span> <span style="color:#996600;">'/sitemaps/sitemap_index.xml.gz'</span>
    escaped_sitemap_uri = <span style="color:#CC00FF; font-weight:bold;">URI</span>.<span style="color:#9900CC;">escape</span><span style="color:#006600; font-weight:bold;">&#40;</span>sitemap_uri<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#6666ff; font-weight:bold;">Net::HTTP</span>.<span style="color:#9900CC;">get</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'www.google.com'</span>, <span style="color:#996600;">'/webmasters/tools/ping?sitemap='</span> <span style="color:#006600; font-weight:bold;">+</span> escaped_sitemap_uri<span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<h2>lib/tasks/sitemap.rake</h2>
<p>This is the rake task that we&#8217;ll call periodically from Cron to generate new sitemap files.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'google_sitemap'</span>
namespace <span style="color:#ff3333; font-weight:bold;">:google_sitemap</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  desc <span style="color:#996600;">&quot;Generate a Google sitemap from the models&quot;</span>
  task<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:generate</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:environment</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    <span style="color:#008000; font-style:italic;"># Generate sitemaps for each of the models listed in the array</span>
    sources = <span style="color:#006600; font-weight:bold;">%</span>w<span style="color:#006600; font-weight:bold;">&#40;</span> Airport Navaid Fix AnotherModel AnotherModel AndAnotherModel EtCetera <span style="color:#006600; font-weight:bold;">&#41;</span>
    sitemap = GoogleSitemapGenerator.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'http://yourdomain.com'</span>, sources<span style="color:#006600; font-weight:bold;">&#41;</span>
    sitemap.<span style="color:#9900CC;">generate</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<h2>public/sitemaps</h2>
<p>Assuming this directory doesn&#8217;t exist already, create it.</p>
<p>Also, depending on what stack you&#8217;re using to deploy your Rails app, you may also need to tell your server to skip proxying HTTP requests to this directory. For example, I&#8217;m proxying requests to Mongrel via Apache. So, in the Apache virtual host conf file for my app, I had to add a ProxyPass directive so Apache would serve the sitemap files instead of Mongrel.</p>

<div class="wp_syntax"><div class="code"><pre class="apache" style="font-family:monospace;"><span style="color: #adadad; font-style: italic;"># Right after the ProxyPass directives for images, stylesheets, and javascripts</span>
<span style="color: #00007f;">ProxyPass</span> /sitemaps !</pre></div></div>

<p>Don&#8217;t forget to restart Apache after you save the new conf file!</p>
<h2>Add a Cron Job</h2>
<p>Lastly, you need to add a cron job to call the rake task so we can generate new sitemap files from time to time. The frequency is up to you and the requirements of your app.</p>
<p>Unfortunately, I&#8217;m not up to date on raw Cron commands. I use a GUI provided by <a href="http://joyent.com/">my web host</a>. But here&#8217;s the command I&#8217;m using on Solaris to call the rake task. You&#8217;ll have to edit this to suit the specifics of your application and server environment.</p>

<div class="wp_syntax"><div class="code"><pre class="cron" style="font-family:monospace;">cd /var/www/apps/myapp/current &amp;&amp; /opt/local/bin/rake RAILS_ENV=production google_sitemap:generate</pre></div></div>

<p>Don&#8217;t forget to tell Rake to use the production environment. Another potential gotcha: you usually have to give cron the full path to rake. You can find out where it is on your server by logging in as the user you plan to use for the cron job (usually root) and doing &#8220;which rake&#8221;. If that doesn&#8217;t bring it up it means rake isn&#8217;t in your PATH. That&#8217;s okay. You&#8217;ll just have to do a little more digging to find out where rake is installed on your system.</p>
<p>If I&#8217;ve left out anything let me know. By the way, this would make a great plugin or gem, if only I knew how to make them.</p>
]]></content:encoded>
			<wfw:commentRss>http://harrylove.org/2008/06/10/google-sitemaps-with-ruby-on-rails-capistrano-and-cron.html/feed</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Dynamic Breadcrumbs with JavaScript, Revision 4</title>
		<link>http://harrylove.org/2007/06/20/dynamic-breadcrumbs-with-javascript-revision-4.html</link>
		<comments>http://harrylove.org/2007/06/20/dynamic-breadcrumbs-with-javascript-revision-4.html#comments</comments>
		<pubDate>Wed, 20 Jun 2007 22:14:26 +0000</pubDate>
		<dc:creator>Harry Love</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Portfolio]]></category>
		<category><![CDATA[breadcrumbs]]></category>

		<guid isPermaLink="false">http://harrylove.org/2007/06/20/dynamic-breadcrumbs-with-javascript-revision-4</guid>
		<description><![CDATA[Updates from Revision 3:

Document title text can also be replaced by text in the replaceTheseCharacters array
Check for the presence of the tag to attach to before attempting to attach to it (lots of &#8216;t&#8217;s in that one)
Added a little bit more documentation to the top



// Dynamic Breadcrumbs
// By Harry Love (http://harrylove.org/)
// License: http://creativecommons.org/licenses/by-sa/3.0/
// Updated: June [...]]]></description>
			<content:encoded><![CDATA[<p>Updates from Revision 3:</p>
<ul>
<li>Document title text can also be replaced by text in the replaceTheseCharacters array</li>
<li>Check for the presence of the tag to attach to before attempting to attach to it (lots of &#8216;t&#8217;s in that one)</li>
<li>Added a little bit more documentation to the top</li>
</ul>
<p><span id="more-55"></span></p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #006600; font-style: italic;">// Dynamic Breadcrumbs</span>
<span style="color: #006600; font-style: italic;">// By Harry Love (http://harrylove.org/)</span>
<span style="color: #006600; font-style: italic;">// License: http://creativecommons.org/licenses/by-sa/3.0/</span>
<span style="color: #006600; font-style: italic;">// Updated: June 20, 2007</span>
&nbsp;
<span style="color: #006600; font-style: italic;">// Link to the script in the head of your page and change the customizations</span>
<span style="color: #006600; font-style: italic;">// below to fit your scenario. That's it. No extra HTML or inline script calls necessary.</span>
<span style="color: #006600; font-style: italic;">// The breadcrumb output is a div with id=&quot;breadcrumbs&quot; so you can style it with CSS.</span>
&nbsp;
<span style="color: #003366; font-weight: bold;">function</span> Breadcrumb<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #006600; font-style: italic;">//////////////////////</span>
	<span style="color: #006600; font-style: italic;">// Begin customization</span>
	<span style="color: #006600; font-style: italic;">//////////////////////</span>
	<span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">homeLinkText</span> <span style="color: #339933;">=</span> 	<span style="color: #3366CC;">'Home'</span><span style="color: #339933;">;</span> 	<span style="color: #006600; font-style: italic;">// Text for the home link</span>
	<span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">separator</span> <span style="color: #339933;">=</span> 		<span style="color: #3366CC;">' &gt; '</span><span style="color: #339933;">;</span>		<span style="color: #006600; font-style: italic;">// Character(s) to use between breadcrumbs</span>
	<span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">homeLinkPosition</span> <span style="color: #339933;">=</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">;</span>			<span style="color: #006600; font-style: italic;">// Where is home? 0 = domain, 1 = 1st directory, and so on ...</span>
	<span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">attachTo</span> <span style="color: #339933;">=</span> 		<span style="color: #3366CC;">'#header'</span><span style="color: #339933;">;</span>	<span style="color: #006600; font-style: italic;">// Attach breadcrumb to a tag or an ID: e.g., body, h1, div, #header, #breadcrumb</span>
	<span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">replaceTheseCharacters</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#91;</span>		<span style="color: #006600; font-style: italic;">// You can replace any text with customized text</span>
		<span style="color: #009900;">&#91;</span><span style="color: #3366CC;">&quot;this text&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;will be replaced with this text&quot;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>
		<span style="color: #006600; font-style: italic;">// [&quot;_&quot;, &quot; &quot;], // Replace underscores with spaces</span>
	<span style="color: #009900;">&#91;</span><span style="color: #3366CC;">''</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">''</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
	<span style="color: #006600; font-style: italic;">////////////////////</span>
	<span style="color: #006600; font-style: italic;">// End customization</span>
	<span style="color: #006600; font-style: italic;">////////////////////</span>
&nbsp;
	<span style="color: #003366; font-weight: bold;">var</span> homeText <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">homeLinkText</span><span style="color: #339933;">;</span>
	<span style="color: #003366; font-weight: bold;">var</span> sep <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">separator</span><span style="color: #339933;">;</span>
	<span style="color: #003366; font-weight: bold;">var</span> position <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">homeLinkPosition</span><span style="color: #339933;">;</span>
	<span style="color: #003366; font-weight: bold;">var</span> tag <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">attachTo</span><span style="color: #339933;">;</span>
	<span style="color: #003366; font-weight: bold;">var</span> replacements <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">replaceTheseCharacters</span><span style="color: #339933;">;</span>
	<span style="color: #003366; font-weight: bold;">var</span> d <span style="color: #339933;">=</span> document<span style="color: #339933;">;</span>
	<span style="color: #003366; font-weight: bold;">var</span> url <span style="color: #339933;">=</span> d.<span style="color: #660066;">location</span>.<span style="color: #660066;">href</span>.<span style="color: #660066;">split</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'//'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#91;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
	<span style="color: #003366; font-weight: bold;">var</span> text <span style="color: #339933;">=</span> url<span style="color: #339933;">;</span>
	url <span style="color: #339933;">=</span> url.<span style="color: #660066;">split</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>url<span style="color: #009900;">&#91;</span>url.<span style="color: #660066;">length</span><span style="color: #339933;">-</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">===</span> <span style="color: #3366CC;">''</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>url.<span style="color: #660066;">pop</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
	url.<span style="color: #660066;">pop</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #003366; font-weight: bold;">var</span> title <span style="color: #339933;">=</span> d.<span style="color: #660066;">title</span><span style="color: #339933;">;</span>
	<span style="color: #003366; font-weight: bold;">var</span> rex<span style="color: #339933;">;</span>
	<span style="color: #000066; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">var</span> i<span style="color: #339933;">=</span><span style="color: #CC0000;">0</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;</span> replacements.<span style="color: #660066;">length</span><span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		rex <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> RegExp<span style="color: #009900;">&#40;</span>replacements<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;g&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		text <span style="color: #339933;">=</span> text.<span style="color: #660066;">replace</span><span style="color: #009900;">&#40;</span>rex<span style="color: #339933;">,</span>replacements<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		title <span style="color: #339933;">=</span> title.<span style="color: #660066;">replace</span><span style="color: #009900;">&#40;</span>rex<span style="color: #339933;">,</span>replacements<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	text <span style="color: #339933;">=</span> text.<span style="color: #660066;">split</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #003366; font-weight: bold;">function</span> createBreadcrumbs<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #003366; font-weight: bold;">var</span> href <span style="color: #339933;">=</span> <span style="color: #3366CC;">''</span><span style="color: #339933;">,</span> a<span style="color: #339933;">,</span> div <span style="color: #339933;">=</span> d.<span style="color: #660066;">createElement</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'div'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		div.<span style="color: #660066;">setAttribute</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'id'</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">'breadcrumbs'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #000066; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">var</span> i <span style="color: #339933;">=</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;</span> url.<span style="color: #660066;">length</span><span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			href <span style="color: #339933;">+=</span> url<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">'/'</span><span style="color: #339933;">;</span>
			<span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>i <span style="color: #339933;">&lt;</span> position<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><span style="color: #000066; font-weight: bold;">continue</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
			<span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>i <span style="color: #339933;">===</span> position<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>text<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> homeText<span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
			text<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> text<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">charAt</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">toUpperCase</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> text<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			a <span style="color: #339933;">=</span> d.<span style="color: #660066;">createElement</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'a'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>		
			a.<span style="color: #660066;">setAttribute</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'href'</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">'http://'</span> <span style="color: #339933;">+</span> href<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			a.<span style="color: #660066;">appendChild</span><span style="color: #009900;">&#40;</span>d.<span style="color: #660066;">createTextNode</span><span style="color: #009900;">&#40;</span>text<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			div.<span style="color: #660066;">appendChild</span><span style="color: #009900;">&#40;</span>a<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			div.<span style="color: #660066;">appendChild</span><span style="color: #009900;">&#40;</span>d.<span style="color: #660066;">createTextNode</span><span style="color: #009900;">&#40;</span>sep<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		div.<span style="color: #660066;">appendChild</span><span style="color: #009900;">&#40;</span>d.<span style="color: #660066;">createTextNode</span><span style="color: #009900;">&#40;</span>title<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #000066; font-weight: bold;">return</span> div<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #003366; font-weight: bold;">function</span> writeHTML<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		tag <span style="color: #339933;">=</span> tag.<span style="color: #660066;">match</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'#'</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">?</span> d.<span style="color: #660066;">getElementById</span><span style="color: #009900;">&#40;</span>tag.<span style="color: #660066;">substring</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span> d.<span style="color: #660066;">getElementsByTagName</span><span style="color: #009900;">&#40;</span>tag<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
		<span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>tag<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			tag.<span style="color: #660066;">appendChild</span><span style="color: #009900;">&#40;</span>createBreadcrumbs<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
	<span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">output</span> <span style="color: #339933;">=</span> writeHTML<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #003366; font-weight: bold;">function</span> addLoadEvent<span style="color: #009900;">&#40;</span>func<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #003366; font-weight: bold;">var</span> oldonload <span style="color: #339933;">=</span> window.<span style="color: #000066;">onload</span><span style="color: #339933;">;</span>
	<span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">typeof</span> window.<span style="color: #000066;">onload</span> <span style="color: #339933;">!==</span> <span style="color: #3366CC;">'function'</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		window.<span style="color: #000066;">onload</span> <span style="color: #339933;">=</span> func<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
		window.<span style="color: #000066;">onload</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			oldonload<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			func<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
addLoadEvent<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #003366; font-weight: bold;">var</span> crumb <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> Breadcrumb<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	crumb.<span style="color: #660066;">output</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>					  
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<div class="center"><iframe src="http://rcm.amazon.com/e/cm?t=loveoirs-20&#038;o=1&#038;p=16&#038;l=st1&#038;mode=books&#038;search=javascript&#038;fc1=000000&#038;lt1=_blank&#038;lc1=3366FF&#038;bg1=FFFFFF&#038;f=ifr" marginwidth="0" marginheight="0" width="468" height="336" border="0" frameborder="0" style="border:none;" scrolling="no"></iframe></div>
]]></content:encoded>
			<wfw:commentRss>http://harrylove.org/2007/06/20/dynamic-breadcrumbs-with-javascript-revision-4.html/feed</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Activity Report Form (ARF)</title>
		<link>http://harrylove.org/2007/05/12/activity-report-form-arf.html</link>
		<comments>http://harrylove.org/2007/05/12/activity-report-form-arf.html#comments</comments>
		<pubDate>Sat, 12 May 2007 19:32:37 +0000</pubDate>
		<dc:creator>Harry Love</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[Portfolio]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[XHTML]]></category>

		<guid isPermaLink="false">http://harrylove.org/2007/05/12/activity-report-form-arf</guid>
		<description><![CDATA[
Client
University of Washington
Details
The Activity Report Form (aka The ARF) is an internal activity tracking application that I created for the librarians of the Health Sciences Library. The librarians need to see statistics, reports, and trends on the types of activities they engage in and the groups they interact with. The previous solution used a web [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://harrylove.org/wp-content/uploads/2007/05/arf.png" alt="ARF activity page" /></p>
<h2>Client</h2>
<p><a href="http://www.washington.edu/">University of Washington</a></p>
<h2>Details</h2>
<p>The Activity Report Form (aka The ARF) is an internal activity tracking application that I created for the librarians of the Health Sciences Library. The librarians need to see statistics, reports, and trends on the types of activities they engage in and the groups they interact with. The previous solution used a web based form from a third party provider that we customized. After capturing the data I was required to import the data into Excel each month and fiddle with the input and output to make it look the right way. Ugh!</p>
<p><span id="more-30"></span></p>
<p><a href="http://rubyonrails.org/">Ruby on Rails</a> to the rescue! In two months, working on my own, I had created a prototype that allowed the librarians to enter data and gather statistics and reports directly on the web without the need to use Excel. Not only that but snapshots of the data could be taken for any time period and variable thanks to the custom dynamic query form I created.</p>
<p>Later on I added some custom report views suitable for printing and an &#8220;Export to Excel&#8221; option for folks who need it. Additionally, I was able to integrate <a href="http://www.maani.us/xml_charts/">XML/SWF Charts</a> for nice line graphs. I am now porting the graphing feature to <a href="http://www.liquidx.net/plotkit/">Plotkit</a> in order to remove the Flash plugin requirement.</p>
<h2>Skills Used</h2>
<p>Ruby on Rails, XHTML, CSS, JavaScript, graphic design, user-centered design, accessibility</p>
<h2>What I Would Do Differently Today</h2>
<p>The design is very plain and could use some pep. Yes, it&#8217;s a statistics application, but a splash of color and some nice icons wouldn&#8217;t hurt.</p>
]]></content:encoded>
			<wfw:commentRss>http://harrylove.org/2007/05/12/activity-report-form-arf.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Loveoirs</title>
		<link>http://harrylove.org/2007/05/11/loveoirs.html</link>
		<comments>http://harrylove.org/2007/05/11/loveoirs.html#comments</comments>
		<pubDate>Fri, 11 May 2007 23:26:02 +0000</pubDate>
		<dc:creator>Harry Love</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Portfolio]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[XHTML]]></category>

		<guid isPermaLink="false">http://harrylove.org/2007/05/11/loveoirs</guid>
		<description><![CDATA[
Client
Me

Details
Loveoirs is our family blog, a place to keep the Love memoirs. Love-oirs. Loveoirs. See? I redesign the site fairly often. At least once a year, I think. (I use &#8220;fairly often&#8221; liberally, especially considering this site has been redesigned 5 times in the last year.) It&#8217;s a chance for me to practice graphic design [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://loveoirs.com/"><img src="/images/portfolio/loveoirs.png" title="Loveoirs article page" alt="Loveoirs article page" height="582" width="400" /></a></p>
<h2>Client</h2>
<p><a href="http://loveoirs.com/">Me<br />
</a></p>
<h2>Details</h2>
<p><a href="http://loveoirs.com/">Loveoirs</a> is our family blog, a place to keep the Love memoirs. Love-oirs. Loveoirs. See? I redesign the site fairly often. At least once a year, I think. (I use &#8220;fairly often&#8221; liberally, especially considering this site has been redesigned 5 times in the last year.) It&#8217;s a chance for me to practice graphic design in a personal way, a way that reflects our family.</p>
<p><span id="more-27"></span></p>
<p>I&#8217;m a fan of minimalism in many forms of design. In most cases I think the content of a thing should speak louder than the container its in. Yes, the container may contribute to the message, it may influence the way the message is received, it may be part of the message sometimes and in rare moments (very rare moments) the container is the message.</p>
<p>But make no mistake: the point of graphic design is to hide the design.</p>
<p>This is not to say I consider myself a good graphic designer; I consider myself a few steps below amateur. But I&#8217;ve got opinions, man, opinions.</p>
<p>In the latest design of Loveoirs I started experimenting with grids. They&#8217;re so hot right now. Grids. So Loveoirs is laid out on a very rigid grid structure that begins with the logo as a departure point. Everything from the font sizes to the vertical and horizontal white space to the width of content containers and everything in between is put in place based on the dimensions, including multiples and divisors, of the logo.</p>
<p>My favorite color combination right now is light blue on dark gray or brown, based on the 2006 Plone Conference t-shirt, so that&#8217;s where that comes from. It was a nice combo. I lifted it. I&#8217;m not sure who the designer was, but kudos.</p>
<p>Lastly, we wanted to feature our photos and our posts at the same time. We have a lot of photos on Flickr. I use a simple JavaScript call from Flickr&#8217;s API to call a random photo from our collection with each page load. Unfortunately, Flickr limits the size of the photos returned. I&#8217;d prefer to have a bigger picture that fills up the second column (to fit the grid), but there we are. Still, I like seeing the photos there.</p>
<h2>Skills Used</h2>
<p>XHTML, CSS, JavaScript, WordPress, graphic design, user-centered design</p>
<h2>What I Would Do Differently Today</h2>
<p>While I&#8217;m a fan of minimalism I&#8217;m also a fan of humorous Easter eggs, widgets, games, and stories. I&#8217;d like to see some hidden playthings on the site that are only revealed after a little exploration. Right now the site mostly reflects our serious, contemplative side but we are really funny, too, boy. Sidesplitting funny.</p>
]]></content:encoded>
			<wfw:commentRss>http://harrylove.org/2007/05/11/loveoirs.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MyHealth Toolkit</title>
		<link>http://harrylove.org/2007/05/11/myhealth-toolkit.html</link>
		<comments>http://harrylove.org/2007/05/11/myhealth-toolkit.html#comments</comments>
		<pubDate>Fri, 11 May 2007 22:03:13 +0000</pubDate>
		<dc:creator>Harry Love</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Portfolio]]></category>
		<category><![CDATA[XHTML]]></category>

		<guid isPermaLink="false">http://harrylove.org/2007/05/11/myhealth-toolkit</guid>
		<description><![CDATA[
Client
University of Washington
Details
The MyHealth Toolkit on HealthLinks was created to address the needs of a growing number of users coming to HealthLinks looking for personal health information, a topic that HealthLinks wasn&#8217;t originally designed to cover. The requirements called for a simple, clean web site with a new style that would be easy to use [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://healthlinks.washington.edu/myhealth/"><img src="/images/portfolio/myhealth.png" title="MyHealth home page" alt="MyHealth home page" height="272" width="400" /></a></p>
<h2>Client</h2>
<p><a href="http://www.washington.edu/">University of Washington</a></p>
<h2>Details</h2>
<p>The <a href="http://healthlinks.washington.edu/myhealth/">MyHealth Toolkit</a> on HealthLinks was created to address the needs of a growing number of users coming to HealthLinks looking for personal health information, a topic that HealthLinks wasn&#8217;t originally designed to cover. The requirements called for a simple, clean web site with a new style that would be easy to use on a variety of screens, including a touch-screen kiosk in the lobby of the Health Sciences Library. Like HealthLinks, the MyHealth Toolkit is a mini-portal to external content, so the point is to get users there as quickly as possible without distractions.</p>
<p><span id="more-20"></span></p>
<p>The solution involves clear language with large headings, lots of white space, and a fluid layout. The fabulous icons come from the <a href="http://tango.freedesktop.org/">Tango Desktop Project</a>. The XHTML is as minimal as can be, making it very easy to edit for the staff. The vibrant red color is used on many health information sites and is meant to exude a call to action, as in &#8220;Hey, you! Take charge of your health!&#8221; Or something like that. Also the color of blood, don&#8217;t you know. Sneaky, subliminal.</p>
<h2>Skills Used</h2>
<p>XHTML, CSS, graphic design, user-centered design, accessibility</p>
<h2>What I Would Do Differently Today</h2>
<p>I kinda like this one. It&#8217;s simple and to the point. I&#8217;m not sure what I would do differently. Do you have any suggestions?</p>
]]></content:encoded>
			<wfw:commentRss>http://harrylove.org/2007/05/11/myhealth-toolkit.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lai Real Estate</title>
		<link>http://harrylove.org/2007/05/11/lai-real-estate.html</link>
		<comments>http://harrylove.org/2007/05/11/lai-real-estate.html#comments</comments>
		<pubDate>Fri, 11 May 2007 18:30:07 +0000</pubDate>
		<dc:creator>Harry Love</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Portfolio]]></category>
		<category><![CDATA[XHTML]]></category>

		<guid isPermaLink="false">http://harrylove.org/2007/05/11/lai-real-estate</guid>
		<description><![CDATA[
Client
Sam Lai, Lai Real Estate, Ltd. 
Details
Sam is a friend of mine who needed a web site for his real estate appraisal business in Seattle. We met several times to discuss style and requirements of the site. In addition to listing his services and rates, Sam wanted an online appraisal request form that would allow [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://lairealestate.com/"><img src="/images/portfolio/lairealestate.png" title="Lai Real Estate home page" alt="Lai Real Estate home page" height="244" width="400" /></a></p>
<h2>Client</h2>
<p><a href="http://lairealestate.com/">Sam Lai, Lai Real Estate, Ltd. </a></p>
<h2>Details</h2>
<p>Sam is a friend of mine who needed a web site for his real estate appraisal business in Seattle. We met several times to discuss style and requirements of the site. In addition to listing his services and rates, Sam wanted an online appraisal request form that would allow customers to submit requests directly from his site.</p>
<p><span id="more-25"></span></p>
<p>I designed the site from the ground up to be flexible, simple, and accessible using XHTML and CSS. PHP is used to do header, sidebar, and footer includes. I also created an accessible form and hooked it up to <a href="http://www.swiftmailer.org/">Swift Mailer</a> to handle form processing. The form handles input and file uploads and then packages everything into a nice email sent directly to Sam.</p>
<p>Sam reports that his clients have been very happy with the site.</p>
<h2>Skills Used</h2>
<p>XHTML, CSS, PHP, graphic design, user-centered design, accessibility</p>
<h2>What I Would Do Differently Today</h2>
<p>When Sam and I originally discussed the project we talked about the possibility of creating a map for the Area section that displays the regions he covers. We never got around to doing it. Today I would use one of the free map APIs from either Google or Yahoo and overlay transparent polygons that show the service area.</p>
<p>I would also redo the site using WordPress to allow Sam to make edits to the site himself. All features of the current site could be supported. In addition, should Sam wish to begin posting articles related to the real estate industry, he could use the blogging feature in WordPress to do so.</p>
]]></content:encoded>
			<wfw:commentRss>http://harrylove.org/2007/05/11/lai-real-estate.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
