<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-24133085</id><updated>2011-12-25T08:47:34.011-08:00</updated><title type='text'>Sean Carley</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Sean Carley</name><uri>http://www.blogger.com/profile/05137044194057455976</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>22</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-24133085.post-3157210424332691215</id><published>2007-02-02T07:44:00.000-08:00</published><updated>2007-02-02T08:39:53.339-08:00</updated><title type='text'>How has Ruby blown my mind?</title><content type='html'>In a word iterators.  No single language feature has changed the way I think about code more than the simple, now seemingly obvious, pervasive,  compelling act of passing anonymous code into a method to be run in context by that object.  In a few days, this feature became so natural to me that languages where it was missing - Java, C, Perl - were &lt;strong&gt;harder&lt;/strong&gt; just because I knew it was missing.&lt;br /&gt;&lt;br /&gt;By the way, if you don't know about &lt;a href="http://on-ruby.blogspot.com/"&gt;Pat Eyler's&lt;/a&gt; &amp;amp; &lt;a href="http://www.apress.com/"&gt;Apress'&lt;/a&gt; ongoing blog about Ruby contests, check out this month's &lt;a href="http://on-ruby.blogspot.com/2007/01/blogging-contest-february-challenge.html"&gt;contest&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-3157210424332691215?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/3157210424332691215/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=3157210424332691215' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/3157210424332691215'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/3157210424332691215'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2007/02/how-has-ruby-blown-my-mind.html' title='How has Ruby blown my mind?'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-115766662323522747</id><published>2006-09-07T14:58:00.000-07:00</published><updated>2006-09-07T15:03:43.243-07:00</updated><title type='text'>Install MIT Scheme</title><content type='html'>&lt;div&gt;&lt;div class="goalentry"&gt;&lt;p&gt;Pat has convinced me to set some minor goals on the way to completing Structure and Interpretation of Computer Programs together.  The first goal is to get the development environment installed.  We have agreed to complete it by Monday, September 11th.&lt;/p&gt;&lt;/div&gt;&lt;div class="goalprogresslink"&gt;See more progress on: &lt;a href="http://www.43things.com/people/progress/milythael?on=4504359"&gt;Read SICP&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-115766662323522747?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/115766662323522747/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=115766662323522747' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/115766662323522747'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/115766662323522747'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/09/install-mit-scheme.html' title='Install MIT Scheme'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-115717306455557653</id><published>2006-09-01T20:47:00.000-07:00</published><updated>2006-09-01T22:22:43.216-07:00</updated><title type='text'>ActiveRecord, Delegation and Demeter</title><content type='html'>Many of you have heard of the Law of Demeter (LoD).  For those who haven't, I recommend reading Robert C. Martin's great article (in his &lt;a href='http://www.objectmentor.com/resources/listArticles?key=author&amp;author=Robert%20C.%20Martin'&gt;Engineering Notebooks&lt;/a&gt;), the &lt;a href='http://www.objectmentor.com/resources/articles/dip.pdf'&gt;Dependency Inversion Principle&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;There are many great descriptions of LoD online but in practice, it boils down to dot chains are usually bad.  (E.G. &lt;code&gt;foo.bar.baz&lt;/code&gt;)  The reason they are bad is because they generally represent a place where encapsulation is broken.  In my example, the currently running code knows that &lt;code&gt;foo&lt;/code&gt; provides &lt;code&gt;bar&lt;/code&gt;, which is ok, but it also knows that &lt;code&gt;bar&lt;/code&gt; provides &lt;code&gt;baz&lt;/code&gt;.  Really, the currently running code should only know the methods on &lt;code&gt;foo&lt;/code&gt;.  If it needs &lt;code&gt;baz&lt;/code&gt;, it should call &lt;code&gt;foo.baz&lt;/code&gt;.  Note that this is not the case with Java's &lt;code&gt;sb = new StringBuffer(); sb.append(str).append(str);&lt;/code&gt; since in this example, you are working on &lt;code&gt;sb&lt;/code&gt; each time and not descending down into &lt;code&gt;sb&lt;/code&gt;'s children.&lt;br /&gt;&lt;br /&gt;The problem with trying to implement LoD in real code is that short of a complicated framework for avoiding the dot chains, you tend to end up writing lots of small delegation methods and you tend to have to write new ones every time a child's implementation changes.  Obviously this becomes fragile over time  and explains why there are refactorings for adding and removing delegation in Martin Fowler's famous book, Refactoring.&lt;br /&gt;&lt;br /&gt;Fortunately, I don't code in average languages anymore.  These days, I am all about Ruby.  In particular, I am usually writing Ruby in the context of Rails.  Over the last couple of months, my boss and I have developed an elegant way to add delegation to ActiveRecord objects.  Our technique could be extrapolated to a more general case but ActiveRecord models is where we want it so that is all we have done.&lt;br /&gt;&lt;br /&gt;The following example explains how our code used to work.&lt;br /&gt;&lt;pre&gt;class User &lt; ActiveRecord::Base; end&lt;br /&gt;class Agent &lt; User&lt;br /&gt;  has_one :agent_detail&lt;br /&gt;end&lt;br /&gt;class AgentDetail &lt; ActiveRecord::Base&lt;br /&gt;  belongs_to :agent&lt;br /&gt;  belongs_to :address&lt;br /&gt;end&lt;br /&gt;class Address &lt; ActiveRecord::Base&lt;br /&gt;  has_one :agent_detail&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;This is backed by a database with these tables:&lt;br /&gt;&lt;pre&gt;users: id, type, *&lt;br /&gt;agent_details: id, agent_id, address_id, *&lt;br /&gt;address: id, *&lt;/pre&gt;&lt;br /&gt;So, agents are users (and exist in the users table) but since agent needs more fields than user does and we didn't want to pollute the users table with the needs of every kind of user, we provide an agent_detail table that contains all the information that agents need and users don't.  Also, since addresses are a cross-cutting concern in an application, we decided to store all addresses in one table which can be referenced by others like agent_details.&lt;br /&gt;&lt;br /&gt;So, say you have an agent &lt;code&gt;bob&lt;/code&gt;.  To get bob's zip code, you would have to do &lt;code&gt;bob.agent_detail.address.zip_code&lt;/code&gt;, breaking encapsulation and delving far too deep into implementation details of an agent.  What we really want to be able to do is &lt;code&gt;bob.zip_code&lt;/code&gt;.  Our code shouldn't care how bob implements this field but it should be able to get this information directly from bob.  We also need to be able to set this information on bob with &lt;code&gt;bob.zip_code = '12345'&lt;/code&gt;.  The normal and naive way to do this follows:&lt;br /&gt;&lt;pre&gt;class Agent &lt; User&lt;br /&gt;  has_one :agent_detail&lt;br /&gt;&lt;br /&gt;  def zip_code&lt;br /&gt;    agent_detail.zip_code&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def zip_code=(zip_code)&lt;br /&gt;    agent_detail.zip_code = zip_code&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;class AgentDetail &lt; ActiveRecord::Base&lt;br /&gt;  belongs_to :agent&lt;br /&gt;  belongs_to :address&lt;br /&gt;&lt;br /&gt;  def zip_code&lt;br /&gt;    address.zip_code&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def zip_code=(zip_code)&lt;br /&gt;    address.zip_code = zip_code&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;This allows us to do &lt;code&gt;bob.zip_code&lt;/code&gt; but it has some problems.  For one, at the moment, nothing is ensuring all agents have an agent_detail and all agent_details have an address.  This means we are subject to nil problems (also know as NullPointerExceptions in the Java world).  Also, say we wanted to change the name of zip_code to zip?  We would have to change a table and a total of 8 lines of code in 2 different classes.  Not exactly the most robust system.  This also has some problems with saving dependent objects.  If you do &lt;code&gt;bob.zip = '12345'; bob.save&lt;/code&gt;, odds are the you expect the zip code change on address to be saved but it isn't.&lt;br /&gt;&lt;br /&gt;Ruby and Rails have some great tools that can help us simplify the code above and remove some of the fragility of the interface in the face of change.  Specifically, method_missing comes to the rescue to handle the &lt;code&gt;bob.zip_code&lt;/code&gt; method and delegate it down and &lt;code&gt;before_save&lt;/code&gt; and &lt;code&gt;after_safe&lt;/code&gt; ActiveRecord callbacks help solve the save problems.  This makes your code look like this:&lt;br /&gt;&lt;pre&gt;class Agent &lt; User&lt;br /&gt;  has_one :agent_detail&lt;br /&gt;&lt;br /&gt;  after_save {|agent| agent.agent_detail.save}&lt;br /&gt;&lt;br /&gt;  def safe_agent_detail&lt;br /&gt;    agent_detail.nil? : build_agent_detail ? agent_detail&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def method_missing(method_id, *args)&lt;br /&gt;    if safe_agent_detail.respond_to? method_id&lt;br /&gt;      agent_detail.send(method_id, *args)&lt;br /&gt;    else&lt;br /&gt;      super&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def respond_to?(method_id)&lt;br /&gt;    safe_agent_detail.respond_to?(method_id) || super&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;class AgentDetail &lt; ActiveRecord::Base&lt;br /&gt;  belongs_to :agent&lt;br /&gt;  belongs_to :address&lt;br /&gt;&lt;br /&gt;  before_save {|agent_detail| agent_detail.address.save}&lt;br /&gt;&lt;br /&gt;  def safe_address&lt;br /&gt;    address.nil? : build_address ? address&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def method_missing(method_id, *args)&lt;br /&gt;    if safe_address.respond_to? method_id&lt;br /&gt;      address.send(method_id, *args)&lt;br /&gt;    else&lt;br /&gt;      super&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def respond_to?(method_id)&lt;br /&gt;    safe_address.respond_to?(method_id) || super&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;This does neatly solve most of our problems but there is still one nagging itch, this code isn't very DRY.  Most of the details in AgentDetail and Address are the same.  What if we could take a page out of Rails and do this:&lt;br /&gt;&lt;pre&gt;class User &lt; ActiveRecord::Base; end&lt;br /&gt;class Agent &lt; User&lt;br /&gt;  has_one_delegate :agent_detail&lt;br /&gt;end&lt;br /&gt;class AgentDetail &lt; ActiveRecord::Base&lt;br /&gt;  belongs_to :agent&lt;br /&gt;  belongs_to_delegate :address&lt;br /&gt;end&lt;br /&gt;class Address &lt; ActiveRecord::Base&lt;br /&gt;  has_one :agent_detail&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;This is in fact, what my boss and I have done.  The following code provides &lt;code&gt;has_one_delegate&lt;/code&gt; and &lt;code&gt;belongs_to_delegate&lt;/code&gt;:&lt;br /&gt;&lt;pre&gt;module ActiveRecord&lt;br /&gt;  class Base&lt;br /&gt;    def self.belongs_to_delegate(delegate_id, options = {})&lt;br /&gt;      delegate_to(:belongs_to, delegate_id, options)&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def self.has_one_delegate(delegate_id, options = {})&lt;br /&gt;      delegate_to(:has_one, delegate_id, options)&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    private&lt;br /&gt;&lt;br /&gt;    def self.delegate_to(macro, delegate_id, options = {})&lt;br /&gt;      send macro, delegate_id, options&lt;br /&gt;&lt;br /&gt;      save_callback = {:belongs_to =&gt; :before_save, :has_one =&gt; :after_save}[macro]&lt;br /&gt;&lt;br /&gt;      send save_callback do |model|&lt;br /&gt;        model.send(delegate_id).save&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;      delegate_names = respond_to?('safe_delegate_names') ? safe_delegate_names : []&lt;br /&gt;      delegate_names = (delegate_names - [delegate_id]) + [delegate_id]&lt;br /&gt;&lt;br /&gt;      def_string, source_file, source_line = &lt;&lt;-"end_eval", __FILE__, __LINE__&lt;br /&gt;&lt;br /&gt;        def self.safe_delegate_names&lt;br /&gt;          #{delegate_names.inspect}&lt;br /&gt;        end&lt;br /&gt;&lt;br /&gt;        def safe_delegates&lt;br /&gt;          self.class.safe_delegate_names.collect do |delegate_name|&lt;br /&gt;            send(delegate_name).nil? ? send("build_\#{delegate_name}") : send(delegate_name)&lt;br /&gt;          end&lt;br /&gt;        end&lt;br /&gt;&lt;br /&gt;        def method_missing(method_id, *args)&lt;br /&gt;          safe_delegates.each do |delegate|&lt;br /&gt;            return delegate.send(method_id, *args) if delegate.respond_to?(method_id)&lt;br /&gt;          end&lt;br /&gt;          super&lt;br /&gt;        end&lt;br /&gt;&lt;br /&gt;        def respond_to?(*args)&lt;br /&gt;          safe_delegates.any? {|delegate| delegate.respond_to?(*args)} || super&lt;br /&gt;        end&lt;br /&gt;&lt;br /&gt;      end_eval&lt;br /&gt;&lt;br /&gt;      module_eval def_string, source_file, source_line + 1&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;The code itself isn't the most beautiful internally, but the simplicity it provides is elegant and effective.  Also, because we eventually did need more than one delegate for the same class, it is robust enough to allow multiple delegates.  Score one for the Ruby/Rails team.  An entire class of refactorings goes from recipe to method call.  This is why I love programming.&lt;br /&gt;&lt;br /&gt;Eventually, we plan to release this as a plugin to the Rails community.  Until then, let me know what you think.  Would you use it?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-115717306455557653?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/115717306455557653/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=115717306455557653' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/115717306455557653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/115717306455557653'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/09/activerecord-delegation-and-demeter.html' title='ActiveRecord, Delegation and Demeter'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-115089998132373588</id><published>2006-06-21T12:00:00.000-07:00</published><updated>2006-06-21T11:11:39.240-07:00</updated><title type='text'>When Inheritance Doesn't Pay - Problems Testing Rails</title><content type='html'>The smallest things have the capacity to cause the most pain.  Today's pain is caused by 5 characters, or to be more acurate, the absence of 5 characters.&lt;br /&gt;&lt;br /&gt;super&lt;br /&gt;&lt;br /&gt;Say it with me, super.  Yep, super.  Guess what Rails doesn't include in the setup of functional tests by default.  You betcha, super.  And the funny thing is, most of the time, it really doesn't matter.  But sometimes, oh boy, yeah, sometimes it makes all the difference.&lt;br /&gt;&lt;br /&gt;Given two test classes:&lt;br /&gt;&lt;br /&gt;a_test.rb&lt;br /&gt;&lt;code type="block"&gt;require File.dirname(__FILE__) + '/../test_helper'&lt;br /&gt;&lt;br /&gt;class ATest &lt; Test::Unit::TestCase&lt;br /&gt;  fixtures :empties&lt;br /&gt;&lt;br /&gt;  def setup&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def test_fixture&lt;br /&gt;    empties(:first)&lt;br /&gt;  end&lt;br /&gt;end&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;b_test.rb&lt;br /&gt;&lt;code type="block"&gt;require File.dirname(__FILE__) + '/a_test'&lt;br /&gt;class BTest &lt; ATest; end&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Would it surprise you that run seperately, both of these pass with flying colors, but run together, they result in abominable crashes?  It sure surprised me.&lt;br /&gt;&lt;br /&gt;&lt;code type="block"&gt;[~/projects/test]$ /usr/bin/ruby -Ilib:test "/usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader.rb" "test/unit/a_test.rb" Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader&lt;br /&gt;Started&lt;br /&gt;.&lt;br /&gt;Finished in 0.115591 seconds.&lt;br /&gt;&lt;br /&gt;1 tests, 0 assertions, 0 failures, 0 errors&lt;br /&gt;[~/projects/test]$ /usr/bin/ruby -Ilib:test "/usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader.rb" "test/unit/b_test.rb" Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader&lt;br /&gt;Started&lt;br /&gt;..&lt;br /&gt;Finished in 0.157902 seconds.&lt;br /&gt;&lt;br /&gt;2 tests, 0 assertions, 0 failures, 0 errors&lt;br /&gt;[~/projects/test]$ /usr/bin/ruby -Ilib:test "/usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader.rb" "test/unit/a_test.rb" "test/unit/b_test.rb"&lt;br /&gt;Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader&lt;br /&gt;Started&lt;br /&gt;EEEE&lt;br /&gt;Finished in 0.088633 seconds.&lt;br /&gt;&lt;br /&gt;  1) Error:&lt;br /&gt;test_fixture(ATest):&lt;br /&gt;NoMethodError: You have a nil object when you didn't expect it!&lt;br /&gt;You might have expected an instance of Array.&lt;br /&gt;The error occured while evaluating nil.[]&lt;br /&gt;    /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.2/lib/active_record/fixtures.rb:475:in `empties'&lt;br /&gt;    ./test/unit/a_test.rb:10:in `test_fixture'&lt;br /&gt;&lt;br /&gt;  2) Error:&lt;br /&gt;test_fixture(ATest):&lt;br /&gt;NoMethodError: You have a nil object when you didn't expect it!&lt;br /&gt;You might have expected an instance of Array.&lt;br /&gt;The error occured while evaluating nil.-&lt;br /&gt;    /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.2/lib/active_record/transactions.rb:112:in `unlock_mutex'&lt;br /&gt;    /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.2/lib/active_record/fixtures.rb:534:in `teardown'&lt;br /&gt;&lt;br /&gt;  3) Error:&lt;br /&gt;test_fixture(BTest):&lt;br /&gt;NoMethodError: You have a nil object when you didn't expect it!&lt;br /&gt;You might have expected an instance of Array.&lt;br /&gt;The error occured while evaluating nil.[]&lt;br /&gt;    /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.2/lib/active_record/fixtures.rb:475:in `empties'&lt;br /&gt;    ./test/unit/a_test.rb:10:in `test_fixture'&lt;br /&gt;&lt;br /&gt;  4) Error:&lt;br /&gt;test_fixture(BTest):&lt;br /&gt;NoMethodError: You have a nil object when you didn't expect it!&lt;br /&gt;You might have expected an instance of Array.&lt;br /&gt;The error occured while evaluating nil.-&lt;br /&gt;    /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.2/lib/active_record/transactions.rb:112:in `unlock_mutex'&lt;br /&gt;    /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.2/lib/active_record/fixtures.rb:534:in `teardown'&lt;br /&gt;&lt;br /&gt;2 tests, 0 assertions, 0 failures, 4 errors&lt;br /&gt;[~/projects/test]$&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The key to this dilemma should be pretty easy to find now that I have bloodied myself on the problem for you.  Exactly, super.  Add super to the setup and all our woes are cured.  I haven't got the slightest idea what magic TestCase.setup is doing but without it, we bomb every time.  Actually, I suspect inheritance isn't even the culprit here.  Running a with itself twice exhibits the same behavior, with the same solution.&lt;br /&gt;&lt;br /&gt;I suspect the error condition is related to the edge case of a test class completing then being loaded again but I'm not sure how to prove it.  I'm guessing the 2nd time through, an action isn't take because Rails is convinced it already happened.  But the moral is clear, super is your friend.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-115089998132373588?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/115089998132373588/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=115089998132373588' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/115089998132373588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/115089998132373588'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/06/when-inheritance-doesnt-pay-problems.html' title='When Inheritance Doesn&apos;t Pay - Problems Testing Rails'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114970899859427634</id><published>2006-06-07T11:07:00.001-07:00</published><updated>2006-06-07T16:49:31.510-07:00</updated><title type='text'>Compounding Errors - Another Rails Testing Adventure</title><content type='html'>The Moral:&lt;br /&gt;Always include &lt;code&gt;test_helper&lt;/code&gt; in Rails tests.&lt;br /&gt;Be specific when handling exceptions.&lt;br /&gt;Use &lt;code&gt;diff&lt;/code&gt;.  Trust &lt;code&gt;diff&lt;/code&gt;.&lt;br /&gt;Pair often.  Pair always.&lt;br /&gt;&lt;br /&gt;The Story:&lt;br /&gt;Every test class generated by Rails includes &lt;code&gt;require File.dirname(__FILE__) + '/../test_helper'&lt;/code&gt;.  If you remove it, or you write your own test and forget it, bad things can happen, things like this....&lt;br /&gt;&lt;br /&gt;First things first, how are the tests?  &lt;code&gt;rake test_functional&lt;/code&gt;.  Failure.  Hmm.  I expected that.  This project was written before we fully committed to test first.  It shouldn't be too hard to get the existing tests to run.  Let's see what we need.&lt;br /&gt;&lt;br /&gt;The failure is in the functional tests.  A require is failing and I don't know why.  I wonder where that file is supposed to come from?  Maybe it is really missing.  There is something similar in another one of our projects.  For now, I will just include the mock we used to make that test pass.  (Copy the &lt;code&gt;test/mock&lt;/code&gt; directory from one rails app to the other.)&lt;br /&gt;&lt;br /&gt;That should fix it.  &lt;code&gt;rake test_functional&lt;/code&gt;.  Blamo.  Still failing.  Hmm.  Naming problems?  File names seem to match.  Hack.  Hack.  Hack.  This sucks, I've burned an afternoon and there's nothing to show for it.&lt;br /&gt;&lt;br /&gt;Interlude.  A night of little sleep and a nagging weight on my mind.  Not fun.  The tension wakes me up at 3:00 AM and I start hacking from home.  It isn't like I was getting any useful rest anyway.&lt;br /&gt;&lt;br /&gt;So, the test isn't finding my mock either.  I don't get it.  Well, I guess I can check my load path before the require.&lt;br /&gt;&lt;pre&gt;puts $:.inspect&lt;br /&gt; ["lib", "test", "/usr/lib/site_ruby/1.8", "/usr/lib/site_ruby/1.8/i386-linux",&lt;br /&gt; "/usr/lib/site_ruby", "/usr/lib/ruby/1.8", "/usr/lib/ruby/1.8/i386-linux",&lt;br /&gt; "."]&lt;/pre&gt;&lt;br /&gt;That doesn't look right.  None of my rails includes are there.  Hack.  Hack.  Hack.&lt;br /&gt;Burned several morning hours and I still have to face Steve and tell him I have nothing to show for it.  Having that hanging over my head sure does make for a long morning commute.&lt;br /&gt;&lt;br /&gt;OK.  Before I talk to Steve about this, lets at least check his machine.  &lt;code&gt;rake test_functional&lt;/code&gt;  Passed.  Everything passed.  Wait.  WTF do you mean everything passed?!  OK.  Maybe I'm just an idiot (I mean past experience suggest this must frequently be the case.) so lets make sure I have the current code.  &lt;code&gt;svn status&lt;/code&gt;.  Nothing modified.  &lt;code&gt;svn udpate&lt;/code&gt;.  Nothing missing.  Well, maybe Steve made changes on his machine.  &lt;code&gt;svn status&lt;/code&gt;.  Nothing modified.  &lt;code&gt;svn udpate&lt;/code&gt;.  Nothing missing.&lt;br /&gt;&lt;br /&gt;I've been here before.  Code works on one machine but not on the other.  The problem is, this time, it is well tested code and I can't find anything that is different.  Discussing it with Steve didn't help either.  He spent a chunk of time getting all these tests to pass just recently so they should still work.&lt;br /&gt;&lt;br /&gt;Well, my machine is different.  We are planning a system upgrade and I'm the guinea pig.  But most of our software is the same.  Sure, the OS is different right now but....  Well, I can at least standardize my software install.  I'll just get our environment setup script to work.&lt;br /&gt;&lt;br /&gt;Great.  That script blows up on my machine too.  It can't get the file from subversion.  Well, of course it can't.  That doesn't look like the repository location to me.  Hack, hack.  Oh.  Idiot.  That was right.  The subversion repository is being served up by &lt;code&gt;http&lt;/code&gt; and I was looking for the &lt;code&gt;svn+ssh&lt;/code&gt; location.&lt;br /&gt;&lt;br /&gt;Well, it still blows up.  Compare the file path.  Still missing.  Load the directory in a browser.  There the file is.  But, umm, well.  OK.  Maybe it is something in the version.  That looks different in the script.  Nope.  That looks right.&lt;br /&gt;&lt;br /&gt;Eventually, I notice the file name the script is looking for is &lt;code&gt;.tar.bz2&lt;/code&gt; but the file name in the directory is &lt;code&gt;.tar.gz&lt;/code&gt;.  Don't ask how long it took.  Too long.&lt;br /&gt;&lt;br /&gt;It's looking for the wrong file name.  Why.  Where does that come from.  There it is.  But, wait, it should be looking for the name that is there first.  If it doesn't find that then it catches the exception and tries the other name.&lt;br /&gt;&lt;br /&gt;The light bulb finally goes on.&lt;br /&gt;&lt;br /&gt;Ah.  I'm getting an exception other than the expected exception and that is incorrectly being understood as a missing file.  And hey, while I'm at it, guess what, I should be running this as root and I'm not.  The real error is a permissions error but I can't see that because we handled every exception instead of just the missing file exception we expected.  And surprise, surprise, everything works great when I run it as root.  I leave the script to run over night since it is taking too long.&lt;br /&gt;&lt;br /&gt;Interlude.  Another bad night.  Another early morning.  This time I was so burned out that instead of going to hacking night, I crashed for 3 hours when I got home.  Of course that meant I couldn't get back to sleep until the wee hours.  And of course that didn't stop me from waking up way too early.&lt;br /&gt;&lt;br /&gt;Back at the office again.  Everything is good on Steve's machine, and not good on my machine.  Well, lets &lt;code&gt;diff&lt;/code&gt; the output.  I should have done this days ago but hindsight is 20/20 and foresight is blind as a bat.  Well, the commands for the tests aren't in the same order but I don't really see anything else.  Lets follow the &lt;code&gt;rake&lt;/code&gt; trace.&lt;br /&gt;&lt;br /&gt;The trace is different but I don't really understand why.  Hack.  Hack.  Oh, by the way, things work in &lt;code&gt;autotest&lt;/code&gt; but not &lt;code&gt;rake&lt;/code&gt;.  Now that is just creepy.&lt;br /&gt;&lt;br /&gt;Finally, I corner Steve to help me out.  I'm not solving this alone, so lets try some of that pairing juju.&lt;br /&gt;&lt;br /&gt;Hack here.  Hack there.  Trace this.  Trace that.  Eventually Steve notices what I already knew but this time, Steve gets it when I just blanked.  It is order dependent.  The tests are being run in a different order.  Hack, hack.  Well, we can't really control the order, it seems to be an artifact of the different OS.  Still, we should be able to figure out what causes the order dependency.  Hack.  Hack.&lt;br /&gt;&lt;br /&gt;Bingo, there it is.  This test (which was copied from another, non-rails project) doesn't include &lt;code&gt;test_helper&lt;/code&gt;.  And guess what.  &lt;code&gt;test_helper&lt;/code&gt; is where &lt;code&gt;environment.rb&lt;/code&gt; is loaded.  And &lt;code&gt;environment.rb&lt;/code&gt; is where the additional include paths are added, not to mention that &lt;code&gt;test_helper&lt;/code&gt; is where the mock paths are added.  No wonder it couldn't find those mocks that I added.&lt;br /&gt;&lt;br /&gt;Finally, we get to here and now.  Two and a half days of developer time wasted over several small errors, compounded together.  Cory Foy recently &lt;a href="http://www.cornetdesign.com/2006/05/being-agile-is-not-easy.html"&gt;wrote&lt;/a&gt; "And that's the trick with being Agile. If you slip, even for a second, it *will* bite you. You have to stay on guard constantly".  This is true of Agile but really, it is true of all development.  How many times have you been bitten by small problems that take days to figure out and would have been avoided with just that little bit more effort?&lt;br /&gt;&lt;br /&gt;Of course, the mark of a great developer isn't the lack of mistakes.  The mark of a great developer is learning from mistakes and preventing them in the future.  Soon, we will either have something that tests to make sure our tests include &lt;code&gt;test_helper&lt;/code&gt;, or even better a way to avoid that step in every single bloody test file.  We will also be more careful to trap only what we expect and leave the unexpected unhandled or at least properly handled.  And I will definitely learn to trust my &lt;code&gt;diff&lt;/code&gt;s earlier.&lt;br /&gt;&lt;br /&gt;Don't make my mistakes, but more importantly, don't make your mistakes twice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114970899859427634?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114970899859427634/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114970899859427634' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114970899859427634'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114970899859427634'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/06/compounding-errors-another-rails_07.html' title='Compounding Errors - Another Rails Testing Adventure'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114855196965493714</id><published>2006-05-25T02:37:00.000-07:00</published><updated>2006-05-25T09:43:19.563-07:00</updated><title type='text'>Assignment in Ruby - Block Assignment</title><content type='html'>For the second installment of Assignment in Ruby, I would like to explore block assignment.  Block assignment is very similar to the simple scoped assignments I explored in my first post.  To understand the differences, it is necessary to understand how block variables are scoped.&lt;br /&gt;&lt;br /&gt;When a block uses a local variable, Ruby has to determine what is being used.  &lt;code&gt;{ |x, y, z| puts x, y, z }&lt;/code&gt; uses three local variables.  Interestingly, these three variables could each potentially have a different scope.&lt;br /&gt;&lt;br /&gt;If there is an &lt;code&gt;x&lt;/code&gt; in the current local scope, the variable &lt;code&gt;x&lt;/code&gt; used in the block will be that local variable, meaning the assignment will be an &lt;code&gt;:lasgn&lt;/code&gt; (local assignment).&lt;br /&gt;&lt;br /&gt;If there is no &lt;code&gt;y&lt;/code&gt; in the current local scope but there is a &lt;code&gt;y&lt;/code&gt; in an enclosing block, the &lt;code&gt;y&lt;/code&gt; variable used will be the block scoped variable from the outer block and the assignment will be a &lt;code&gt;:dasgn&lt;/code&gt; (block assignment).&lt;br /&gt;&lt;br /&gt;If there is no &lt;code&gt;z&lt;/code&gt; in the current local scope and there is no &lt;code&gt;z&lt;/code&gt; in any enclosing blocks, then a new &lt;code&gt;z&lt;/code&gt; will be created in the scope of the current block and the assignment will be a &lt;code&gt;:dasgn_curr&lt;/code&gt; (block local assignment).&lt;br /&gt;&lt;br /&gt;The different scopes are each illustrated in the following code:&lt;br /&gt;&lt;pre&gt;$ echo "x = 123; foo() {|x| y = 456; bar() {|x,y,z| puts x, y, z}}" | parse_tree_show -f&lt;br /&gt;[[:lasgn, :x, [:lit, 123]],&lt;br /&gt; [:iter,&lt;br /&gt;  [:fcall, :foo],&lt;br /&gt;  [:lasgn, :x],&lt;br /&gt;  [:block,&lt;br /&gt;   [:dasgn_curr, :y],&lt;br /&gt;   [:dasgn_curr, :y, [:lit, 456]],&lt;br /&gt;   [:iter,&lt;br /&gt;    [:fcall, :bar],&lt;br /&gt;    [:masgn, [:array, [:lasgn, :x], [:dasgn, :y], [:dasgn_curr, :z]]],&lt;br /&gt;    [:fcall, :puts, [:array, [:lvar, :x], [:dvar, :y], [:dvar, :z]]]]]]]&lt;/pre&gt;&lt;br /&gt;Written a little more nicely, the code under inspection is:&lt;br /&gt;&lt;pre&gt;x = 123&lt;br /&gt;foo() { |x|&lt;br /&gt;  y = 456&lt;br /&gt;  bar() { |x,y,z|&lt;br /&gt;    puts x, y, z&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Note the 2nd to last line of the S expression &lt;code&gt;[:masgn, [:array, [:lasgn, :x], [:dasgn, :y], [:dasgn_curr, :z]]]&lt;/code&gt;.  The first thing to notice is&lt;code&gt;:masgn&lt;/code&gt;.  &lt;code&gt;:masgn&lt;/code&gt; stands for multiple assignment and will be covered in its own post.  After the &lt;code&gt;:masgn&lt;/code&gt; you can see concrete examples of the local assignment to &lt;code&gt;x&lt;/code&gt;, the outer block assignment to &lt;code&gt;y&lt;/code&gt; and the current block assignment to &lt;code&gt;z&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;This can be a sticky situation and I know I missed it when I was learning Ruby.  I only internalized how block scopes are handled when I saw it reflected in ParseTree and the AST.  I still don't really understand why you would want to reuse local scoped variables inside a block but that is probably an indication of my weakness with closures.  If you have any ideas, please let me know.&lt;br /&gt;&lt;br /&gt;I hope you have enjoyed another brief forray into Assignment in Ruby.  Join me next time as we explore multiple assignment and how it can simplify your life.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114855196965493714?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114855196965493714/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114855196965493714' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114855196965493714'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114855196965493714'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/05/assignment-in-ruby-block-assignment.html' title='Assignment in Ruby - Block Assignment'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114854952414347986</id><published>2006-05-25T02:17:00.000-07:00</published><updated>2006-05-25T02:33:31.086-07:00</updated><title type='text'>Climbing without a Net</title><content type='html'>Developing test first is like rock climbing on lead.&lt;br /&gt;&lt;br /&gt;When a climber climbs lead, they have to place protection as they go.  At any point, if they fall, their last piece of protection generally keeps them from falling far.  Sometimes, that protection will fail and when it does, the other protection they have placed keeps them from death or serious injury.&lt;br /&gt;&lt;br /&gt;Each test written is like a piece of protection we have placed.  Usually, the last test we wrote is the most important but all the tests together keep us from serious injury.  Our tests really are our safety net and I can't understand why people choose to live without it.&lt;br /&gt;&lt;br /&gt;If test first is lead climbing, then it goes to reason that the test first challenge is top roping and developing without tests is free climbing.  Sure the best climbers can skate up easy climbs without protection but for the rest of us, that is the path to certain doom.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114854952414347986?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114854952414347986/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114854952414347986' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114854952414347986'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114854952414347986'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/05/climbing-without-net.html' title='Climbing without a Net'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114832156763641955</id><published>2006-05-22T09:41:00.000-07:00</published><updated>2006-05-22T11:16:33.630-07:00</updated><title type='text'>When Rails Needs a Clue - Single Table Inheritance Problems</title><content type='html'>Given &lt;code&gt;Grandchild &lt; Child &lt; Parent &lt; ActiveRecord::Base&lt;/code&gt;, your controllers need &lt;code&gt;model :grandchild&lt;/code&gt;.  Without it, well, this could happen:&lt;br /&gt;&lt;br /&gt;There is a sinking feeling in your gut.  You've been working in rails for weeks.  You've been doing everything right, Test Driven Development to the hilt, when suddenly, the worst happens.  Behavior in test doesn't match behavior in development.  You begin to question whether any of your testing is valid.  Does your app really work, or does it just seem to work in test?  How can test and development possibly differ?  Your worst nightmares are coming true.&lt;br /&gt;&lt;br /&gt;This might seem like a ridiculous scenario but it is exactly what I have dealt with for the last week.  We have a table with single table inheritance.  &lt;code&gt;Admin &lt; Broker &lt; User &lt; ActiveRecord::Base&lt;/code&gt; all in table :users.  Errors started showing up in development that passed in test.  We tracked it down to differing sql statements.  In test, &lt;code&gt;Broker.find_all&lt;/code&gt; generated &lt;code&gt;SELECT * FROM users WHERE ( (users.`type` = 'Broker' OR users.`type` = 'Admin' ) )&lt;/code&gt;.  In development, the exact same &lt;code&gt;Broker.find_all&lt;/code&gt; generated &lt;code&gt;SELECT * FROM users WHERE ( (users.`type` = 'Broker' ) )&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;We had no idea what was going on and we had features that had to be ready right now for a demo.  We worked around the problem.  We wrote our own find conditions and moved on, writing a todo to find out what was wrong.  The sleepless nights started and the worry began to grow.&lt;br /&gt;&lt;br /&gt;Over the weekend, the other developer on my team looked at some code I wrote Friday and had problems.  He didn't have time to look into it but told me about it this morning.  My first response, well, I'll write a test that shows the problem, only every test I wrote worked fine in test.  I could reproduce his problems in development every time.  This shouldn't be so hard.&lt;br /&gt;&lt;br /&gt;Start from the basics.  I created a test with exactly the same input as was reported in the error on development.  Same parameters.  Same session.  Same flash.  Everything was exactly the same.  Only, what failed in development passed in test.  The sick feeling in my stomach is getting stronger.  I feel like I'm in a dream watching my career go down the drain.  Sure enough, I track it down to nearly the same problem as before.  We have &lt;code&gt;Default &lt; ActiveRecord::Base; belongs_to Broker&lt;/code&gt;.  If the &lt;code&gt;default.broker_id&lt;/code&gt; pointed to a &lt;code&gt;user&lt;/code&gt; with &lt;code&gt;type=Admin&lt;/code&gt;, it failed in development.  &lt;code&gt;Default.broker&lt;/code&gt; was returning nil when it should have been returning an &lt;code&gt;Admin&lt;/code&gt;.  Again, the SQL was different between test and development in exactly the same way.  Development would only accept &lt;code&gt;Brokers&lt;/code&gt; and test accepted &lt;code&gt;Brokers&lt;/code&gt; and &lt;code&gt;Admins&lt;/code&gt;.  This didn't make any sense and this time, we had time to look at it.&lt;br /&gt;&lt;br /&gt;My partner started looking at the source in Rails and I started searching online.  I didn't find anything with a few quick searches.  I knew the #rubyonrails channel on Freenode is pretty active so I decided to ask my question there.  "Has anyone seen differences between test and development when..."  Hmm.  I want the exact SQL I'm seeing in test and development before I ask this question.  I know, I'll fire up the console for both and get it from the logs that way.&lt;br /&gt;&lt;br /&gt;Loki took pity on me here and Coyote found something else to toy with.  &lt;code&gt;export RAILS_ENV=test; ./script/console&lt;/code&gt;.  So far, so good.  &lt;code&gt;Broker.find_all;&lt;/code&gt;  I only get &lt;code&gt;Brokers&lt;/code&gt; back.  WTF!  That's the same thing I've been seeing in development.  A quick check.  Yes, I'm in test.  Hmm.  &lt;code&gt;User.find_all;&lt;/code&gt;  I get everything.  &lt;code&gt;(select * from users&lt;/code&gt;, no where clause)  &lt;code&gt;Admin.find_all;&lt;/code&gt; I get only &lt;code&gt;Admins&lt;/code&gt;.  I expected that, &lt;code&gt;Admin&lt;/code&gt; doesn't have any children.  &lt;code&gt;Broker.find_all;&lt;/code&gt; Holy hand grenades.  That is everything, &lt;code&gt;Admins&lt;/code&gt; included.  The where clause changed.  Umm.  But.  Maybe this time I put something different.  Just to be sure, I used up arrow to run the first &lt;code&gt;Broker.find_all;&lt;/code&gt; (I couldn't see a difference but who knows at this point.)   Sure enough, the original search turns up the new results.  Then, wham, lightning struck.  If &lt;code&gt;Admin&lt;/code&gt; has never been loaded, Rails doesn't know &lt;code&gt;Admin &lt; Broker&lt;/code&gt;!&lt;br /&gt;&lt;br /&gt;A quick conversation with my partner, a little test (added &lt;code&gt;model :admin&lt;/code&gt; to the controller) and sure enough, everything is peachy.  There is just one nagging doubt.  I don't know how to create this in test.  You see, single table inheritance is implemented in a single YAML fixture.  When &lt;code&gt;fixtures :users&lt;/code&gt; runs, Rails figures out I have a &lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Broker&lt;/code&gt;, &lt;code&gt;Customer&lt;/code&gt; (&lt;code&gt;&lt; User&lt;/code&gt;) and &lt;code&gt;Admin&lt;/code&gt; model.  Loading my fixture seems to make Rails aware of those relationships.  So, how do I get data for admins into the table for users without making Rails aware of the &lt;code&gt;Admin&lt;/code&gt; class?  I have no idea.&lt;br /&gt;&lt;br /&gt;The sinking feeling isn't gone but it is a good deal smaller.  I now have a picture of the world that explains the unexplainable and test and development work the same again.  But still, that little doubt.&lt;br /&gt;&lt;br /&gt;If you know how to test this, won't you please let me in on the secret?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114832156763641955?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114832156763641955/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114832156763641955' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114832156763641955'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114832156763641955'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/05/when-rails-needs-clue-single-table.html' title='When Rails Needs a Clue - Single Table Inheritance Problems'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114805600152958799</id><published>2006-05-19T09:24:00.000-07:00</published><updated>2006-05-19T11:45:12.876-07:00</updated><title type='text'>Lost but not Forgotten</title><content type='html'>I still owe you several posts on the 12 kinds of Ruby assignment.  I also have more to write about my sagas with ParseTree and the like.  I can't promise to have them soon but I do promise to have them, eventually.  In the meantime, consider this my formal apology for letting you down.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114805600152958799?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114805600152958799/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114805600152958799' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114805600152958799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114805600152958799'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/05/lost-but-not-forgotten.html' title='Lost but not Forgotten'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114805578317136590</id><published>2006-05-19T09:09:00.000-07:00</published><updated>2006-05-19T09:35:12.010-07:00</updated><title type='text'>flunk, my autotest friend</title><content type='html'>You use &lt;code&gt;autotest&lt;/code&gt;, right?  (&lt;code&gt;gem install ZenTest&lt;/code&gt; and get on it if you don't).  Well, then you know &lt;code&gt;autotest&lt;/code&gt; auto-focuses on failing tests.  You probably also love this and rely on it all the time.  Well, leverage &lt;code&gt;flunk&lt;/code&gt; and take your relationship with &lt;code&gt;autotest&lt;/code&gt; to the next level.&lt;br /&gt;&lt;pre&gt;test_some_silly_thing&lt;br /&gt;  blah, blah, blah, blah&lt;br /&gt;  this is a test that should be much smaller but isn't&lt;br /&gt;  or is mystifying me with its strange behavior or something&lt;br /&gt;  and I want to debug what the F@#! is going on, and&lt;br /&gt;  I don't want to run the other 4800 tests that take more&lt;br /&gt;  than the 8 milliseconds attention span I am willing to&lt;br /&gt;  wait for my tests to run or, ... you get the idea&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;Now, lets make &lt;code&gt;autotest&lt;/code&gt; focus on &lt;code&gt;test_some_silly_thing&lt;/code&gt;.  Add the following line as the last line of the test:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  flunk "Passed!"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Cool!  Now, even if our test passes, we fail.  This means autotest will stay focused while we play.  You can now do things like check the database after your test runs (because it is the only test that was run and no other tests had a chance to muck things up).&lt;br /&gt;&lt;br /&gt;By the way, if you don't already know, &lt;code&gt;flunk&lt;/code&gt; is the equivalent of &lt;code&gt;assert false&lt;/code&gt;.  It is so much easier to read and understand than &lt;code&gt;assert false&lt;/code&gt; is though.&lt;br /&gt;&lt;br /&gt;A few links you might find useful:&lt;br /&gt;&lt;a href='http://www.zenspider.com/ZSS/Products/ZenTest/'&gt;ZenTest&lt;/a&gt;&lt;br /&gt;&lt;a href="http://nubyonrails.com/articles/2006/04/19/autotest-rails"&gt;Using autotest with Rails&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.linuxjournal.com/article/8921"&gt;article on ZenTest in Linux Journal&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.segment7.net/articles/2006/04/25/autotest-sucks"&gt;Dr. Brain rails against his own creation&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.segment7.net/articles/category/zentest"&gt;Everything else Dr. Brain said about ZenTest in his blog&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114805578317136590?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114805578317136590/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114805578317136590' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114805578317136590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114805578317136590'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/05/flunk-my-autotest-friend.html' title='flunk, my autotest friend'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114492145513173416</id><published>2006-04-12T18:38:00.000-07:00</published><updated>2006-04-19T19:31:19.733-07:00</updated><title type='text'>Assignment in Ruby - Simple Scoped Assignment</title><content type='html'>The largest class of assignments in Ruby are the simple scoped assignments.  Ruby represents assignment to a name in a particular scope with its own scope assignment node.  I won't go into what nodes mean to the VM in this article but think of them as named expressions.  The following table shows the simple scoped assignments and their related code and abstract syntax trees (AST).&lt;br /&gt;&lt;br /&gt;&lt;table border="1" align="left" cellpadding="3" bordercolor="#ffffff" width="100%"&gt;&lt;thead align="left"&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Scope&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;AST Name&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;Code&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;AST&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Local&lt;/td&gt;&lt;td&gt;&lt;code&gt;:lasgn&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;a=nil&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;[:lasgn, :a, [:nil]]&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Instance&lt;/td&gt;&lt;td&gt;&lt;code&gt;:iasgn&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;@a=nil&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;[:iasgn, :@a, [:nil]]&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Class&lt;/td&gt;&lt;td&gt;&lt;code&gt;:cvasgn&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;@@a=nil&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;[:cvasgn, :@@a, [:nil]]&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Global&lt;/td&gt;&lt;td&gt;&lt;code&gt;:gasgn&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;$a=nil&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;[:gasgn, :$a, [:nil]]&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;tfoot&gt;&lt;tr&gt;&lt;td colspan="4"&gt;Each of these ASTs was generated using ParseTree 1.4.1 and the following command:&lt;br/&gt;&lt;code&gt;echo "code" | parse_tree_show -f&lt;/code&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;As you can see, each of these has the same structure in the AST.  By using different types of nodes for each one, it is simpler for the Ruby VM to determine where to look for a variable and set it.&lt;br /&gt;&lt;br /&gt;The code below is a straight through SexpProcessor using the ParseTree gem and the included SexpProcessor as a base.  This processor doesn't do anything exciting but by explicitly writing the process_type methods, we have exposed interaction points where we could do things like store a list of assigned variables, output funny messages or generate metrics.&lt;br /&gt;&lt;br /&gt;Sexp stands for &lt;a href="http://en.wikipedia.org/wiki/S-expression"&gt;S-expression&lt;/a&gt;.  ParseTree represents S-expressions in ruby as nested arrays of arrays.  S-expressions are particularly well known for their use in most Lisp like languages to represent code and data.  If you would like to know more about S-expressions, Google is your friend.&lt;br /&gt;&lt;br /&gt;SexpProcessors have a few rules that you must observe in order to get a correct traversal of the expression.  First, you need to understand that the dispatch method process(exp) is initially called for every pair of matched brackets.  This is important because in our process_foo methods, we need to call process() on any members of exp that are arrays.  If we don't, we essentially prune that piece of the S-expression from our processing.  Usually, that is wrong.  It is also important to not that process(exp) only takes arrays as input.  If you call process(exp.shift) and the shifted element is a literal, you will have an error on your hands.  process(exp) will then pass control onto process_foo(exp) where foo is the :literal that appears as the first element of the expression.  (In our processor, because of the auto_shift_type in initialize, the first element of exp is shifted off before control passes to process_foo.  This results in slightly cleaner, easier to read code in process foo.  Instead of &lt;code&gt;s(exp.shift, exp.shift, process(exp.shift))&lt;/code&gt;, we get the slightly more clear &lt;code&gt;s(:iasgn, exp.shift, process(exp.shift))&lt;/code&gt;.  This is obviously a personal preference.)&lt;br /&gt;&lt;br /&gt;Another basic rule of SexpProcessor is that what comes in should be what comes out, or, in the case of auto_shift_type, what would have come out if auto_shift_type were false.  This is best illustrated with an example.&lt;br /&gt;&lt;br /&gt;process([:lasgn, :a, [nil]]) should return [:lasgn, :a, [nil]]&lt;br /&gt;&lt;br /&gt;with auto_shift_type = false&lt;br /&gt;process([:lasgn, :a, [nil]]) calls process_lasgn([:lasgn, :a, [nil]])&lt;br /&gt;&lt;br /&gt;with auto_shift_type = true&lt;br /&gt;process([:lasgn, :a, [nil]]) calls process_lasgn([:a, [nil]])&lt;br /&gt;&lt;br /&gt;in either case process_lasgn() must return [:lasgn, :a, [nil]].&lt;br /&gt;&lt;br /&gt;Still another rule, in a process_foo(exp) method, exp should be empty before the method returns.  Failure to empty the exp is considered bad form (and is usually wrong too) and therefor raises an error.  This is the quickest way to catch errors in coding like&lt;br /&gt;&lt;pre&gt;# process_lasgn([:a, [nil]])&lt;br /&gt;def process_lasgn(exp) &lt;br /&gt;  s(:lasgn, # auto_shift_type = true or this would be s(exp.shift,&lt;br /&gt;    exp.shift) # :a&lt;br /&gt;  # exp now == [[nil]]&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;This will really help you when you get the structure wrong for the s-expression representing a node.&lt;br /&gt;&lt;br /&gt;The final bit of magic to understand before venturing off to write your own processor is s().  s(*args) is shorthand for Sexp.new(*args).  It was added to keep things easier to read and as a user of ParseTree &amp; SexpProcessor, I am sure you will appreciate it.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;# A Straight Through SexpProcessor exposing&lt;br /&gt;# the process_foo simple assignment methods.&lt;br /&gt;&lt;br /&gt;begin require 'rubygems' rescue LoadError end&lt;br /&gt;require 'parse_tree'&lt;br /&gt;require 'sexp_processor'&lt;br /&gt;&lt;br /&gt;class PassThroughProcessor &lt; SexpProcessor&lt;br /&gt;  def initialize&lt;br /&gt;    super&lt;br /&gt;    self.auto_shift_type = true&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def process_lasgn(exp)&lt;br /&gt;    s(:lasgn, exp.shift, process(exp.shift))&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def process_iasgn(exp)&lt;br /&gt;    s(:iasgn, exp.shift, process(exp.shift))&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def process_cvasgn(exp)&lt;br /&gt;    s(:cvasgn, exp.shift, process(exp.shift))&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def process_gasgn(exp)&lt;br /&gt;    s(:gasgn, exp.shift, process(exp.shift))&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You would invoke this processor on an unsuspecting class with the following magic incantation.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;PassThroughProcessor.new.process(ParseTree.new.parse_tree(klass))&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you are a glutton for punishment, you could run it against PassThroughProcessor.  (*Note: As is, this wouldn't create any output at all but the skeleton is all there.  Enjoy yourself.*)&lt;br /&gt;&lt;br /&gt;I have more than used up the time and space allotted for this article.  I hope this was educational and I look forward to seeing you next time with Assignment in Ruby.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114492145513173416?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114492145513173416/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114492145513173416' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114492145513173416'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114492145513173416'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/04/assignment-in-ruby-simple-scoped.html' title='Assignment in Ruby - Simple Scoped Assignment'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114466785399697499</id><published>2006-04-10T04:09:00.000-07:00</published><updated>2006-04-10T06:47:08.263-07:00</updated><title type='text'>Assignment in Ruby</title><content type='html'>Would you believe Ruby has a dozen different ways to represent assignment internally?  This may be something you don't expect to find when you go poking around the internals of things like the Ruby implementation or ParseTree.  I know I didn't.&lt;br /&gt;&lt;br /&gt;Over the coming weeks, I will:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;document the 12 different representations&lt;/li&gt;&lt;li&gt;discuss what they are and what is unique about them&lt;/li&gt;&lt;li&gt;show you what ruby code creates each one and the AST (abstract syntax tree) generated by ParseTree for each&lt;/li&gt;&lt;li&gt;show you a code example of how you would write your own parse_foo method in a SexpParser if you wanted to track something about each one&lt;/li&gt;&lt;li&gt;show you what we have done (if anything) with each one in CheckR and explain why&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Join me in an journey under the covers as I explore assignment in Ruby.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114466785399697499?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114466785399697499/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114466785399697499' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114466785399697499'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114466785399697499'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/04/assignment-in-ruby.html' title='Assignment in Ruby'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114456557195197337</id><published>2006-04-08T23:51:00.000-07:00</published><updated>2006-04-08T23:52:51.956-07:00</updated><title type='text'>Ping Pong Programming - No Pairing</title><content type='html'>&lt;div&gt;&lt;div class="goalentry"&gt;&lt;p&gt;The paradigm of ping pong pairing can be used without pairing.  I have been ping pong programming remotely with Pat Eyler for a couple weeks now and it has been tremendously effective.  Basically, the process for ping ponging remotely is the same as ping pong pairing but instead of passing a keyboard back and forth, we check our code into Subversion.  A typical scenario looks something like this:&lt;/p&gt;	&lt;p&gt;Pat updates his code to make sure everything is current.  He runs the test suite to see what the currently failing test is.  After some analysis, he fixes the code and makes the test pass.  Now, he checks his code into Subversion.  Next, he writes a new test and confirms that it fails.  Because he has to communicate the change to me, he checks his failing test into Subversion with a comment about the current test.  Finally, he notifies me that the ball is in my court.  It is my turn to repeat the process.&lt;/p&gt;	&lt;p&gt;This style of development is more powerful than simple Test Driven Development for the same reasons I mentioned before.  By posing tests for your opponent, you develop stronger tests than you do for yourself.&lt;/p&gt;	&lt;p&gt;Remote ping pong does have its shortcomings though.  In particular, it doesn&amp;#8217;t deal with time imbalances very well.  While Pat is implementing my test, I am doing nothing.  If I have several hours to devote to coding and Pat only has a few minutes, I will spend a lot of time waiting for a test when I could be coding.&lt;/p&gt;	&lt;p&gt;I want to address the time disparity concern by figuring out what works in ping pong and creating a model with all the benefits but without the time constrained challenges.  In addition, I would like to figure out how to apply the principles of ping pong to teams of more than two people.  A tenative name for this model of development is Test Exchange.&lt;/p&gt;	&lt;p&gt;I will describe Test Exchange in a later post.&lt;/p&gt;&lt;/div&gt;&lt;div class="goalprogresslink"&gt;See more progress on: &lt;a href="http://www.43things.com/people/progress/milythael?on=2672054"&gt;Create a form of ping pong programming for distributed teams.&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114456557195197337?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114456557195197337/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114456557195197337' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114456557195197337'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114456557195197337'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/04/ping-pong-programming-no-pairing_08.html' title='Ping Pong Programming - No Pairing'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114456406721469817</id><published>2006-04-08T23:26:00.000-07:00</published><updated>2006-04-08T23:27:47.256-07:00</updated><title type='text'>Ping Pong Pair Programming</title><content type='html'>&lt;div&gt;&lt;div class="goalentry"&gt;&lt;p&gt;I don&amp;#8217;t know who first coined the name Ping Pong Pair Programming but I know from the first time I heard of it, I was convinced.  It seems likely that &lt;span class="caps"&gt;PPP &lt;/span&gt;(ping pong pairing) arose out of a need to address some of the challenges of pair programming.  Namely, the difficulty of maintaining attention when you are not the one at the keyboard.&lt;/p&gt;	&lt;p&gt;&lt;span class="caps"&gt;PPP&lt;/span&gt; is also a derivative of Test Driven Development.  In &lt;span class="caps"&gt;TDD&lt;/span&gt;, you write your test first and you never write code for a feature that is not in a failing test already.  It is a great way to ensure a robust test suite for all of your development.  &lt;span class="caps"&gt;TDD&lt;/span&gt; works like this: 1) write a test, 2) confirm that it fails, 3) write code to make the test pass, 4) confirm the test passes, 5) confirm no other tests are broken, 6) refactor mercilessly.  &lt;span class="caps"&gt;TDD&lt;/span&gt; is very effective but there are challenges.  It is very easy to shortcut your tests a little by writing more than you really need.  It is also easy to avoid testing the really hard cases.&lt;/p&gt;	&lt;p&gt;Ping pong pairing works with two people.  Let us call them Bob and Tom.  The day starts with Bob at the keyboard.  All the tests pass.  Bob writes a test.  Bob confirms it fails.  The keyboard passes to Tom.  Tom works on making the test pass with Bob at his side, helping him along.  Tom finishes his implementation and confirms all the tests pass.  Tom writes a new test and confirms it fails.  The keyboard returns to Bob.&lt;/p&gt;	&lt;p&gt;&lt;span class="caps"&gt;PPP&lt;/span&gt; is very similar to &lt;span class="caps"&gt;TDD&lt;/span&gt; in that it is made up of te same steps.  The difference is that after a failing test is implemented, the other developer takes control.  This can develop into a friendly game.  Bob tries to write a good test that forces Tom to implement real and valuable functionality.  Tom tries to pass Bob&amp;#8217;s test with the simplest thing that could possibly work.  The harder Bob and Tom try to push the difficult work to the other person, the more robust the test suite and the finished code become.&lt;/p&gt;	&lt;p&gt;Ping pong pairing addresses the psychological challenges in both pair programming and test driven development.  Because control switches so frequently, partners stay more engaged in what is going on.  Because you don&amp;#8217;t have to implement the code for your own tests, you are much less likely to take shortcuts just to allow yourself to get by.&lt;/p&gt;	&lt;p&gt;Ping pong pairing is hard work and is has been very rewarding when I have done it.  It results in much better code and more robust tests.  It also helps to keep things moving at a good pace.  If you pair program, I strongly recommend you try your hand at &lt;span class="caps"&gt;PPP&lt;/span&gt;.&lt;/p&gt;&lt;/div&gt;&lt;div class="goalprogresslink"&gt;See more progress on: &lt;a href="http://www.43things.com/people/progress/milythael?on=2672054"&gt;Create a form of ping pong programming for distributed teams.&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114456406721469817?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114456406721469817/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114456406721469817' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114456406721469817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114456406721469817'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/04/ping-pong-pair-programming.html' title='Ping Pong Pair Programming'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114449564611042539</id><published>2006-04-08T04:25:00.000-07:00</published><updated>2006-04-08T04:27:26.116-07:00</updated><title type='text'>StL.rb Hacking Nights</title><content type='html'>&lt;div&gt;&lt;div class="goalentry"&gt;&lt;p&gt;Seattle.rb has a meeting every week.  Once a month they have presentations and the other weeks are dedicated to hacking nights.  I have envied this for quite a while and I finally decided to do something about it.&lt;/p&gt;	&lt;p&gt;Tuesday, April 4 was the first ever &lt;a href="http://www.stlruby.org/"&gt;StL.rb&lt;/a&gt; hacking night.  After announcing it a couple weeks in advance, I spent 3 hours at Borders and 2 other members of StL.rb made it.  Borders wasn&amp;#8217;t the best place to hold the meetings so next week we are going to try St. Louis Bread Company.  Most of you might know it as Panera Bread Company.  They didn&amp;#8217;t change the name in St. Louis.  I guess people might have gotten upset.&lt;/p&gt;	&lt;p&gt;I&amp;#8217;ve made myself available for hacking any night people want but the weekly hacking nights are going to be on the same weeknight as our regular meeting.  Since that is Tuesday, I will be too busy to be jealous of Seattle.rb&amp;#8217;s meetings on the same night.&lt;/p&gt;&lt;/div&gt;&lt;div class="goalprogresslink"&gt;See more progress on: &lt;a href="http://www.43things.com/people/progress/milythael?on=182355"&gt;help build local Ruby Brigades&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114449564611042539?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114449564611042539/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114449564611042539' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114449564611042539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114449564611042539'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/04/stlrb-hacking-nights.html' title='StL.rb Hacking Nights'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114449509732188483</id><published>2006-04-08T04:16:00.000-07:00</published><updated>2006-04-08T04:18:17.366-07:00</updated><title type='text'>CheckR</title><content type='html'>&lt;div&gt;&lt;div class="goalentry"&gt;&lt;p&gt;I started &lt;a href="http://rubyforge.org/projects/checkr/"&gt;CheckR&lt;/a&gt; with a friend.  It hopes to be a static source analysis tool for Ruby code, along the lines of lint for C/C++.  We are actively developing on it in a remote, ping pong, test driven development way.  We are very close to our first alpha release.  The first release will hopefully add some small value and be able to catch a few errors earlier in the development cycle.  I guess we need autocheckr too.  And we definitely need to run checkr against itself.&lt;/p&gt;&lt;/div&gt;&lt;div class="goalprogresslink"&gt;See more progress on: &lt;a href="http://www.43things.com/people/progress/milythael?on=2664129"&gt;start an open source project&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114449509732188483?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114449509732188483/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114449509732188483' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114449509732188483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114449509732188483'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/04/checkr.html' title='CheckR'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114435219851130823</id><published>2006-04-06T12:04:00.000-07:00</published><updated>2006-04-06T12:45:03.743-07:00</updated><title type='text'>Emacs + ZenTest = ZenMacs?</title><content type='html'>Good friends make you a better person.  Because of the recent example of Ryan Davis, Eric Hodel and especially Pat Eyler, and the less recent example of Gus Mueller, I am a better programmer who does better things.  The following is one of those things.&lt;br /&gt;&lt;br /&gt;I have been learning Emacs and the ZenTest suite at the same time.  This morning I came up with the following bit of goodness.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/528/60/1600/emacs_before.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/528/60/320/emacs_before.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;M-x autotest&lt;/code&gt; from a buffer whose current dir is the root of your project results in:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/528/60/1600/emacs_after.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/528/60/320/emacs_after.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;You now have a shell with autotest running in the bottom left pane, an eshell ready for your subversion commands in the bottom right pane and you can work with your project in the top pane.&lt;br /&gt;&lt;br /&gt;This is due to a macro I wrote to create this setup for me automagically.  The macro does the following:  (*warning emacs speak*)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;C-x 2, C-x o, C-x 3&lt;br /&gt;M-x shell&lt;br /&gt;  autotest&lt;br /&gt;M-x set-variable&lt;br /&gt;  comint-scroll-to-bottom-on-output&lt;br /&gt;  all&lt;br /&gt;C-x o&lt;br /&gt;M-x eshell&lt;br /&gt;M-x set-variable&lt;br /&gt;  comint-scroll-to-bottom-on-output&lt;br /&gt;  all&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I have included this little macro in my .emacs file with the following bit of elisp:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;(fset 'autotest&lt;br /&gt;   [?\C-x ?2 ?\C-x ?o ?\C-x ?3 ?\M-x ?s ?h ?e ?l ?l ?\C-m ?a ?u ?t ?o ?t ?e ?s ?t ?\C-m ?\M-x ?s ?e ?t ?- ?v ?a ?r ?i ?a ?b ?l ?e ?\C-m ?c ?o ?m ?i ?n ?t ?- ?s ?c ?r ?o ?l ?l ?- ?t ?o ?- ?b ?o ?t ?t ?o ?m ?- ?o ?n ?- ?o ?u ?t ?p ?u ?t ?\C-m ?a ?l ?l ?\C-m ?\C-x ?o ?\M-x ?e ?s ?h ?e ?l ?l ?\C-m ?\M-x ?s ?e ?t ?- ?v ?a ?r ?i ?a ?b ?l ?e ?\C-m ?c ?o ?m ?i ?n ?t ?- ?s ?c ?r ?o ?l ?l ?- ?t ?o ?- ?b ?o ?t ?t ?o ?m ?- ?o ?n ?- ?o ?u ?t ?p ?u ?t ?\C-m ?a ?l ?l ?\C-m])&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Enjoy and let me know if you can think of any improvements.  In particular, I would like to prompt for a directory path at the beginning and I need to figure out how to handle pre-existing *shell* or *eshell* buffers.  Still, as is, this does what I need.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114435219851130823?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114435219851130823/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114435219851130823' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114435219851130823'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114435219851130823'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/04/emacs-zentest-zenmacs.html' title='Emacs + ZenTest = ZenMacs?'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114426718433230879</id><published>2006-04-05T12:48:00.000-07:00</published><updated>2006-04-05T13:02:04.293-07:00</updated><title type='text'>___Pandora____Just____Rocks____</title><content type='html'>For the first time ever, I find myself contemplating the use of a blink tag.  Discretion has prevented it but it was a close race.&lt;br /&gt;&lt;br /&gt;Pandora is incredible.  My tastes in music have always been fairly eclectic.  I like stuff from all over the board but I don't really understand why I like what I like.  I have been using Pandora for a couple of days and I already understand more about my taste in music than 30+ years of developing that taste taught me.&lt;br /&gt;&lt;br /&gt;As you listen to a custom play list based on Pandora's analysis of something you said you like (for example, I am listening to the Evanescence station right now.), take the time to explore you options.  If you hear something you really like, give it a thumbs up and the qualities of that track will be used to select future tracks.  If you hear something you don't like, give it a thumbs down.  You will never hear that track again on that station.  Also, Pandora seems to learn from that, and maybe the one difference that had from other tracks will be thrown out.  In a frighteningly quick amount of time, artists I like start cross polinating channels and emerging spontaneously.&lt;br /&gt;&lt;br /&gt;At any time if you want to know why Pandora suggested a track, you can ask it.  It will give a quick summary of the musical properties that contributed to its selection.  If you keep the process up, fairly quickly you will start to see common themes.  If you pay attention to those descriptions, you start to see a pattern emerge.  It turns out I really like minor key tonality, mixed acoustic and electric instrumentation, subtle use of vocal harmony and mild rhythmic syncopation.  Woah.  When I look back, almost everything that has ever knocked my socks off has combined a few of these features.  Suddenly the most disparate of my tastes come together and make perfect sense.&lt;br /&gt;&lt;br /&gt;I was totally unprepared to learn about myself from a random music link I found on the net.  If you haven't tried it yet, why are you reading this tripe?!  Expand your horizons and get yourself over to &lt;a href="http://pandora.com/"&gt;Pandora&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114426718433230879?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114426718433230879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114426718433230879' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114426718433230879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114426718433230879'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/04/pandorajustrocks.html' title='___Pandora____Just____Rocks____'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114414995393830226</id><published>2006-04-04T04:22:00.000-07:00</published><updated>2006-04-04T04:25:53.953-07:00</updated><title type='text'>Opening Pandora's Box</title><content type='html'>Wow.  Just bloody freaking wow.  I don't know how I got there but you have got to check out &lt;a href="http://pandora.com/"&gt;Pandora&lt;/a&gt; if you like music, any music, even just a little tiny bit.  The interface isn't great.  I have no idea where they got it but it really does suck.  Still, if you can help me find more music in some of the obscure areas I like, then you are golden and Pandora does it with spades.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114414995393830226?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114414995393830226/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114414995393830226' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114414995393830226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114414995393830226'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/04/opening-pandoras-box.html' title='Opening Pandora&apos;s Box'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114411716222023326</id><published>2006-04-03T19:04:00.000-07:00</published><updated>2006-04-08T11:48:22.930-07:00</updated><title type='text'>unit_diff is your friend</title><content type='html'>Pat introduced me to unit_diff a while ago.  At the time, it didn't resonate very well.  There isn't much documentation and I didn't get what it was supposed to do.  Now, I know exactly what it does and I can give first hand testimony to how great it is.&lt;br /&gt;&lt;br /&gt;unit_diff is one of the tools included in &lt;a href="http://www.zenspider.com/ZSS/Products/ZenTest/"&gt;ZenTest&lt;/a&gt;, along with zentest, multiruby and autotest.  If you haven't installed it yet and you are using Test/Unit (You are using Test/Unit, right?), then you absolutely must grab the gem.  &lt;code&gt;$ sudo gem install ZenTest&lt;/code&gt;  unit_diff runs diff on the expected and actual output of failed assert_equal tests in your test cases.  Pipe your test output into unit_diff and relax with all the extra time you now have since you don't have to search through pages and pages of output to find the 2 characters that were different.&lt;br /&gt;&lt;br /&gt;Actual command example I used recently:&lt;br /&gt;&lt;pre&gt;$ ruby -Ilib test/test_parse_tree.rb | unit_diff&lt;br /&gt;Loaded suite test/test_parse_tree&lt;br /&gt;Started&lt;br /&gt;.....FF.........................&lt;br /&gt;Finished in 0.347 seconds.&lt;br /&gt;&lt;br /&gt;1) Failure:&lt;br /&gt;test_case_stmt2(TestParseTree) [(eval):1]:&lt;br /&gt;6,10c6,8&lt;br /&gt;&lt;    [:case,&lt;br /&gt;&lt;     nil,&lt;br /&gt;&lt;     [:when, [:array, [:lit, 1]], [:lit, 2]],&lt;br /&gt;&lt;     [:when, [:array, [:lit, 3]], [:lit, 4]],&lt;br /&gt;&lt;     [:lit, 5]]]]]&lt;br /&gt;---&lt;br /&gt;&gt;    [:when, [:array, [:lit, 1]], [:lit, 2]],&lt;br /&gt;&gt;    [:when, [:array, [:lit, 3]], [:lit, 4]],&lt;br /&gt;&gt;    [:lit, 5]]]]&lt;br /&gt;&lt;br /&gt;2) Failure:&lt;br /&gt;test_class(TestParseTree) [test/test_parse_tree.rb:370]:&lt;br /&gt;Must return a lot of shit.&lt;br /&gt;76,80c76,78&lt;br /&gt;&lt;      [:case,&lt;br /&gt;&lt;       nil,&lt;br /&gt;&lt;       [:when, [:array, [:lit, 1]], [:lit, 2]],&lt;br /&gt;&lt;       [:when, [:array, [:lit, 3]], [:lit, 4]],&lt;br /&gt;&lt;       [:lit, 5]]]]],&lt;br /&gt;---&lt;br /&gt;&gt;      [:when, [:array, [:lit, 1]], [:lit, 2]],&lt;br /&gt;&gt;      [:when, [:array, [:lit, 3]], [:lit, 4]],&lt;br /&gt;&gt;      [:lit, 5]]]],&lt;br /&gt;&lt;br /&gt;32 tests, 32 assertions, 2 failures, 0 errors&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Without unit_diff, this was nearly 700 lines of output.  Imagine how hard that was to process.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114411716222023326?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114411716222023326/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114411716222023326' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114411716222023326'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114411716222023326'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/04/unitdiff-is-your-friend.html' title='unit_diff is your friend'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114411512128851283</id><published>2006-04-03T18:12:00.000-07:00</published><updated>2006-04-03T20:07:53.916-07:00</updated><title type='text'>Debugging ParseTree (or Walking in Great Footsteps)</title><content type='html'>In the course of creating &lt;a href="http://rubyforge.org/projects/checkr"&gt;CheckR&lt;/a&gt;, &lt;a href="http://on-ruby.blogspot.com/"&gt;Pat Eyler&lt;/a&gt; and I discovered the following strangeness:&lt;br /&gt;&lt;br /&gt;First example.  Case with expression followed by multiple whens and an else.&lt;br /&gt;&lt;pre&gt;$ echo 'case 1; when 2; 3; when 4; 5; else; 6;end' | parse_tree_show -f&lt;br /&gt;[[:class,&lt;br /&gt; :Example,&lt;br /&gt; :Object,&lt;br /&gt; [:defn,&lt;br /&gt;  :example,&lt;br /&gt;  [:scope,&lt;br /&gt;   [:block,&lt;br /&gt;    [:args],&lt;br /&gt;    [:case,&lt;br /&gt;     [:lit, 1],&lt;br /&gt;     [:when, [:array, [:lit, 2]], [:lit, 3]],&lt;br /&gt;     [:when, [:array, [:lit, 4]], [:lit, 5]],&lt;br /&gt;     [:lit, 6]]]]]]]&lt;br /&gt;$&lt;/pre&gt;&lt;br /&gt;This results in a clear AST (abstract syntax tree).&lt;br /&gt;&lt;br /&gt;Second example.  Case with no expression followed by identical whens and else.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ echo 'case; when 2; 3; when 4; 5; else; 6;end' | parse_tree_show -f&lt;br /&gt;[[:class,&lt;br /&gt; :Example,&lt;br /&gt; :Object,&lt;br /&gt; [:defn,&lt;br /&gt;  :example,&lt;br /&gt;  [:scope, [:block, [:args], [:when, [:array, [:lit, 2]], [:lit, 3]]]]]]]&lt;br /&gt;$&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Since the only change was making &lt;code&gt;case 1;' into 'case;&lt;/code&gt;, this AST is far from clear and is obviously missing things.  There is no case node, no second when node and no else value.  Unfortunately, for CheckR, we need the when nodes for our testing.  Something has to be done.&lt;br /&gt;&lt;br /&gt;Pat is on very good terms with &lt;a href="http://drbrain.livejournal.com/"&gt;drbrain&lt;/a&gt; (Eric Hodel) and &lt;a href="http://blog.zenspider.com/"&gt;zenspider&lt;/a&gt; (Ryan Davis) as the three of them ran &lt;a href="http://www.zenspider.com/Languages/Ruby/Seattle/index.html"&gt;Seattle.rb&lt;/a&gt; together.  A few IMs later the bug was confirmed against the development branch of &lt;a href="http://rubyforge.org/projects/parsetree"&gt;ParseTree&lt;/a&gt;.  drbrain recorded a bug on the ParseTree RubyForge &lt;a href="http://rubyforge.org/tracker/?func=detail&amp;atid=1778&amp;amp;aid=3970&amp;group_id=439"&gt;bug tracker&lt;/a&gt; and assigned it to zenspider.&lt;br /&gt;&lt;br /&gt;Because I want to be a responsible netizen and (let's admit it) because I want to be cool like the big boys, I decided to create a test case for inclusion in the ParseTree test suite.  Then, because it would be even cooler, and because Pat is always there egging me on, I thought I would take a whack at fixing ParseTree myself, with Pat's help.&lt;br /&gt;&lt;br /&gt;Because of Pat's relationship with zenspider, we had access to a snapshot of the development tips of ParseTree.  zenspider is not using RubyForge to host the dev code, so not everyone has access to this.  As a result, I will document the rest of this story as if we had access to the same things the world has access to.  All references to source code will be to the released code for ParseTree-1.3.7.&lt;br /&gt;&lt;br /&gt;First the test case.  This is very likely to be wrong since I don't have a correct AST of the &lt;code&gt;case;when&lt;/code&gt; syntax but it is a start.  ParseTree uses two files to dynamically generate its test case, something.rb and test_parse_tree.rb.  something.rb contains method and class definitions to be parsed.  test_parse_tree.rb contains expected ASTs to be compared against the ASTs generated during the tests.  All it takes to add a new test case is add the method definition to something.rb and the expected AST to test_parse_tree.rb.&lt;br /&gt;&lt;br /&gt;Before we go mucking things up, we need to know where the test suite stands as released.  We have to make sure we don't break anything else when we add our own tests.&lt;br /&gt;&lt;br /&gt;The following results were produced on Cygwin.  It was also necessary on Cygwin to comment out &lt;code&gt;$TESTING = true&lt;/code&gt; in test_composite_sexp_processor.rb and test_sexp_processor.rb in order to get RubyInline to work correctly when compiling for the test.  Many thanks to drbrain for his patience and help with that particular problem.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ cd /usr/lib/ruby/gems/1.8/gems/ParseTree-1.3.7&lt;br /&gt;$ make test&lt;br /&gt;Loaded suite test/test_all&lt;br /&gt;Started&lt;br /&gt;.....F.F..F......F..............................F................................&lt;br /&gt;Finished in 0.328 seconds.&lt;br /&gt;&lt;br /&gt;-- output snipped due to length --&lt;br /&gt;&lt;br /&gt;81 tests, 94 assertions, 5 failures, 0 errors&lt;br /&gt;make: *** [test] Error 1&lt;br /&gt;$&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If I recall, there were only 4 errors in linux when I ran this same test.  Either way, it is very important to know about the failing tests before we start.  We don't want to assume we broke more than we intended when we add our tests.  (As an asside, the dev branch doesn't have these errors, allowing us to be even more confident in what we do.)  Now, on to the test.&lt;br /&gt;&lt;br /&gt;in test/something.rb&lt;br /&gt;&lt;pre&gt;def case_stmt2&lt;br /&gt; case&lt;br /&gt; when 1&lt;br /&gt;   2&lt;br /&gt; when 3&lt;br /&gt;   4&lt;br /&gt; else&lt;br /&gt;   5&lt;br /&gt; end&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is fairly simple but contains everything I know about the problem to date.  Namely, ParseTree seems to eat the case, when 3 and else.&lt;br /&gt;&lt;br /&gt;in test/test_parse_tree.rb&lt;br /&gt;&lt;pre&gt; @@case_stmt2 = [:defn, :case_stmt2,&lt;br /&gt;   [:scope,&lt;br /&gt;     [:block,&lt;br /&gt;       [:args],&lt;br /&gt;       [:case,&lt;br /&gt;         [:when, [:array, [:lit, 1]], [:lit, 2]],&lt;br /&gt;         [:when, [:array, [:lit, 3]], [:lit, 4]],&lt;br /&gt;         [:lit, 5]]]]]&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;At first glance, it seems fairly difficult to generate this and it would have been if I had done it by hand.  The easy way was to run the following command, steal its output and munge it just a little bit.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ echo 'class Blah; def case_stmt2; case true; when 1; 2; when 3; 4; else; 5; end; end; end' | parse_tree_show&lt;br /&gt;[[:class,&lt;br /&gt; :Blah,&lt;br /&gt; :Object,&lt;br /&gt; [:defn,&lt;br /&gt;  :case_stmt2,&lt;br /&gt;  [:scope,&lt;br /&gt;   [:block,&lt;br /&gt;    [:args],&lt;br /&gt;    [:case,&lt;br /&gt;     [:true],&lt;br /&gt;     [:when, [:array, [:lit, 1]], [:lit, 2]],&lt;br /&gt;     [:when, [:array, [:lit, 3]], [:lit, 4]],&lt;br /&gt;     [:lit, 5]]]]]]]&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Looking at the other expected trees, we see they all start with the method definition.  Also, we need to remove the &lt;code&gt;[:true]&lt;/code&gt; because it doesn't appear in our test method.  I want to reiterate, the resulting expectation, recorded above in test_parse_tree.rb may not be correct but it is a place to start.&lt;br /&gt;&lt;br /&gt;Finally, we run our test again.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ make test&lt;br /&gt;ruby  -w -Ilib:bin:../../RubyInline/dev test/test_all.rb&lt;br /&gt;Loaded suite test/test_all&lt;br /&gt;Started&lt;br /&gt;.....F.F..FF......F..............................F................................&lt;br /&gt;Finished in 0.354 seconds.&lt;br /&gt;&lt;br /&gt;-- output snipped due to length --&lt;br /&gt;&lt;br /&gt;  3) Failure:&lt;br /&gt;test_case_stmt2(TestParseTree) [(eval):1]:&lt;br /&gt;&lt;[:defn,&lt;br /&gt; :case_stmt2,&lt;br /&gt; [:scope,&lt;br /&gt;  [:block,&lt;br /&gt;   [:args],&lt;br /&gt;   [:case,&lt;br /&gt;    [:when, [:array, [:lit, 1]], [:lit, 2]],&lt;br /&gt;    [:when, [:array, [:lit, 3]], [:lit, 4]],&lt;br /&gt;    [:lit, 5]]]]]&gt; expected but was&lt;br /&gt;&lt;[:defn,&lt;br /&gt; :case_stmt2,&lt;br /&gt; [:scope, [:block, [:args], [:when, [:array, [:lit, 1]], [:lit, 2]]]]]&gt;.&lt;br /&gt;&lt;br /&gt;-- output snipped due to length --&lt;br /&gt;&lt;br /&gt;82 tests, 95 assertions, 6 failures, 0 errors&lt;br /&gt;make: *** [test] Error 1&lt;br /&gt;$&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;One new test failure and it's our test.  Perfect.  Also, if you look closely, you will see our new test is included in the massive output of test_class.  That's important because even if we started with no test failures at all, our addition of one test would cause two failures.  test_class is an integration test of the class as a whole (including all methods) and ensures that working methods still parse in a sane way as a class and also that the AST for the class definition itself is correct.&lt;br /&gt;&lt;br /&gt;Now, any sane person would stop here, submit the test to the ParseTree developers and call it a day.  Unfortunately, neither Pat nor I appears to be sane, so we started chasing the cause of the failure.&lt;br /&gt;&lt;br /&gt;To Be Continued....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114411512128851283?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114411512128851283/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114411512128851283' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114411512128851283'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114411512128851283'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/04/debugging-parsetree-or-walking-in.html' title='Debugging ParseTree (or Walking in Great Footsteps)'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-24133085.post-114243389294665107</id><published>2006-03-15T06:42:00.000-08:00</published><updated>2006-03-15T06:44:52.956-08:00</updated><title type='text'>Convictions</title><content type='html'>Recently, I am becoming convinced of a couple of things. First, emacs is good. This one is growing on me. I still suck completely at emacs but the more I learn, the more I think there is to like. Second, I have lost it. I am not a great programmer. I am pretty sure I used to be but I have descended from great to merely good. Something is missing. I have lost the drive that used to make me devour all things programming. I wonder if I can get it back.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/24133085-114243389294665107?l=sean-carley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sean-carley.blogspot.com/feeds/114243389294665107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=24133085&amp;postID=114243389294665107' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114243389294665107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24133085/posts/default/114243389294665107'/><link rel='alternate' type='text/html' href='http://sean-carley.blogspot.com/2006/03/convictions.html' title='Convictions'/><author><name>Sean</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
