<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Punctuated Productivity</title>
	<atom:link href="http://punctuatedproductivity.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://punctuatedproductivity.com</link>
	<description>Experiences of a Software Engineer (cough)</description>
	<lastBuildDate>Fri, 01 Feb 2013 21:36:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='punctuatedproductivity.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Punctuated Productivity</title>
		<link>http://punctuatedproductivity.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://punctuatedproductivity.com/osd.xml" title="Punctuated Productivity" />
	<atom:link rel='hub' href='http://punctuatedproductivity.com/?pushpress=hub'/>
		<item>
		<title>Concatenate State and Case</title>
		<link>http://punctuatedproductivity.com/2012/03/25/concatenate-state-and-case/</link>
		<comments>http://punctuatedproductivity.com/2012/03/25/concatenate-state-and-case/#comments</comments>
		<pubDate>Mon, 26 Mar 2012 05:09:39 +0000</pubDate>
		<dc:creator>Kelly Felkins</dc:creator>
				<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://punctuatedproductivity.com/?p=68</guid>
		<description><![CDATA[Nested logic is difficult to get right the first time and really difficult to maintain. Here&#8217;s a simple technique that flattens nested logic into a single level case statement that is far easier to get right and far easier to maintain. The technique has 2 parts: concatenate state, and case. The goal is to transform [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=68&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p><img src="http://punctuatedproductivity.files.wordpress.com/2012/03/cable.jpeg?w=450" alt="" /></p>
<p>Nested logic is difficult to get right the first time and really difficult to maintain. Here&#8217;s a simple technique that flattens nested logic into a single level case statement that is far easier to get right and far easier to maintain. The technique has 2 parts: <strong>concatenate state, and case</strong>.</p>
<p>The goal is to transform a nested if into a flattened case statement.</p>
<p><pre class="brush: ruby;">
    if a == &quot;x&quot; then
      if b == &quot;y&quot; then
        print &quot;a=x, b=y&quot;
      else
        print &quot;a=x, b!=y&quot;
      end
    else
      if b == &quot;y&quot; then
        print &quot;a!=x, b=y&quot;
      else
        print &quot;a!=x, b!=y&quot;
      end
    end
</pre></p>
<p>becomes</p>
<p><pre class="brush: ruby;">
    case
      when a == &quot;x&quot; &amp;&amp; b == &quot;y&quot;
        print &quot;a=x, b=y&quot;
      when a == &quot;x&quot; &amp;&amp; b != &quot;y&quot;
        print &quot;a=x, b!=y&quot;
      when a != &quot;x&quot; &amp;&amp; b == &quot;y&quot;
        print &quot;a!=x, b=y&quot;
      when a != &quot;x&quot; &amp;&amp; b != &quot;y&quot;
        print &quot;a!=x, b!=y&quot;
    end
</pre></p>
<p>Why?</p>
<p>This is easier to understand. It is very easy to enumerate all permutations of the possible state components. </p>
<p>When you do this the initial implementation is easier. But the really big win is when you return to the code weeks, months, or years later. By then you&#8217;ve forgotten all of the interconnected logic that led you to the original implementation. You are forced to stare at the implementation and attempt to rebuild your mental model. With <strong>concatenate state, and case</strong> all of this is laid out in simple order.</p>
<p>How do you do it? First you bundle all of the state into a single variable. Then you drop into a case statement with all posible permutations of those states to find the code to execute.</p>
<p>Some languages let you have multiple value case expressions, with syntax something like:</p>
<p><pre class="brush: ruby;">
case
when a&gt;b &amp;&amp; c&lt;d
when a&gt;b &amp;&amp; c=&gt;d
when a&lt;=b &amp;&amp; c&lt;d
when a&lt;=b &amp;&amp; c=&gt;d
end
</pre></p>
<p>If your language supports this and your state can be expressed cleanly then you&#8217;re done &#8212; you&#8217;ve achieved the simple enumeration of all possible states.</p>
<p>Often the state cannot be expressed so succinctly, so the expressions alone bloat and obfuscate the logic.</p>
<p>To get around this simply reduce the expressions to easy to read and understand strings, then concatenate them together for a single composite state. For example:</p>
<p><pre class="brush: ruby;">
ab_state = a&gt;b ? &quot;a&gt;b&quot; : &quot;a&lt;=b&quot;
cd_state = c&lt;d ? &quot;c&lt;d&quot; : &quot;c&gt;=d&quot;

case ab_state + &quot; and &quot; + cd_state
when &quot;a&gt;b and c&lt;d&quot;
when &quot;a&gt;b and c&gt;=d&quot;
when &quot;a&lt;=b and c&lt;d&quot;
when &quot;a&lt;=b and c&gt;=d&quot;
</pre></p>
<p>In this contrived example the strings are just as verbose as their non-string expressions, so there is no win. But often the expressions are not so succinct but the strings can be as succinct as make them, while hopefully communicating the status clearly.</p>
<p>Here&#8217;s a real life example pulled from some code I wrote a few years ago. The problem is to validate passwords in a user object on save. There are 3 attributes that are of interest: a password hash, a new password, and a new password confirmation.</p>
<p>The logic goes like this: We need either an existing password hash or a new password and matching password confirmation. If we have a new password or a new password confirmation, then we are attempting to change or set the password, and both items must be present and they must match. If neither are present, but we have an existing password hash, then we are valid, meaning we are not trying to change our password. If neither the new password or new password confirmation are present, and the password hash is blank, then we are invalid since all user records must have a password.</p>
<p>A first implementation might look something like this:</p>
<p><pre class="brush: ruby;">
if password_hash.blank?
  if new_password.blank?
    errors.add(&quot;new_password&quot;, &quot;is required&quot;)
  else
    if new_password_again.blank?
      errors.add(&quot;new_password_again&quot;, &quot;new password must be entered twice&quot;)
    else
      if new_password != new_password_again
        errors.add(&quot;new_password_again&quot;, &quot;does not match your new password&quot;)
      end
    end
  end
else
  if new_password.blank?
    if new_password_again.present?
      errors.add(&quot;new_password&quot;, &quot;new password must be entered twice&quot;)
      #else
      # ok
    end
  else
    if new_password_again.blank?
      errors.add(&quot;new_password_again&quot;, &quot;new password must be entered twice&quot;)
    else
      if new_password != new_password_again
        errors.add(&quot;new_password_again&quot;, &quot;does not match your new password&quot;)
      end
    end
  end
end
</pre></p>
<p>That&#8217;s 27 lines of code (and 2 comment lines). And it is treacherous code. I think it works because I have a test suite and it passed. Without that test suite my confidence in this code would be near zero.</p>
<p>Let&#8217;s try cleaning it up a bit.</p>
<p><pre class="brush: ruby;">
if password_hash.blank? &amp;&amp; new_password.blank? &amp;&amp; new_password_again.blank?
  errors.add(&quot;new_password&quot;, &quot;is required&quot;)
end
if new_password_again.present? &amp;&amp; new_password.blank?
  errors.add(&quot;new_password&quot;, &quot;new password must be entered twice&quot;)
end
if new_password.present? &amp;&amp; new_password_again.blank?
  errors.add(&quot;new_password_again&quot;, &quot;new password must be entered twice&quot;)
end
if new_password.present? &amp;&amp; new_password_again.present?
  if new_password != new_password_again
    errors.add(&quot;new_password_again&quot;, &quot;does not match your new password&quot;)
  end
end
</pre></p>
<p>This is a little easier to read but no easier to ensure that it is correct.</p>
<p>Here is the code with <strong>concatenated state and case</strong>:</p>
<p><pre class="brush: ruby;">
newpw_key = new_password.blank? ? 'nonew' : 'new'
newpw_again_key = new_password_again.blank? ? 'noagain' : 'again'
newpw_equal_key = new_password == new_password_again ? 'eql' : 'noteql'
pw_key = password_hash.blank? ? 'nopw' : 'pw'

case &quot;#{newpw_key}-#{newpw_again_key}-#{newpw_equal_key}-#{pw_key}&quot;
  when &quot;nonew-noagain-eql-pw&quot;
  when &quot;nonew-noagain-eql-nopw&quot;
    errors.add(&quot;new_password&quot;, &quot;is required&quot;)
  # when &quot;nonew-noagain-noteql-pw&quot;
  #   not possible
  # when &quot;nonew-noagain-noteql-nopw&quot;
  #   not possible
  # when &quot;nonew-again-eql-pw&quot;
  #   not possible
  # when &quot;nonew-again-eql-nopw&quot;
  #   not possible
  when &quot;nonew-again-noteql-pw&quot;
    errors.add(&quot;new_password&quot;, &quot;new password must be entered twice&quot;)
  when &quot;nonew-again-noteql-nopw&quot;
    errors.add(&quot;new_password&quot;, &quot;new password must be entered twice&quot;)
  # when &quot;new-noagain-eql-pw&quot;
  #   not possible
  # when &quot;new-noagain-eql-nopw&quot;
  #   not possible
  when &quot;new-noagain-noteql-pw&quot;
    errors.add(&quot;new_password_again&quot;, &quot;new password must be entered twice&quot;)
  when &quot;new-noagain-noteql-nopw&quot;
    errors.add(&quot;new_password_again&quot;, &quot;new password must be entered twice&quot;)
  when &quot;new-again-eql-pw&quot;
  when &quot;new-again-eql-nopw&quot;
  when &quot;new-again-noteql-pw&quot;
    errors.add(&quot;new_password_again&quot;, &quot;does not match your new password&quot;)
  when &quot;new-again-noteql-nopw&quot;
    errors.add(&quot;new_password_again&quot;, &quot;does not match your new password&quot;)
  else
    raise &quot;unexpected case #{newpw_key}-#{newpw_equal_key}-#{pw_key}&quot;
end
</pre></p>
<p>This weighs in at 26 lines and 12 comments &#8212; still a pretty heavy method. <strong>However, this one carefully enumerates all the possible permuations of the state values</strong> &#8212; 4 state values, each with 2 possible values, for 16 possible states.</p>
<p>Of the 16 possible states, some are logically impossible. For example, you can&#8217;t say that the new password was specified and the new password confirmation was not specified but that the new password and the new password confirmation are the same &#8212; it can&#8217;t happen, so you can safely ignore that case.</p>
<p>The remaining states are either valid or invalid. When they are invalid some action is taken.</p>
<p>This code is as clear to me today as it was when I wrote it over 3 years ago.</p>
<p>I hope this helps you write cleaner and easier to maintain code. </p>
<p>-Kelly</p>
<br />Filed under: <a href='http://punctuatedproductivity.com/category/programming/'>programming</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/punctuatedproductivity.wordpress.com/68/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/punctuatedproductivity.wordpress.com/68/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=68&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://punctuatedproductivity.com/2012/03/25/concatenate-state-and-case/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/f1f775620141267099c40bbf1f950b76?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">KellyFelkins</media:title>
		</media:content>

		<media:content url="http://punctuatedproductivity.files.wordpress.com/2012/03/cable.jpeg" medium="image" />
	</item>
		<item>
		<title>Rails Migrations and AR Models &#8212; be careful my friend</title>
		<link>http://punctuatedproductivity.com/2010/10/23/rails-migrations-and-ar-models-be-careful-my-friend/</link>
		<comments>http://punctuatedproductivity.com/2010/10/23/rails-migrations-and-ar-models-be-careful-my-friend/#comments</comments>
		<pubDate>Sun, 24 Oct 2010 04:17:09 +0000</pubDate>
		<dc:creator>Kelly Felkins</dc:creator>
				<category><![CDATA[rubyonrails]]></category>

		<guid isPermaLink="false">http://punctuatedproductivity.com/?p=60</guid>
		<description><![CDATA[Be careful when using AR models in migrations. Many of us know this and some know the workaround of defining your model in the migration, as documented in the aging Rails Recipes book by Chad Fowler in the recipe &#8220;Safely Use Models in Migrations.&#8221; As the recipe specifies, you may need to refresh the model&#8217;s [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=60&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Be careful when using AR models in migrations. Many of us know this and some know the workaround of defining your model <em>in the migration</em>, as documented in the aging <a href="http://www.amazon.com/Rails-Recipes-Pragmatic-Programmers-Fowler/dp/0977616606/ref=sr_1_1?ie=UTF8&amp;qid=1287891641&amp;sr=8-1">Rails Recipes</a> book by Chad Fowler in the recipe &#8220;Safely Use Models in Migrations.&#8221;</p>
<p>As the recipe specifies, you may need to refresh the model&#8217;s column data, with the &#8220;<a href="http://api.rubyonrails.org/v2.3.8/classes/ActiveRecord/Base.html#M001824">reset_column_information</a>&#8221; class method.</p>
<p>I was caught by surprise when I did this on models that extended a base class using single table inheritance. My first try was to reset the column information of the base class.</p>
<p>Nothing complained. The data was simply not what I expected. After a while I decided to reset the column information for <em>every</em> sub class &#8212; voilà, success.</p>
<br />Filed under: <a href='http://punctuatedproductivity.com/category/rubyonrails/'>rubyonrails</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/punctuatedproductivity.wordpress.com/60/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/punctuatedproductivity.wordpress.com/60/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=60&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://punctuatedproductivity.com/2010/10/23/rails-migrations-and-ar-models-be-careful-my-friend/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/f1f775620141267099c40bbf1f950b76?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">KellyFelkins</media:title>
		</media:content>
	</item>
		<item>
		<title>Easy migration between Databases revisited</title>
		<link>http://punctuatedproductivity.com/2009/02/20/easy-migration-between-databases-revisited/</link>
		<comments>http://punctuatedproductivity.com/2009/02/20/easy-migration-between-databases-revisited/#comments</comments>
		<pubDate>Fri, 20 Feb 2009 18:41:07 +0000</pubDate>
		<dc:creator>Kelly Felkins</dc:creator>
				<category><![CDATA[rubyonrails]]></category>

		<guid isPermaLink="false">http://punctuatedproductivity.com/?p=32</guid>
		<description><![CDATA[Tobias Lütke described a technique for moving data from one database to another using rake, active record, and yaml. Unfortunately his rake task is out of date and comments are closed on his article. An updated version of the rake task is available on github. Posted in rubyonrails<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=32&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Tobias Lütke <a href="http://blog.leetsoft.com/2006/5/29/easy-migration-between-databases">described a technique</a> for moving data from one database to another using rake, active record, and yaml. Unfortunately his rake task is out of date and comments are closed on his article. An updated version of the rake task is <a href="http://github.com/kellyfelkins/backup.rake/tree/master">available on github</a>.</p>
<br />Posted in rubyonrails  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/punctuatedproductivity.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/punctuatedproductivity.wordpress.com/32/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=32&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://punctuatedproductivity.com/2009/02/20/easy-migration-between-databases-revisited/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/f1f775620141267099c40bbf1f950b76?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">KellyFelkins</media:title>
		</media:content>
	</item>
		<item>
		<title>Citizen’s Briefing Book</title>
		<link>http://punctuatedproductivity.com/2009/01/12/citizen%e2%80%99s-briefing-book/</link>
		<comments>http://punctuatedproductivity.com/2009/01/12/citizen%e2%80%99s-briefing-book/#comments</comments>
		<pubDate>Mon, 12 Jan 2009 18:36:56 +0000</pubDate>
		<dc:creator>Kelly Felkins</dc:creator>
				<category><![CDATA[civics]]></category>

		<guid isPermaLink="false">http://punctuatedproductivity.com/?p=26</guid>
		<description><![CDATA[...another way for you to talk to your government -- <a href="http://citizensbriefingbook.change.gov/">Citizen's Briefing Book</a>.<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=26&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Here is another way for you to talk to your government &#8212; <a href="http://citizensbriefingbook.change.gov/">Citizen&#8217;s Briefing Book</a>.</p>
<p>And here is the <a href="http://change.gov/newsroom/entry/your_ideas_and_submissions_in_the_citizens_briefing_book/">press release</a>.</p>
<br />Posted in civics  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/punctuatedproductivity.wordpress.com/26/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/punctuatedproductivity.wordpress.com/26/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=26&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://punctuatedproductivity.com/2009/01/12/citizen%e2%80%99s-briefing-book/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/f1f775620141267099c40bbf1f950b76?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">KellyFelkins</media:title>
		</media:content>
	</item>
		<item>
		<title>Happy Path Testing With Selenium RC Fu</title>
		<link>http://punctuatedproductivity.com/2008/02/05/happy-path-testing-with-selenium-rc-fu/</link>
		<comments>http://punctuatedproductivity.com/2008/02/05/happy-path-testing-with-selenium-rc-fu/#comments</comments>
		<pubDate>Tue, 05 Feb 2008 22:23:34 +0000</pubDate>
		<dc:creator>Kelly Felkins</dc:creator>
				<category><![CDATA[rubyonrails]]></category>
		<category><![CDATA[selenium]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://punctuatedproductivity.wordpress.com/?p=23</guid>
		<description><![CDATA[Note: a Pivotal Blabs version of this article is available here. Selenium RC Fu is a fantastic system for testing Ruby On Rails applications. It is the blending of xUnit testing with selenium. Selenium is an amazing system that operates your browser as if a human were sitting there moving the mouse, pressing buttons and [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=23&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<blockquote><p>Note: a <a href="http://www.pivotalblabs.com">Pivotal Blabs</a> version of this article is available <a href="http://pivots.pivotallabs.com/users/kelly/blog/articles/397-happy-path-testing-with-selenium-rc-fu">here</a>.</p></blockquote>
<p>Selenium RC Fu is a fantastic system for testing Ruby On Rails applications. It is the blending of xUnit testing with selenium. Selenium is an amazing system that operates your browser as if a human were sitting there moving the mouse, pressing buttons and keys.</p>
<p>Selenium RC Fu is also a remarkable example of the power of open source. It&#8217;s <a href="http://selenium.openqa.org/">Selenium</a> remotely controlled by rails and ruby. You can learn more about it by viewing the slides for <a href="http://www.slideshare.net/alexchaffee/fullstack-webapp-testing-with-selenium-and-rails">Full-stack webapp testing with Selenium and Rails</a>  presented by my colleagues Alex Chaffee and Brian Takita at the SDForum Silicon Valley Ruby Conference. </p>
<p>Now that you are excited about Selenium RC Fu, by law I must inform you that this wonderful testing tool comes with some costs. First, this is the daisy cutter of testing &#8212; problems will be detected, but it won&#8217;t be too specific about those problems. A failed selenium test will likely only tell you some expected text was not present on the page &#8212; you have to do some digging to discover the real problem. </p>
<p>It&#8217;s also slow. To be fair, a lot of software is running to do this testing.</p>
<p>So use selenium testing sparingly. A good strategy is to restrict selenium testing to &#8220;happy path&#8221; testing. These happy path tests become a compliment to other more focused and faster unit and integration tests. </p>
<p><b>Getting Started With Selenium RC Fu</b></p>
<p>Selenium RC Fu is hosted at rubyforge.org in the &#8220;pivotal.rb&#8221; project. It&#8217;s not well documented and a little hard to find. The primary documentation is the <a href="http://pivotalrb.rubyforge.org/svn/seleniumrc_fu/trunk/README">README</a> File.</p>
<p>Step 1 is to add it to your project:</p>
<pre><code>
    script/plugin install svn://rubyforge.org/var/svn/pivotalrb/seleniumrc_fu/trunk
</code></pre>
<p><b>Building Your Tests</b></p>
<p>Create your tests in <code>app/tests/selenium</code>. The basic structure mirrors the other test types:</p>
<pre><code>

    require File.dirname(__FILE__) + "/selenium_helper"
    class HappyPathsTest &lt; MyProject::SeleniumTestCase
    
      def test_nav_bar
      	  ...
      end
    
      def test_about
      	  ...
      end
    
      # more tests
    end

</code></pre>
<p>Note that <code>HappyPathsTest</code> extends something called <code>MyProject::SeleniumTestCase</code>. Selenium RC Fu provides a sample selenium helper file that suggests this convention for name spacing your tests. </p>
<p>For the happy paths tests I simply wanted to go to a page and know that the page loads. A pattern I learned from my colleague Shifra is:</p>
<ol>
<li>Pick something you know is on the target page, but not on the current page. Let&#8217;s call it &#8216;evidence&#8217;. Assert that evidence <b>is not</b> present on the current page.  </li>
<li>go to the target page </li>
<li>assert the evidence <b>is</b> present on the target page.</li>
</ol>
<p>This process insures that you are moving about as you expect. Here&#8217;s an example from my project:</p>
<pre><code>

      def test_tasks
        ...

        assert_element_not_present "xpath=//h1&amp;91;text()='The Daily Planet']"
        click_and_wait "link=The Daily Planet"
        assert_element_present "xpath=//h1[text()='The Daily Planet']"

        ...    
      end

</code></pre>
<p>I repeated this simple pattern for every path. </p>
<p><b>Selenium Functions/Assertions/Commands</b></p>
<p>Here are the functions and assertions I used in my tests:</p>
<ul>
<li><code>click_and_wait locator</code></li>
<li><code>go_back</code></li>
<li><code>wait_for_page_to_load</code></li>
<li><code>type locator, text</code></li>
<li><code>assert_text_present text</code></li>
<li><code>assert_text_not_present text</code></li>
<li><code>assert_equal value1, value2</code></li>
</ul>
<p>Many of the functions require a &#8216;locator&#8217; argument. This needs to identify a single element on the page. Often the name or id of an element is sufficient, but you may need to use an XPath. Check  <a href="http://selenium.rubyforge.org/rdoc/classes/Selenium.html">the docs</a>  for more information on element locators.</p>
<p>These functions were sufficient for me. Look for more in <code>vendor/plugins/seleniumrc_fu/lib/seleniumrc_fu/selenium_dsl.rb</code>. </p>
<p>Selenium RC Fu comes with some rake commands too (from <code>rake -T selenium</code>):</p>
<pre><code>

    rake selenium:restart_servant           # Stop and start the selenium servant (the server that launches browsers) on localhost
    rake selenium:run_server                # Run the selenium servant in the foreground
    rake selenium:server                    # Run the selenium remote-control server
    rake selenium:start_servant             # Start the selenium servant (the server that launches browsers) on localhost
    rake selenium:stop_servant              # Stop the selenium servant (the server that launches browsers) on localhost
    rake selenium:test                      # Run a selenium test file (default test/selenium/selenium_suite)
    rake selenium:test_with_server_started  # Run the selenium tests in Firefox

</code></pre>
<p><b>Running Your Tests</b></p>
<p>You can run your selenium tests individually or as part of a suite. Selenium RC Fu comes with a suite script in the  <code>vendor/plugins/seleniumnrc_fu/sample</code> directory. Drop that in your <code>test/selenium</code> directory. </p>
<p>The selenium server must be running before running individual tests. You can start the server with the rake command <code>rake selenium:server</code>. Once the server is running you run your tests from the command line or via the suite.</p>
<p>You can also run the suite with the command <code>rake selenium:test</code>. This command is smart enough to start the server if it&#8217;s not already running. However, when the server is started by this mechanism its a little more work to stop it. You can stop the server with a simple ^c if it was started via the <code>rake selenium:server</code> command.</p>
<p><b>Make It So</b></p>
<p>That&#8217;s really all you need to build your tests but here are few more things that may help.</p>
<p><b>Site Diagram</b></p>
<p>A site diagram is handy for building your happy paths tests. There will be a lot of paths through the site and marking the paths on the diagram as you visit them is an easy way to track your progress. On the diagram you need to indicate both pages and the paths to those pages.</p>
<p>The later is really important. It&#8217;s the paths that you will be testing with your happy path selenium testing. Often there are buttons, links, or other controls such as check boxes that may touch the server and reload the current page, or just update the current page via ajax. Be sure to add these to your navigation diagram.</p>
<p>Here&#8217;s the diagram I used for my site. </p>
<p><img src="http://punctuatedproductivity.files.wordpress.com/2008/02/navigation.png?w=450" alt="site diagram" /></p>
<p><b>Selenium IDE</b></p>
<p>You can code your tests by hand using Selenium RC Fu functions but it&#8217;s easier if you use <a href="http://www.openqa.org/selenium-ide/">Selenium IDE</a>. This is a Firefox plugin that records your activity. That activity is available in various selenium dialects and can be pasted into your tests. Unfortunately, it does not have output for Selenium RC Fu. Nonetheless I still found it useful. I simply clicked through my application while Selenium IDE was recording, pasted the resulting selenium commands into my tests, then converted those commands to equivalent Selenium RC Fu commands. For example:</p>
<p>Output from Selenium IDE:</p>
<pre><code>

    @selenium.click "link=My Account"
    @selenium.wait_for_page_to_load "30000"
    assert @selenium.is_text_present("Update Account")

</code></pre>
<p>Becomes:</p>
<pre><code>

    click_and_wait "link=My Account"
    assert_text_present "Update Account"

</code></pre>
<p><b>Test Data</b></p>
<p>Your selenium tests will be exercising your application just like standard unit or integration tests. These tests require data and there are a variety of techniques to make data available to your tests. Building your happy path tests is easier if you have fixture data and that fixture data is loaded in your development environment via <code>rake db:fixtures:load</code>. If you use   <a href="http://code.google.com/p/fixture-scenarios/">fixture scenarious</a>, it is handy to have a scenario for selenium testing, which you would load with something like <code>rake db:scenario:load SCENARIO=selenium</code>.</p>
<p>With your fixtures loaded in your development environment you can see exactly what is present during testing and the selenium IDE will record exactly what is played back by selenium.</p>
<p><b>XPath Checker</b></p>
<p>Usually Selenium IDE can assign a locator that simply works but there are times when it can&#8217;t or won&#8217;t.  Using <a href="http://code.google.com/p/xpathchecker/">XPath Checker</a> you can right click on an element and it will display an XPath to that element. You can also experiment with varitions and XPath Checker will list all of the elements on the page that can be identified by that XPath.</p>
<p>That&#8217;s it. Let me know how it goes.</p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/punctuatedproductivity.wordpress.com/23/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/punctuatedproductivity.wordpress.com/23/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/punctuatedproductivity.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/punctuatedproductivity.wordpress.com/23/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=23&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://punctuatedproductivity.com/2008/02/05/happy-path-testing-with-selenium-rc-fu/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/f1f775620141267099c40bbf1f950b76?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">KellyFelkins</media:title>
		</media:content>

		<media:content url="http://punctuatedproductivity.files.wordpress.com/2008/02/navigation.png" medium="image">
			<media:title type="html">site diagram</media:title>
		</media:content>
	</item>
		<item>
		<title>Teaching Your Tests To Report Unused Parameters</title>
		<link>http://punctuatedproductivity.com/2008/01/23/teaching-your-tests-to-report-unused-parameters/</link>
		<comments>http://punctuatedproductivity.com/2008/01/23/teaching-your-tests-to-report-unused-parameters/#comments</comments>
		<pubDate>Wed, 23 Jan 2008 20:57:55 +0000</pubDate>
		<dc:creator>Kelly Felkins</dc:creator>
				<category><![CDATA[rubyonrails]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://punctuatedproductivity.com/2008/01/23/teaching-your-tests-to-report-unused-parameters/</guid>
		<description><![CDATA[Note: a Pivotal Blabs version of this article is available here. Recently I was about to check in some changes and did a last minute click through of the application. All of a sudden I&#8217;m staring at a stack trace. My tests were green and I had functional tests for the failing controller/action. Tests are [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=21&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<blockquote><p>Note: a <a href="http://www.pivotalblabs.com">Pivotal Blabs</a> version of this article is available <a href="http://pivots.pivotallabs.com/users/kelly/blog/articles/384-teaching-your-tests-to-report-unused-parameters">here</a>.</p></blockquote>
<p>Recently I was about to check in some changes and did a last minute click through of the application.  All of a sudden I&#8217;m staring at a stack trace. My tests were green and I had functional tests for the failing<br />
controller/action.</p>
<p>Tests are like pants &#8212; they cover your backside while you focus on other things like adding features to your application. Suddenly I felt a breeze on my cheeks. Something was amiss.</p>
<p>I soon discovered the action and its associated tests had diverged over time. Some of the parameters were renamed in the action but not in the functional test. Since some of the work of the action was<br />
conditional on the presence of certain parameters, that work was no longer being tested.</p>
<p>This exposed weaknesses in the tests and code, such as expected side effects in the tests that are never checked. If they had been checked the tests would have failed and the parameter name mismatch<br />
would have been discovered.</p>
<p>Most functional tests provide specific parameters that should at least be examined during the processing of the action. Reporting unread parameters would strengthen those tests. It was conceivable to me that<br />
some of the other functional tests had similar unused parameters. I wanted all of my functional tests to report all unused parameters.</p>
<p>The first step was to instrument the params hash. I wanted to track access to the params hash and report parameters that were not read during the processing of the action. I don&#8217;t know what all is done to params during the life cycle of a test. I&#8217;m only interested in access<br />
from the time the action starts till it returns so I need to be able<br />
to turn the tracking on and off at specific times.</p>
<p>It turns out that Rails uses a subclass of <code>Hash</code> called <a href="http://api.rubyonrails.org/classes/HashWithIndifferentAccess.html"><code>HashWithIndifferentAccess</code></a>. I added my changes to <code>HashWithIndifferentAccess</code> in <code>test/test_helper.rb</code>:</p>
<pre><code>
    class HashWithIndifferentAccess
      def [](key)
        @accessed_keys ||= {}
        @accessed_keys[key] = true
        super
      end
    
      def start_logging
        @accessed_keys = {}
      end
    
      def end_logging
        @accessed_keys['action'] = true
        @accessed_keys['controller'] = true
        never_accessed = []
        self.each_key do |key|
          never_accessed &lt;&lt; key unless @accessed_keys.include?(key)
        end
        raise "Some keys never accessed: #{never_accessed.join(', ')}" unless never_accessed.empty?
      end
    end
</code></pre>
<p>With these changes an exception will be raised if any first level keys are not read between <code>start_logging</code> and <code>end_logging</code>. </p>
<p>In each of my functional tests I added code similar to this (from <code>account_controller_test.rb</code>):</p>
<pre><code>
    class AccountController
      around_filter :check_params
      private
      def check_params
        params.start_logging
        yield
        params.end_logging
      end
    end
</code></pre>
<p>The <a href="http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html#M000318">around filter</a> starts and ends the logging in the context of the action. </p>
<p>With these changes in place my tests no longer passed and my backside was warm and protected again. </p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/punctuatedproductivity.wordpress.com/21/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/punctuatedproductivity.wordpress.com/21/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/punctuatedproductivity.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/punctuatedproductivity.wordpress.com/21/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=21&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://punctuatedproductivity.com/2008/01/23/teaching-your-tests-to-report-unused-parameters/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/f1f775620141267099c40bbf1f950b76?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">KellyFelkins</media:title>
		</media:content>
	</item>
		<item>
		<title>ruby and gems on os-x leopard</title>
		<link>http://punctuatedproductivity.com/2007/11/01/uninstalling-ruby-installed-by-source-on-os-x/</link>
		<comments>http://punctuatedproductivity.com/2007/11/01/uninstalling-ruby-installed-by-source-on-os-x/#comments</comments>
		<pubDate>Thu, 01 Nov 2007 13:47:55 +0000</pubDate>
		<dc:creator>Kelly Felkins</dc:creator>
				<category><![CDATA[leopard]]></category>
		<category><![CDATA[os-x]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[rubyonrails]]></category>

		<guid isPermaLink="false">http://punctuatedproductivity.com/2007/11/01/uninstalling-ruby-installed-by-source-on-os-x/</guid>
		<description><![CDATA[I continue the quest to use the leopard provided ruby and gems. Unfortunately, I&#8217;ve made some mistakes. Perhaps I can save you from doing the same. Want to benefit from my experience without investing by reading the story? Skip to the recommended process at the end. The Story Part I: Anticipation I upgraded to leopard [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=9&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>I continue the quest to use the leopard provided ruby and gems. Unfortunately, I&#8217;ve made some mistakes. Perhaps I can save you from doing the same. Want to benefit from my experience without investing by reading the story? Skip to the <a href="/2007/11/01/uninstalling-ruby-installed-by-source-on-os-x#benefit">recommended process</a> at the end.
</p>
<h3>The Story</h3>
<h4>Part I: Anticipation</h4>
<p>I upgraded to leopard recently. Before doing so I read about leopard&#8217;s new and improved ruby and rails support and some of the issues around this support. Since I had previously installed ruby and rails using Dan Benjamin&#8217;s excellent <a href="http://hivelogic.com/narrative/articles/ruby-rails-mongrel-mysql-osx">&#8220;Building Ruby, Rails, Subversion, Mongrel, and MySQL on Mac OS X&#8221;</a> I anticipated that, at the least, I would need to make some adjustments when it was time to install or update gems so I wasn&#8217;t surprised when I got an error while running <a href="http://rubyforge.org/projects/geminstaller/">geminstaller</a> on one of my projects. I&#8217;m not blaming geminstaller. I suspect the problem would have occurred if I had attempted my installation with a standard &#8220;sudo gem install&#8221; command.<br />
<h4>Part II: Just Like I Had Good Sense</h4>
<p>I decided to move forward by removing my installations of ruby and rubygems then using the leopard installation for future gem installs. The first step was to remove the installations of rubygems and ruby. Fortunately, I had saved the source directories that I used to install these features originally. To remove rubygems, I did the following:
</p>
<blockquote>
<pre>$ cd /usr/local/src/rubygems-0.9.2 
$ sudo rm `cat InstalledFiles`
</pre>
</blockquote>
<p>This will remove the gem excutable, but not the gems themselves. I expected to remove the gem library when I removed my installation of ruby.To remove ruby I thought I could do something similar:</p>
<blockquote>
<pre>$ cd /usr/local/src/ruby-1.8.6 
$ cat .installed.list | xargs -L 1 sudo rm -dfv
</pre>
</blockquote>
<p>I ran that last command over and over again until it no longer deleted anything. I could have changed the rm command to &#8216;rm -rf&#8217;  but I wanted to see what got deleted. At some point I realized I was only deleting &#8216;ri&#8217; stuff so it was time to do some more research.</p>
<h4>Part III: The Awakening</h4>
<p>Apparently you can specify an option when installing ruby from source to keep a list of all files installed. I didn&#8217;t know about this and didn&#8217;t do it. It would have been helpful.Instead I attempted to decipher the  makefile to determine what gets done when you say &#8216;make install&#8217;. It turns out most of the work is done in a script called instruby.rb. I sifted through that script and attempted to devine where and what it moves into place when performing an install. I listed every directory and file, then removed the duplicates and came up with this set of commands for removing ruby:<a title="removing-ruby" name="removing-ruby"></a></p>
<blockquote>
<pre>
sudo rm -rf /usr/local/lib/ruby 
sudo rm /usr/local/bin/ruby 
sudo rm /usr/local/bin/erb 
sudo rm /usr/local/bin/irb 
sudo rm /usr/local/bin/rdoc 
sudo rm /usr/local/bin/ri 
sudo rm /usr/local/bin/testrb 
sudo rm /usr/local/share/man/man1/ruby.1
</pre>
</blockquote>
<p>I expected that typing <code>$ ruby -v</code> would show the leopard version of ruby. Surprisingly, I saw something like &#8220;/usr/local/bin/ruby not found&#8221; (I don&#8217;t remember the exact message). Of course the issue was that the previous ruby had been hashed by the shell. Fixing it was simple: <code>$ hash -r</code>. Now I see:</p>
<blockquote><p>
<code>$ ruby -vruby 1.8.6 (2007-06-07 patchlevel 36) [universal-darwin9.0]<br />
</code>
</p></blockquote>
<p>Then I tried a few commands installed as gems. Here again there were errors. This time the solution was to remove the associated bin file from /usr/local/bin. At this point I realized that all gems with executables that I had previously installed had entries in /usr/local/bin that would likely generate errors.</p>
<h4>Part IV: Drastic Measures</h4>
<p>It was at this point I elected to remove the entire /usr/local and /opt/local directories. Keep in mind that I have a relatively new macbook and I can still fairly easily reinstall anything that I had previously removed from there.Now my gem installs are working correctly when I apply some of the new darwin solutions outlined below.<a title="benefit" name="benefit"></a></p>
<h3><a title="benefit" name="benefit"></a>What would I do now, with the benefit of hindsight?</h3>
<ol>
<li>First, why change your existing ruby and gems installation? If it works, you&#8217;re done. Wait till the kinks are worked out before investing your time.</li>
<li>If you really want to use the leopard versions of ruby and rails, <strong>don&#8217;t install leopard</strong>&#8230;  Not Yet!</li>
<li><strong>Remove all of your existing gems</strong>, using &#8220;sudo gem uninstall [gem name here]. You don&#8217;t need them in their current location. Remove them first while you can use the gem utility. This will remove them both from the gem install directory and also from the /usr/local/bin directories (for gems that have bins).</li>
<li><strong>Uninstall ruby</strong> using the commands <a href="#removing-ruby">listed above</a>.</li>
<li>Now <strong>install leopard</strong>.</li>
<li><strong>Update xcode </strong>from the leopard install disk.</li>
<li><strong>Do not</strong> <code>gem update --system</code> (see <a href="http://discussions.apple.com/thread.jspa?threadID=1202925&amp;tstart=0">this thread</a> on apple&#8217;a ruby support discussion)</li>
</ol>
<p>At this point you should be able to use rubygems to install and update gems in the <em>new normal way.</em>  What&#8217;s that mean? I&#8217;ve encountered these issues:
<ol>
<li>The <a href="http://trac.macosforge.org/projects/ruby/wiki/Troubleshooting">well documented architecture issue</a> for some gems. Hint: try something like &#8220;<code>sudo env ARCHFLAGS="-arch i386" gem install [gem name here]</code>&#8220;.</li>
<li>The &#8216;gems&#8217; command apparently attempts to update gems even when they don&#8217;t need updating. Why update a gem that doesn&#8217;t need updating? I don&#8217;t know, but if you try it will often fail with</li>
<blockquote><p> <code>Directory /Library/Ruby/Gems/1.8/doc/[some gem]/ri already exists, but it looks like it isn't an RDoc directory. Because RDoc doesn't want to risk destroying any of your existing files, you'll need to specify a different output directory name (using the --op option).</code></p></blockquote>
<p>Try adding the &#8220;&#8211;no-rdoc&#8221; option.</ol>
<p>If you see &#8220;missing ruby header files&#8221; when installing a gem then you probably missed the &#8220;install xcode&#8221; recommendation above.</p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/punctuatedproductivity.wordpress.com/9/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/punctuatedproductivity.wordpress.com/9/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/punctuatedproductivity.wordpress.com/9/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/punctuatedproductivity.wordpress.com/9/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=9&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://punctuatedproductivity.com/2007/11/01/uninstalling-ruby-installed-by-source-on-os-x/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/f1f775620141267099c40bbf1f950b76?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">KellyFelkins</media:title>
		</media:content>
	</item>
		<item>
		<title>bread and butter capistrano</title>
		<link>http://punctuatedproductivity.com/2007/02/20/bread-and-butter-capistrano/</link>
		<comments>http://punctuatedproductivity.com/2007/02/20/bread-and-butter-capistrano/#comments</comments>
		<pubDate>Tue, 20 Feb 2007 05:32:16 +0000</pubDate>
		<dc:creator>Kelly Felkins</dc:creator>
				<category><![CDATA[capistrano]]></category>
		<category><![CDATA[rubyonrails]]></category>

		<guid isPermaLink="false">http://punctuatedproductivity.wordpress.com/2007/07/31/bread-and-butter-capistrano/</guid>
		<description><![CDATA[The East Bay Ruby Meetup for February was all about Capistrano. Capistrano’s creator, Jamis Buck, says capistrano &#8230;is a utility for executing commands in parallel on multiple machines, such as for automating the deployment of applications Three of us talked about capistrano, deployment and scaling. My talk was titled “Bread and Butter Capistrano” because I [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=3&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p><a href="http://punctuatedproductivity.files.wordpress.com/2007/07/29.png" title="29.png"><img src="http://punctuatedproductivity.files.wordpress.com/2007/07/29.png?w=450" alt="29.png" align="right" /></a>The <a href="http://ruby.meetup.com/81/calendar/5400280/">East Bay Ruby Meetup for February</a> was all about <a href="http://manuals.rubyonrails.com/read/book/17">Capistrano</a>.</p>
<p>Capistrano’s creator, <a href="http://weblog.jamisbuck.org/">Jamis Buck</a>, says capistrano</p>
<blockquote><p>&#8230;is a utility for executing commands in parallel on multiple machines, such as for automating the deployment of applications</p></blockquote>
<p>Three of us talked about capistrano, deployment and scaling. My talk was titled “Bread and Butter Capistrano” because I wanted to emphasize that I use it as a tool to get my work done, find it very helpful, and use it all the time, but I haven’t spent a lot of time studying the tool itself.</p>
<p>Here’s the talk. Enjoy!</p>
<p><a href="http://punctuatedproductivity.files.wordpress.com/2007/07/capistrano.pdf" title="Bread and Butter Capistrano">Bread and Butter Capistrano</a></p>
<p>If you’ve read this far, please consider rating me at <a href="http://www.workingwithrails.com/recommendation/new/person/6874-kelly-felkins">Working With Rails</a>.</p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/punctuatedproductivity.wordpress.com/3/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/punctuatedproductivity.wordpress.com/3/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/punctuatedproductivity.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/punctuatedproductivity.wordpress.com/3/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=3&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://punctuatedproductivity.com/2007/02/20/bread-and-butter-capistrano/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/f1f775620141267099c40bbf1f950b76?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">KellyFelkins</media:title>
		</media:content>

		<media:content url="http://punctuatedproductivity.files.wordpress.com/2007/07/29.png" medium="image">
			<media:title type="html">29.png</media:title>
		</media:content>
	</item>
		<item>
		<title>denim &#8211; simply sketch your user interface</title>
		<link>http://punctuatedproductivity.com/2006/12/15/denim-simply-sketch-your-user-interface/</link>
		<comments>http://punctuatedproductivity.com/2006/12/15/denim-simply-sketch-your-user-interface/#comments</comments>
		<pubDate>Fri, 15 Dec 2006 07:12:34 +0000</pubDate>
		<dc:creator>Kelly Felkins</dc:creator>
				<category><![CDATA[ui]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[denim]]></category>

		<guid isPermaLink="false">http://punctuatedproductivity.com/2006/12/15/denim-simply-sketch-your-user-interface/</guid>
		<description><![CDATA[DENIM is an application that allows you to sketch a user interface using a tablet. <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=12&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve been using a tool for user interface prototyping and testing called &#8220;<a href="http://dub.washington.edu/denim/">DENIM</a>&#8220;. DENIM is an application that allows you to sketch a user interface using a tablet connected to your pc or mac. It allows you to zoom in and out to create varying levels of detail as needed.<img src="http://punctuatedproductivity.files.wordpress.com/2007/11/281.png?w=450" alt="Example UI sketch of DENIM" style="float:right;" />
</p>
<p>For example, often you will first sketch the general flow of the site. This will look like boxes with a textual name or title. You can then draw lines between these boxes indicating a flow from one box to the other.   With DENIM, you can then select a box and zoom in. When you do this the box becomes a page. You can then add details to the page, such as (sketched) forms, buttons and links.  Now you can draw a line from one of these sketched buttons to another page <span style="font-style:italic;" class="Apple-style-span">and the sketched button becomes a live link</span>. What does this mean? Well, DENIM allows you to save your prototype UI as html pages with clickable images&#8211;your sketches embedded in html. When you click on one of the hot areas, which are blue (non hot areas are black ink on white, hot areas are blue ink on white), you jump to the target page.
</p>
<p>Suddenly you have a live prototype. I&#8217;ve shown these live prototypes to customers and colleagues. At first people seem perplexed. They seem to think I&#8217;ve scribbled on a tablet, taken screen a shot and this is how I&#8217;m going to show them the proposed UI. Then they click on a link or two and suddenly they get it&#8230; and off they go exploring the whole site, spewing out suggested changes. After a few minutes you go back to your office and spend a few hours coming up with a new version. A couple iterations of this and you and the customer have a pretty good understanding of what is to be built.</p>
<h4>But It&#8217;s Not All Rosy</h4>
<p>
This program feels abandoned. It has some bugs. The web site has a video of a future version, but the video is a few years old and the features in the video have not been released.  I&#8217;ve learned to save my work often, and to save it as different versions. Occasionally your DENIM model will become corrupted and sometimes you cannot fix it, so you have to revert to an earlier state. DENIM creates snapshots for you but I lost trust in them early on and haven&#8217;t tried again.  DENIM also seems to have a size or complexity limit. When the complexity of your UI exceeds some level the model can no longer be opened. I&#8217;ve also noticed that some machines can open some models while others cannot &#8212; perhaps it is a memory issue.Even with the bugs DENIM is a worthwhile tool to have in your bag. Please check it out.</p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/punctuatedproductivity.wordpress.com/12/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/punctuatedproductivity.wordpress.com/12/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/punctuatedproductivity.wordpress.com/12/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/punctuatedproductivity.wordpress.com/12/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=12&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://punctuatedproductivity.com/2006/12/15/denim-simply-sketch-your-user-interface/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/f1f775620141267099c40bbf1f950b76?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">KellyFelkins</media:title>
		</media:content>

		<media:content url="http://punctuatedproductivity.files.wordpress.com/2007/11/281.png" medium="image">
			<media:title type="html">Example UI sketch of DENIM</media:title>
		</media:content>
	</item>
		<item>
		<title>cygwin, terms, and emacs</title>
		<link>http://punctuatedproductivity.com/2004/03/17/11/</link>
		<comments>http://punctuatedproductivity.com/2004/03/17/11/#comments</comments>
		<pubDate>Wed, 17 Mar 2004 14:09:47 +0000</pubDate>
		<dc:creator>Kelly Felkins</dc:creator>
				<category><![CDATA[cygwin]]></category>
		<category><![CDATA[emacs]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[unix]]></category>

		<guid isPermaLink="false">http://punctuatedproductivity.com/2007/11/01/11/</guid>
		<description><![CDATA[Getting a cygwin terminal emulation to work properly with a remote unix or linux host has been challenging. The problems are primarily when interacting with emacs, but there are various other issues, such as unix curses applications (getting box drawing characters to display correctly). I&#8217;ve recently switched back to the cygwin xterm as my primary [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=11&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Getting a cygwin terminal emulation to work properly with a remote unix or linux host has been challenging. The problems are primarily when interacting with emacs, but there are various other issues, such as unix curses applications (getting box drawing characters to display correctly).</p>
<p>I&#8217;ve recently switched back to the cygwin xterm as my primary terminal emulator. Previously I used rxvt. I switched to xterm for a variety of reasons (read &#8220;not well defined&#8221;), some of which are:<br />
<a href="http://www-2.cs.cmu.edu/~maverick/GNUEmacsColorThemeTest/index-perl.html"><img src="http://punctuatedproductivity.files.wordpress.com/2007/11/6.png?w=450" style="float:right;" alt="6.png" /></a></p>
<ul>
<li> I was dissatisfied with the colors in emacs. I started experimenting with <a href="http://www.emacswiki.org/cgi-bin/wiki.pl?ColorTheme">colortheme.el</a>, and noticed that the color themes as displayed on the <a href="http://www.cs.cmu.edu/~maverick/GNUEmacsColorThemeTest/index-java.html">color-theme web page</a> were different from what I saw in my rxvt window. I don&#8217;t understand why.</li>
<li>font issues have been frustrating. I don&#8217;t know if I&#8217;ll run into these problems with xterm or not. Specifically, I had to jump through hoops (read &#8220;use an rsvt specific term setting&#8221;) to get line drawing fonts working. That broke other things.</li>
</ul>
<h3>challanges</h3>
<p>challenge: <strong>get delete working correctly in emacs on solaris via xterm</strong></p>
<ul>
<li>discussion: delete should delete the character under the cursor. Delete is the same as backspace on solaris.</li>
<li>status: <strong>solved!</strong> I added this to my .emacs:</li>
</ul>
<blockquote><p><code>(define-key function-key-map [delete] [deletechar])</code></p></blockquote>
<p>This will likely break something else, especially since I share this  .emacs with linux systems. Stay tuned.</p>
<p>challenge: <strong>get backspace working correctly in emacs in xterm</strong></p>
<ul>
<li>discussion: backspace in emacs should delete the previous character. ^h brings up the emacs help system, &#8220;info&#8221;. Backspace keys often generate a ^h character.</li>
<li>status: <strong>solved!</strong> I created an .Xdefaults with this line &#8220;<code>xterm.*.backarrowKey: false</code>&#8220;. Check out the discussion of this in<br />
<a href="http://www.squish.net/docs/delbs.html">Squishywishywoo: delete and backspace keys under X (emacs, xterm)</a> under &#8220;xterm&#8221;.</li>
</ul>
<p>challenge: <strong>get shift-arrow keys to work in rxvt</strong></p>
<ul>
<li>discussion: I&#8217;m checking out <a href="http://hovav.net/elisp/windmove-0.93.el">windmove.el</a> and its default key bindings use shift-some arrow key. The keys codes that emacs received were unrecognized.</li>
<li>status: <strong>solved!</strong> I installed <a href="http://savannah.gnu.org/cgi-bin/viewcvs/emacs/emacs/lisp/term/rxvt.el">rxvt.el</a>. I grabbed the 1.4 version and put it in the <code>~/emacs-lisp/term</code> directory. Then I changed my <code>$TERM</code> type to &#8220;rxvt&#8221;.</li>
</ul>
<p>challenge: <strong>get cycle-buffer f10 key working</strong></p>
<ul>
<li>discussion: The above fix broke one of the keys that works cycle-buffer, f10.</li>
<li>status: <strong>solved!</strong> I looked at the rxvt.el file but didn&#8217;t see a problem, not that I would recognize a problem if it was in front of my face. I then compared it to the xterm.el file. I decided I would rather go with the smaller and less complicated xterm.el file. So I made a copy of this from /usr/share/emacs/21.2/lisp/term/ and placed it in my ~/emacs-lisp/term directory. I then added key definitions, using the definitions I found in rxvt. With this I now have my long standing key definition problems fixed. Here are the lines I added to my cygwin ~/emacs-lisp/term/xterm.el file:</li>
</ul>
<blockquote><p><code><br />
(define-key function-key-map "\\e[33~" [S-f9])<br />
(define-key function-key-map "\\e[34~" [S-f10])<br />
(define-key function-key-map "\\e[d" [S-left])<br />
(define-key function-key-map "\\e[c" [S-right])<br />
(define-key function-key-map "\\e[a" [S-up])<br />
(define-key function-key-map "\\e[b" [S-down])<br />
(define-key function-key-map "\\e[7~" [home])<br />
(define-key function-key-map "\\e[7^" [C-home])<br />
(define-key function-key-map "\\e[8~" [end])<br />
(define-key function-key-map "\\e[8^" [C-end])<br />
</code></p></blockquote>
<p>In the process I cleaned up (read &#8220;deleted&#8221;) my .Xdefaults and .Xresources files &#8211; they&#8217;re gone. Oh, and windmove.el ROCKS!</p>
<p>Here are some techniques to help you diagnose terminal emulation problems:</p>
<table>
<tr>
<th>for this</th>
<th>do this</th>
</tr>
<tr>
<td>display the code being sent when you press a key</td>
<td><code>ctrl-v [some key]</code></td>
</tr>
<tr>
<td>display the code being sent in emacs (describe-key and describe-key-briefly)</td>
<td><code>C-h k and C-h c</code></td>
</tr>
</table>
<h3>resources:</h3>
<ul>
<li><a href="http://www.tldp.org/HOWTO/Keyboard-and-Console-HOWTO.html">The Linux keyboard and console HOWTO</a></li>
<li> <a href="http://www.ibb.net/~anne/keyboard.html">BackSpace and Delete Configuration for Linux &#8211; VT, xterm,<br />
bash, tcsh, netscape and more</a></li>
<li><a href="http://www.squish.net/docs/delbs.html">Squishywishywoo: delete and backspace keys under X &#8211; emacs, xterm</a></li>
<li><a href="http://www.tldp.org/HOWTO/BackspaceDelete/index.html">Linux Backspace/Delete mini-HOWTO</a></li>
<li> <a href="http://www.cs.utk.edu/~shuford/terminal/index.html">The Archive of VIDEO TERMINAL INFORMATION</a></li>
<li> <a href="http://groups.google.com/groups?q=xterm+shift-left+shift-right+emacs&amp;hl=en&amp;lr=&amp;ie=UTF-8&amp;oe=UTF-8&amp;selm=30r9e1%248bj%40caesar.wits.ac.za&amp;rnum=2">useful newgroup note #1</a></li>
</ul>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/punctuatedproductivity.wordpress.com/11/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/punctuatedproductivity.wordpress.com/11/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/punctuatedproductivity.wordpress.com/11/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/punctuatedproductivity.wordpress.com/11/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=punctuatedproductivity.com&#038;blog=1443617&#038;post=11&#038;subd=punctuatedproductivity&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://punctuatedproductivity.com/2004/03/17/11/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/f1f775620141267099c40bbf1f950b76?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">KellyFelkins</media:title>
		</media:content>

		<media:content url="http://punctuatedproductivity.files.wordpress.com/2007/11/6.png" medium="image">
			<media:title type="html">6.png</media:title>
		</media:content>
	</item>
	</channel>
</rss>
