<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en-US">
  <title>Wood for the Trees</title>
  <subtitle type="html">struggling to dig life</subtitle>
  <id>tag:www.mathewabonyi.com,2005:Typo</id>
  <generator uri="http://www.typosphere.org" version="4.0">Typo</generator>
  
  <link href="http://www.mathewabonyi.com" rel="alternate" type="text/html" />
  <updated>2007-10-09T23:59:57-05:00</updated>
  <link rel="self" href="http://feeds.feedburner.com/WoodForTheTrees" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:f438f74a-16c8-4b1a-a3bd-8cf6c6c555c3</id>
    <published>2007-10-09T21:51:00-05:00</published>
    <updated>2007-10-09T23:59:57-05:00</updated>
    <title type="html">Metaprogramming.apply(Aspects)</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/cltEL8KE6MM/metaprogramming-apply-aspects" rel="alternate" type="text/html" />
    <category term="discoveries" scheme="http://www.mathewabonyi.com/articles/category/discoveries" label="Discoveries" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="aop" scheme="http://www.mathewabonyi.com/articles/tag/aop" />
    <category term="aspects" scheme="http://www.mathewabonyi.com/articles/tag/aspects" />
    <category term="aquarium" scheme="http://www.mathewabonyi.com/articles/tag/aquarium" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/tag/ruby" />
    <category term="discoveries" scheme="http://www.mathewabonyi.com/articles/tag/discoveries" />
    <category term="metaprogramming" scheme="http://www.mathewabonyi.com/articles/tag/metaprogramming" />
    
  <content type="html">&lt;p&gt;I&amp;#8217;m just briefly breaking my silence (which must continue a bit longer) to raise awareness about an impressive and very useful gem which has just come onto the scene: &lt;a href="http://aquarium.rubyforge.org/"&gt;Aquarium&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;Although I don&amp;#8217;t follow Aspect Oriented Programming very closely, I know about its benefits and use it when necessary. All Rails developers, for example, employ its principles without even knowing it: before, after and around filters, proxies to alter execution, callbacks and the like. If you don&amp;#8217;t know about &lt;span class="caps"&gt;AOP&lt;/span&gt; yet, basically it&amp;#8217;s the practice of separating components which appear across the various classes of an application to provide system or class-agnostic functionality&amp;#8212;for example, logging, authentication or object persistence.&lt;/p&gt;


	&lt;p&gt;Having these components intrude on, for example, calculating a bank balance really gets annoying after a while. Imagine every moderately sized script you write needing lots of diagnostic messages, custom event handlers, and the like. It gets very tedious because you know there&amp;#8217;s the ideal of reuse just around the corner.&lt;/p&gt;


	&lt;h3&gt;Clean, Ruby-esque &lt;span class="caps"&gt;AOP&lt;/span&gt;&amp;#8230; a Pipe Dream?&lt;/h3&gt;


	&lt;p&gt;Imagine methods without calls to a logger, without a litter of &amp;#8216;if/then/else&amp;#8217; for authentication or authorisation, without calls to save an object and checking if it returns true or false&amp;#8230; What you&amp;#8217;ll get is the raw functionality of your class or module, which&amp;#8230; umm, is as it should be. Wrapping a method (using cuts, wrapping or aspects from &lt;span class="caps"&gt;AOP&lt;/span&gt;) lets you extract common functionality which cuts across objects.&lt;/p&gt;


	&lt;p&gt;Until now, there&amp;#8217;s only been the Facets libraries for &lt;span class="caps"&gt;AOP&lt;/span&gt; (&amp;#8216;advice&amp;#8217;, &amp;#8216;aop&amp;#8217; and &amp;#8216;cut&amp;#8217; in Facets 2.0.0) and AspectR. The former is buggy at best and the latter is like returning to Java. So I wasn&amp;#8217;t very keen on &lt;span class="caps"&gt;AOP&lt;/span&gt; in Ruby at all. Metaprogramming seemed easy enough and didn&amp;#8217;t need the structure of &lt;span class="caps"&gt;AOP&lt;/span&gt; to follow.&lt;/p&gt;


	&lt;p&gt;Enter Aquarium. It&amp;#8217;s a glass of clean water in a desert. Ruby-style. The most important thing it offers is a &amp;#8216;Join Point Model&amp;#8217; which will make you wet. In other words, it is very easy to intercept method calls to wrap them and introduce functionality before or after the original definition.&lt;/p&gt;


	&lt;p&gt;Sadly, I don&amp;#8217;t have the time for an extensive example from my own coding, so I&amp;#8217;ll have to rip off the example from Aquarium&amp;#8217;s own &lt;span class="caps"&gt;README&lt;/span&gt; which I think illustrates its power best (with a few modifications to show how I use it at the moment): method tracing.&lt;/p&gt;


	&lt;h3&gt;Example: Method Tracing&lt;/h3&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;
&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;rubygems&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;aquarium&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;aquarium/aspects/dsl/object_dsl&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="comment"&gt;# makes the Aspects DSL (before/after/around) system-wide&lt;/span&gt;

&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;Example&lt;/span&gt;
  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Foo&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;
      &lt;span class="ident"&gt;p&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Inside:   Foo#initialize: args = &lt;span class="expr"&gt;#{args.inspect}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;do_it&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;
      &lt;span class="ident"&gt;p&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Inside:   Foo#do_it: args = &lt;span class="expr"&gt;#{args.inspect}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;BarModule&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;
      &lt;span class="ident"&gt;p&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Inside:   BarModule#initialize: args = &lt;span class="expr"&gt;#{args.inspect}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;do_something_else&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;
      &lt;span class="ident"&gt;p&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Inside:   BarModule#do_something_else: args = &lt;span class="expr"&gt;#{args.inspect}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Bar&lt;/span&gt;
    &lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;BarModule&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;p&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Before advising the methods:&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="ident"&gt;foo1&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Example&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Foo&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="symbol"&gt;:a1&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:a2&lt;/span&gt;
&lt;span class="ident"&gt;foo1&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;do_it&lt;/span&gt; &lt;span class="symbol"&gt;:b1&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:b2&lt;/span&gt;

&lt;span class="ident"&gt;bar1&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Example&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Bar&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="symbol"&gt;:a3&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:a4&lt;/span&gt;
&lt;span class="ident"&gt;bar1&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;do_something_else&lt;/span&gt; &lt;span class="symbol"&gt;:b3&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:b4&lt;/span&gt;

&lt;span class="comment"&gt;# EXAMPLE 1: Wrapping methods on multiple classes using the powerful 'types' matcher.&lt;/span&gt;
&lt;span class="comment"&gt;# It can take constants or regular expressions. Also demonstrates method_options, which&lt;/span&gt;
&lt;span class="comment"&gt;# can specify public, protected, private, inherited or local methods.&lt;/span&gt;

&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;Example&lt;/span&gt;
  &lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;Aquarium&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Aspects&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;DSL&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;AspectDSL&lt;/span&gt;
  &lt;span class="ident"&gt;around&lt;/span&gt; &lt;span class="symbol"&gt;:types&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="constant"&gt;Foo&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;Bar&lt;/span&gt;&lt;span class="punct"&gt;],&lt;/span&gt; &lt;span class="symbol"&gt;:methods&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; 
  &lt;span class="symbol"&gt;:method_options&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:suppress_ancestor_methods&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;jp&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="keyword"&gt;begin&lt;/span&gt;
      &lt;span class="ident"&gt;p&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Ex. 1: Entering: &lt;span class="expr"&gt;#{jp.target_type.name}&lt;/span&gt;#&lt;span class="expr"&gt;#{jp.method_name}&lt;/span&gt;: args = &lt;span class="expr"&gt;#{args.inspect}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="ident"&gt;jp&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;proceed&lt;/span&gt;
    &lt;span class="keyword"&gt;ensure&lt;/span&gt;
      &lt;span class="ident"&gt;p&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Ex. 1: Leaving:  &lt;span class="expr"&gt;#{jp.target_type.name}&lt;/span&gt;#&lt;span class="expr"&gt;#{jp.method_name}&lt;/span&gt;: args = &lt;span class="expr"&gt;#{args.inspect}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="comment"&gt;# EXAMPLE 2: Wrapping one or more methods very simply.&lt;/span&gt;
&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;Example&lt;/span&gt;
  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Bar&lt;/span&gt;
    &lt;span class="ident"&gt;after&lt;/span&gt; &lt;span class="symbol"&gt;:do_something_else&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;jp&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;p&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Ex. 2: Simply after: &lt;span class="expr"&gt;#{jp.target_type.name}&lt;/span&gt;#&lt;span class="expr"&gt;#{jp.method_name}&lt;/span&gt;: args = &lt;span class="expr"&gt;#{args.inspect}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="comment"&gt;# EXAMPLE 3: Using the Aspect class without the DSL&lt;/span&gt;

&lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;Aquarium&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Aspects&lt;/span&gt;
&lt;span class="constant"&gt;Aspect&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="symbol"&gt;:before&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:method&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:do_it&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:type&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;Example&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Foo&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;jp&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
  &lt;span class="ident"&gt;p&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Ex. 3: Simply before (using Aspect.new): &lt;span class="expr"&gt;#{jp.target_type.name}&lt;/span&gt;#&lt;span class="expr"&gt;#{jp.method_name}&lt;/span&gt;: args = &lt;span class="expr"&gt;#{args.inspect}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="comment"&gt;# EXAMPLE 4: Contain aspects within a namespace for clarity. These are executed&lt;/span&gt;
&lt;span class="comment"&gt;# upon loading the source.&lt;/span&gt;

&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;Example&lt;/span&gt;
  &lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;AllAspects&lt;/span&gt;
    &lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;Aquarium&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Aspects&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;DSL&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;AspectDSL&lt;/span&gt;
    &lt;span class="ident"&gt;before&lt;/span&gt; &lt;span class="symbol"&gt;:do_it&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:type&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;Example&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Foo&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;jp&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;p&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Ex. 4: Simply before (using namespaces): &lt;span class="expr"&gt;#{jp.target_type.name}&lt;/span&gt;#&lt;span class="expr"&gt;#{jp.method_name}&lt;/span&gt;: args = &lt;span class="expr"&gt;#{args.inspect}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="comment"&gt;# And now see the result...&lt;/span&gt;
&lt;span class="ident"&gt;puts&lt;/span&gt;
&lt;span class="ident"&gt;p&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;After advising the methods. Notice that #intialize isn't advised:&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="ident"&gt;foo2&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Example&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Foo&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="symbol"&gt;:a5&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:a6&lt;/span&gt;
&lt;span class="ident"&gt;foo2&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;do_it&lt;/span&gt; &lt;span class="symbol"&gt;:b5&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:b6&lt;/span&gt;

&lt;span class="ident"&gt;bar1&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Example&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Bar&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="symbol"&gt;:a7&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:a8&lt;/span&gt;
&lt;span class="ident"&gt;bar1&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;do_something_else&lt;/span&gt; &lt;span class="symbol"&gt;:b7&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:b8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;You could argue that the namespace for Aquarium needs a little work and the availability of Aspect.new system wide should be a given, but adding that oneself until Aquarium has it is no big deal. I also didn&amp;#8217;t demonstrate pointcuts, another option to around, before and after. With a pointcut, you can encapsulate the type, scope and method names&amp;#8212;perhaps as a constant, class variable or class method to reduce dependencies.&lt;/p&gt;


	&lt;h3&gt;Overview&lt;/h3&gt;


	&lt;p&gt;All in all, the &lt;span class="caps"&gt;DSL&lt;/span&gt; is already miles better than anything else out there and Aquarium was only released to the public months ago. To summarise, I think this &lt;span class="caps"&gt;DSL&lt;/span&gt; is killer because it is&amp;#8230;&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;clear about what is affected&lt;/li&gt;
		&lt;li&gt;easy to separate from what is affected&lt;/li&gt;
		&lt;li&gt;rich with selectors (type, scope, name, pointcut)&lt;/li&gt;
		&lt;li&gt;able to choose whether to alter method execution&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;So, I would definitely suggest giving it a go and seeing how it can modularise some of your &amp;#8216;cross cutting concerns&amp;#8217;. I also suggest the Aquarium &lt;span class="caps"&gt;README&lt;/span&gt; and the reading recommended on its site, http://aquarium.rubyforge.org/.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=cltEL8KE6MM:RRismPNQatM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=cltEL8KE6MM:RRismPNQatM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=cltEL8KE6MM:RRismPNQatM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=cltEL8KE6MM:RRismPNQatM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=cltEL8KE6MM:RRismPNQatM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=cltEL8KE6MM:RRismPNQatM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/10/09/metaprogramming-apply-aspects</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:bbb7ac96-fc6f-4e5d-a4eb-81a89dae2371</id>
    <published>2007-08-05T00:38:00-05:00</published>
    <updated>2007-08-28T00:44:49-05:00</updated>
    <title type="html">Distributed SCMs: Be Smart, Use the Bleeding Edge</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/rQZDDnsWCmA/distributed-scms-be-smart-use-the-bleeding-edge" rel="alternate" type="text/html" />
    <category term="management" scheme="http://www.mathewabonyi.com/articles/category/management" label="Management" />
    <category term="mercurial" scheme="http://www.mathewabonyi.com/articles/category/mercurial" label="Mercurial" />
    <category term="announcements" scheme="http://www.mathewabonyi.com/articles/category/announcements" label="Announcements" />
    <category term="svn" scheme="http://www.mathewabonyi.com/articles/tag/svn" />
    <category term="subversion" scheme="http://www.mathewabonyi.com/articles/tag/subversion" />
    <category term="scm" scheme="http://www.mathewabonyi.com/articles/tag/scm" />
    <category term="management" scheme="http://www.mathewabonyi.com/articles/tag/management" />
    <category term="mercurial" scheme="http://www.mathewabonyi.com/articles/tag/mercurial" />
    <category term="methodology" scheme="http://www.mathewabonyi.com/articles/tag/methodology" />
    <category term="git" scheme="http://www.mathewabonyi.com/articles/tag/git" />
    <category term="bazaar" scheme="http://www.mathewabonyi.com/articles/tag/bazaar" />
    <category term="bzr" scheme="http://www.mathewabonyi.com/articles/tag/bzr" />
    <category term="darcs" scheme="http://www.mathewabonyi.com/articles/tag/darcs" />
    
  <content type="html">&lt;p&gt;I&amp;#8217;ll preface this title by admitting I&amp;#8217;ve not paid much attention to my version control software. When you&amp;#8217;re smart (&lt;strong&gt;cue 16-ton weight&lt;/strong&gt;), you try to think ahead and choose software or hardware which will vastly reduce the effort of programming. While staying on the edge with most things, for version control I neglected to really think about it. I had a very brief and painful time with &lt;span class="caps"&gt;CVS&lt;/span&gt; and promptly converted to Subversion because it was already installed, everyone used it, it was much easier, it solved the immediate problems with &lt;span class="caps"&gt;CVS I&lt;/span&gt; had, and it did what I needed to do at the time.&lt;/p&gt;


	&lt;p&gt;Of course, in programming, we all know that &amp;#8220;what I needed to do&amp;#8221; is never the same as &amp;#8220;what I need to do now&amp;#8221;. I have a lot more code to manage, I have a lot of projects which I am developing on my own or manage or take a serious part in, and I have more projects which I want to dip my feet into. The number of snippets, little things here and there, and feedback or changes from other people has meant a lot of branches, tags, &lt;ins&gt;svn cp&lt;/ins&gt; and other management issues.&lt;/p&gt;


	&lt;p&gt;Now, this might sound like a &amp;#8220;shiny thing&amp;#8221; moment, but it is completely different from shiny thing syndrome. My advice to practically everyone reading this is to keep moving at the cutting edge of version control&amp;#8230; and that doesn&amp;#8217;t mean upgrading to Subversion 1.4.4. It means that there are genuine advances in version control which go well beyond Subversion and well beyond what it will ever be. I&amp;#8217;ve known about distributed SCMs for a while, but haven&amp;#8217;t really bothered looking into it. I&amp;#8217;m writing this post because I think a lot of people are in the same boat. Subversion is supported, if not as the default &lt;span class="caps"&gt;SCM&lt;/span&gt;, practically everywhere: Sourceforge, Rubyforge, Google Code, Unfuddle, Lighthouse, etc. Every respectable project hosting service uses Subversion and almost every plugin, gem or developer out there at the moment references to Subversion repositories. Well, I&amp;#8217;m going to make a serious splash in this department and say: it&amp;#8217;s time to move on from Subversion. I have completely grown out of it, and I&amp;#8217;m not even managing a codebase as large as Rails (or even ActiveRecord).&lt;/p&gt;


	&lt;p&gt;The single biggest development in version control over the last umpteen years, but specifically the last 2, is distributed version control. Basically, that means everyone&amp;#8217;s working copy is a repository, every commit is local and every &amp;#8216;commit&amp;#8217; to another repository is a &amp;#8216;push&amp;#8217; (every &amp;#8216;update&amp;#8217; from another repository is a &amp;#8216;pull&amp;#8217;)&amp;#8212;that means merging is the default behaviour when you communicate with another repository. In other words, every repository is a branch. This way, distributed version control doesn&amp;#8217;t force a single source code management architecture. These systems are completely open and the advantages to this model-less model are (obviously) endless:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;most importantly, distribution is all about caring more about the &lt;strong&gt;coder-to-code relationship&lt;/strong&gt; than the &lt;strong&gt;coder-to-project relationship&lt;/strong&gt;&lt;/li&gt;
		&lt;li&gt;Distributed SCMs are &lt;strong&gt;orders of magnitude faster&lt;/strong&gt; in almost every department&lt;/li&gt;
		&lt;li&gt;they usually use &lt;strong&gt;a bit more space&lt;/strong&gt;&lt;/li&gt;
		&lt;li&gt;actual commits are almost instant, since they are local&lt;/li&gt;
		&lt;li&gt;merging is simplified enormously&amp;#8212;it&amp;#8217;s the daily business of a &lt;span class="caps"&gt;DSCM&lt;/span&gt;&lt;/li&gt;
		&lt;li&gt;no &lt;strong&gt;need&lt;/strong&gt; for an update&amp;#8212;&lt;strong&gt;you merge when ready&lt;/strong&gt;&lt;/li&gt;
		&lt;li&gt;development can happen anywhere&amp;#8212;&lt;strong&gt;no connection required&lt;/strong&gt;&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;no default model&lt;/strong&gt; of management or hierarchy&lt;/li&gt;
		&lt;li&gt;much more intuitive code structure&amp;#8212;&lt;strong&gt;do what you like&lt;/strong&gt;&lt;/li&gt;
		&lt;li&gt;no need for access control, but it&amp;#8217;s there if you need it&lt;/li&gt;
		&lt;li&gt;etc.&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;You&amp;#8217;ll see the benefits just in the development of your own code, without merging with anyone else&amp;#8217;s repositories but your own, that distribution of management doesn&amp;#8217;t require distribution of code. You can use any methodology, any hierarchy, any paradigm of managing your code that you wish.&lt;/p&gt;


	&lt;p&gt;Returning to my original point, staying at the bleeding edge doesn&amp;#8217;t mean upgrade to the next minor or major version to get the latest feature. It means upgrade, or change software if necessary, to use the latest breakthroughs which will cut costs and save time for your own projects or your company&amp;#8217;s projects.&lt;/p&gt;


	&lt;p&gt;Now, you&amp;#8217;re probably asking which distributed &lt;span class="caps"&gt;SCM&lt;/span&gt; to use. I personally go for &lt;a href="http://www.selenic.com/mercurial/wiki/index.cgi/Mercurial"&gt;Mercurial&lt;/a&gt; for the following reasons:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;span class="caps"&gt;SVK&lt;/span&gt; is a lie&amp;#8212;it is not distributed; it just distributes &lt;span class="caps"&gt;SVN&lt;/span&gt;, which is like a multiplying a herd of turtles&lt;/li&gt;
		&lt;li&gt;much faster than Arch, &lt;a href="http://darcs.net/"&gt;Darcs&lt;/a&gt; and &lt;a href="http://bazaar-vcs.org/"&gt;Bazaar&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;only &lt;a href="http://git.or.cz"&gt;Git&lt;/a&gt; is faster&amp;#8212;but that was developed by Linus Torvalds&lt;/li&gt;
		&lt;li&gt;Mercurial and Git are relatively younger, meaning they&amp;#8217;ve learnt from the mistakes of other DSCMs&lt;/li&gt;
		&lt;li&gt;Mercurial and Git come from the same background (the BitKeeper drama) and BitKeeper, according to Torvalds, was the only &lt;span class="caps"&gt;SCM&lt;/span&gt; worth using&lt;/li&gt;
		&lt;li&gt;Mercurial compares feature-for-feature with other DSCMs, and then some&lt;/li&gt;
		&lt;li&gt;Mercurial uses less space than others&lt;/li&gt;
		&lt;li&gt;very user friendly &lt;span class="caps"&gt;CLI&lt;/span&gt;&amp;#8212;by far the easiest to learn of the DSCMs&lt;/li&gt;
		&lt;li&gt;decent documentation&lt;/li&gt;
		&lt;li&gt;a few project hosting sites already (like &lt;a href="http://sharesource.org/"&gt;ShareSource&lt;/a&gt;)&lt;/li&gt;
		&lt;li&gt;ability to have Mercurial support using the &lt;span class="caps"&gt;HTTP&lt;/span&gt; front-end, even where you normally wouldn&amp;#8217;t have it (i.e. Sourceforge and Rubyforge)&lt;/li&gt;
		&lt;li&gt;If you don&amp;#8217;t have a feature in Mercurial, find or code a Mercurial extension&amp;#8212;it is fully customisable&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h2&gt;My Forceful Conclusion&lt;/h2&gt;


	&lt;p&gt;Like the language you are using (Ruby 1.8), the framework (Rails 1.2), the testing suite (RSpec 1.0 and Mocha), the operating system (Mac &lt;span class="caps"&gt;OS X 10&lt;/span&gt;.4 and Linux 2.6), the web server (nginx, LiteSpeed, or lighttpd), the application server (LiteSpeed or Mongrel), the blogging software (Typo or Mephisto), even the hardware (Amazon S3/EC2 or many dual core 2+ GHz servers with many GBs of &lt;span class="caps"&gt;RAM&lt;/span&gt;), you need to use the latest version control software (Mercurial 0.9.4 or Git 1.5). If you don&amp;#8217;t, it&amp;#8217;s like using an old server with 333MHz &amp;#38; 256MB &lt;span class="caps"&gt;RAM&lt;/span&gt;, or Apache 1.3 &amp;#38; mod_ruby. But unlike an out-moded application server, &lt;span class="caps"&gt;SVN&lt;/span&gt; &lt;strong&gt;will&lt;/strong&gt; make you a slower developer than the one sitting next to you using Mercurial, Git, Darcs or Bazaar. Oh, and I needn&amp;#8217;t mention that he&amp;#8217;ll get all the babes too.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=rQZDDnsWCmA:gGzEMv2ojXg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=rQZDDnsWCmA:gGzEMv2ojXg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=rQZDDnsWCmA:gGzEMv2ojXg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=rQZDDnsWCmA:gGzEMv2ojXg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=rQZDDnsWCmA:gGzEMv2ojXg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=rQZDDnsWCmA:gGzEMv2ojXg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/08/05/distributed-scms-be-smart-use-the-bleeding-edge</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:7bb78938-06cc-409a-8ded-de56c65a861e</id>
    <published>2007-07-30T20:11:00-05:00</published>
    <updated>2007-07-30T20:38:14-05:00</updated>
    <title type="html">Positive Reasons for Typo</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/rjSt_8YASS4/positive-reasons-for-typo" rel="alternate" type="text/html" />
    <category term="announcements" scheme="http://www.mathewabonyi.com/articles/category/announcements" label="Announcements" />
    
  <content type="html">&lt;p&gt;I highly respect quick responses to a post. In &lt;a href="http://blog.simb.net/articles/2007/07/30/reasons-why-i-choose-typo"&gt;Simeon&amp;#8217;s post&lt;/a&gt;, he laments my lack of a positive reasoning for choosing Typo, so I&amp;#8217;d like to quickly outline why I originally selected Typo and why I was drawn back to it.&lt;/p&gt;


	&lt;p&gt;Typo leverages the modern technologies that are out there and, true to a Rails application, has a love of shiny things without overdoing it. Nevertheless, it is fast (70 req/sec is my entire readership in a second, which makes me feel very warm and cuddly). Despite the speed, it&amp;#8217;s not stripped down at all. What it comes with built-in (every social networking/bookmarking sidebar imaginable, multiple feed formats, multiple database support) is easily supplemented by its easy-to-use plugin architecture. It now uses the Rails plugin architecture, which makes it even easier. Adding themes is a doddle. Even editing the core code gives you the result you want, more often than not.&lt;/p&gt;


	&lt;p&gt;Basically, Typo is written to offer complex features using the simplest code. There are areas you could quibble with me on that score, and I may agree, but generally Typo is a sleek and sexy blogging engine with lots of features and yet which still has limitless potential. Just look at their &lt;a href="http://trac.typosphere.org/report/3"&gt;Trac&lt;/a&gt;. The active development suggests a project which is going somewhere and getting there fast. I stopped using Typo after 4.0 and came back at 4.1. Already I see a lot of changes under the hood that would suggest more than just a minor version change. Even with all these changes, reverting back to Typo took me no less than 10 minutes. That says a lot to me.&lt;/p&gt;


	&lt;p&gt;Sure, WordPress and other blogging engines may have plugins or built-in features which rival those in Typo, but the sheer speed and flexibility of development behind Typo is peerless. I like that. It feels edgy.&lt;/p&gt;


	&lt;p&gt;I&amp;#8217;m no old man and I don&amp;#8217;t need Zimmerframe 2.2.1. Give me the bloody nose brawls, the midday groggy writer&amp;#8217;s block, the street-vomiting hard nights, the raving ecstasy of Typo any day. She&amp;#8217;s the kind of girl you can take out for a walk in the park or on a bruiser&amp;#8217;s night and return home proud and glowingly post coital, because she&amp;#8217;s always good fun, day or night.&lt;/p&gt;


	&lt;p&gt;Typo&amp;#8217;s no dumb blonde.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=rjSt_8YASS4:Rwu4nlFKuts:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=rjSt_8YASS4:Rwu4nlFKuts:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=rjSt_8YASS4:Rwu4nlFKuts:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=rjSt_8YASS4:Rwu4nlFKuts:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=rjSt_8YASS4:Rwu4nlFKuts:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=rjSt_8YASS4:Rwu4nlFKuts:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/07/30/positive-reasons-for-typo</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:6f52be1a-68e6-4f85-8869-8b558a44f84d</id>
    <published>2007-07-30T12:02:00-05:00</published>
    <updated>2007-07-30T19:35:53-05:00</updated>
    <title type="html">Seriously Sick of WordPress -- Back to Typos</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/dG-VPy-98L8/seriously-sick-of-wordpress-back-to-typos" rel="alternate" type="text/html" />
    <category term="announcements" scheme="http://www.mathewabonyi.com/articles/category/announcements" label="Announcements" />
    
  <content type="html">&lt;p&gt;I should have listened to myself ages ago when I discovered Ruby and said to myself: &amp;#8220;I hated &lt;span class="caps"&gt;PHP&lt;/span&gt; before there was an alternative. I hate &lt;span class="caps"&gt;PHP&lt;/span&gt; even more now that there is one.&amp;#8221; So why did I convert from Typo to WordPress? I really, &lt;strong&gt;really&lt;/strong&gt; do not understand why there are more posts about converting from Typo to Wordpress than vice versa.&lt;/p&gt;


	&lt;p&gt;My dear peers, pelt me with rancid tomatoes. I don&amp;#8217;t know what I was thinking when converting to a &lt;span class="caps"&gt;PHP&lt;/span&gt; monstrosity. WordPress is such a bloated and badly coded blog engine (and that is much due to the way &lt;span class="caps"&gt;PHP&lt;/span&gt; itself encourages you to code). Every time I try to dig into its code, I become nauseous and frustrated, despite years of &lt;span class="caps"&gt;PHP&lt;/span&gt; experience.&lt;/p&gt;


	&lt;p&gt;What brought me to this realisation is an innocent attempt to upgrade&amp;#8230; the quality of a software distribution and its development practices always reveal themselves when you upgrade, because it is in the care and attention to backwards compatibility which show developers have really considered what they are doing, not just chasing the new shiny thing.&lt;/p&gt;


	&lt;p&gt;So in trying to upgrade from WordPress 2.0 to 2.2, I found my entire day wasted chasing down errors, discovering none of my plugins will work, finding my theme broken in numerous places, and just plain frustrated by the idiotic process of removing, moving, copying, overwriting and double-checking files and directories. Upgrading WordPress is the most painful form of manual labour I&amp;#8217;ve experienced in a while.&lt;/p&gt;


	&lt;p&gt;So, without further ado, my list of complaints before saying &lt;em&gt;Adieu!&lt;/em&gt; to WordPress:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Biggest of all: upgrading is a serious pain in the arse, even with my upgrade.rb script (notice the irony)&lt;/li&gt;
		&lt;li&gt;Upgrading breaks almost every plugin you use&lt;/li&gt;
		&lt;li&gt;Plugin compatibility is atrocious. There may be a lot of them in their plugin repository, but look at the fine print and you&amp;#8217;ll see they range from pre-1.x to 2.2 compatibility, and that doesn&amp;#8217;t mean 2.2 works with 1.5 or vice versa. You need the right version or, more likely than not, it won&amp;#8217;t even be recognised. So, really, each version has more like 30 plugins available, not 3000.&lt;/li&gt;
		&lt;li&gt;With every upgrade, you are welcoming security bugs (the worst kind) and I just don&amp;#8217;t like the idea that upgrading to fix one is just introducing a new one. The reason for this is that WordPress is not a framework; its core functionality is part of its higher functionality, and with every upgrade they are mucking with both. I&amp;#8217;d suggest to the WordPress development team that they extract the underlying &lt;span class="caps"&gt;HTTP&lt;/span&gt; management and the overlying &lt;span class="caps"&gt;CMS&lt;/span&gt; functionality&amp;#8230; but then, why not use something like Django, CakePHP or Rails?&lt;/li&gt;
		&lt;li&gt;Their plugin architecture is horrendously 90&amp;#8217;s&lt;/li&gt;
		&lt;li&gt;The admin interface is hideous and unintuitive&amp;#8212;where do I set pings and trackbacks again? Why do plugins enable new menus in weird places? I can never remember where anything is because nothing is organised properly&lt;/li&gt;
		&lt;li&gt;Creating and editing posts is so slow I find myself making a 10-cup cafetiere every time.&lt;/li&gt;
		&lt;li&gt;Does WordPress send pings? I never see my blog appear after a post. I can only assume it doesn&amp;#8217;t, even when I tell it to.&lt;/li&gt;
		&lt;li&gt;The WordPress helper functions are megolithic and cryptic. I can&amp;#8217;t cuss them more, really. And they don&amp;#8217;t even cover every situation. I&amp;#8217;d rather have thousands of little ones to learn that three which do vastly different things, none of which do what I want. That means editing themes is a serious pain.&lt;/li&gt;
		&lt;li&gt;Lastly, I just despise &lt;span class="caps"&gt;PHP&lt;/span&gt; and giving it support by using it.&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;But this isn&amp;#8217;t just about me whinging. I have positive reasons for moving back to Typo:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;It&amp;#8217;s written in Rails, which lets me do whatever I want with it, in a pleasurable way. I know Rails well enough to muck around. All I need to do is learn how Typo works, and it isn&amp;#8217;t that complicated&amp;#8212;nothing written in Rails, that I&amp;#8217;ve encountered, is too complicated to grok in a day or two.&lt;/li&gt;
		&lt;li&gt;Typo 4.2 is going to introduce multiple blogs within a single installation, which I really would like to have. So I&amp;#8217;m in for the long haul.&lt;/li&gt;
		&lt;li&gt;The latest Typo (4.1) uses Rails 1.2, which was one of my concerns with 4.0.&lt;/li&gt;
		&lt;li&gt;The rumour of an admin interface redesign is rather thrilling, but then, I still really like Typo&amp;#8217;s admin interface as it stands. I&amp;#8217;m very curious what they&amp;#8217;re going to do.&lt;/li&gt;
		&lt;li&gt;Typo now uses Rails plugins to do everything. I intend to write a FeedBurner plugin that lets you do all sorts of things, like FeedFlare, Feedburner redirects and other nifty FeedBurner stuff. Now that it is easy to write a plugin, I&amp;#8217;m keen to test it out and prove its worth.&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;So, back to Typo 4.x. Let&amp;#8217;s hope its developers sorted out some of the issues that put me off it and caused me to so whimsically (and stupidly) change to WordPress.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Over the next week, my dear readers, you may encounter a few problems. These are only temporary as I muck around with Typo 4.1 and juice it for all its worth.&lt;/p&gt;


	&lt;p&gt;Oh, and thank you for waiting the few months while I was deeply involved with a major project. I&amp;#8217;m back permanently now, on freelance. It&amp;#8217;s good to be free and back on the Rails.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=dG-VPy-98L8:XEmxWORmkqE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=dG-VPy-98L8:XEmxWORmkqE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=dG-VPy-98L8:XEmxWORmkqE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=dG-VPy-98L8:XEmxWORmkqE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=dG-VPy-98L8:XEmxWORmkqE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=dG-VPy-98L8:XEmxWORmkqE:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/07/30/seriously-sick-of-wordpress-back-to-typos</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:efaf7c2a-9129-470f-baec-73d2f76358c3</id>
    <published>2007-07-27T21:18:35-05:00</published>
    <updated>2007-07-27T21:18:39-05:00</updated>
    <title type="html">Project Management and Best Practices in Retrospect-iva</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/QTLhzVwrYfE/project-management-and-best-practices-in-retrospect-iva" rel="alternate" type="text/html" />
    <category term="retrospectiva" scheme="http://www.mathewabonyi.com/articles/category/retrospectiva" label="Retrospectiva" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    
  <content type="html">&lt;p&gt;I don&amp;#8217;t hear a lot about project management, even though there&amp;#8217;s a lot about how to manage a project. Testing, deployment and source code management get the most attention, and project management seems to get the least. Maybe that&amp;#8217;s because it hasn&amp;#8217;t been done properly yet and all the solutions out there only address pieces of the overall problem.&lt;/p&gt;


	&lt;p&gt;So I&amp;#8217;m going to try to clarify, for myself mostly, the kind of project management that is needed and why it is so important in development.&lt;/p&gt;


	&lt;h3&gt;Getting It Right&lt;/h3&gt;


	&lt;p&gt;When I say &lt;em&gt;management&lt;/em&gt;, I mean a combination of something like Lighthouse and Basecamp, with a serious overhaul of perpsective. An integral part of good development is developing ideas hand-in-hand with the code. Management is all about keeping this communication as agile as the coding process, sticking to priorities, and addressing the right things at the right time.&lt;/p&gt;


	&lt;p&gt;Is Basecamp sufficient for managing a project? No. &lt;span class="caps"&gt;DHH&lt;/span&gt; even says it is not meant for managing Rails projects; it&amp;#8217;s for marketers and managers. It&amp;#8217;s only a piece of the puzzle, because it provides no way to easily track code.&lt;/p&gt;


	&lt;p&gt;Is Trac sufficient for managing a project? No. It is too much like a big todo list and a bug tracker combined. It is very developer-centric&amp;#8212;even when the developer is also the designer and manager, there&amp;#8217;s no way to make known the other roles. Trac too is only a piece, because it provides no way to easily communicate ideas. A wiki doesn&amp;#8217;t cut it.&lt;/p&gt;


	&lt;p&gt;Are help desks sufficient? No. They are too customer/support-centric. They have no way to easily communicate the ideas of developers and designers.&lt;/p&gt;


	&lt;p&gt;What about &lt;a href="http://www.lighthouseapp.com/"&gt;Lighthouse&lt;/a&gt; or &lt;a href="http://www.unfuddle.com/"&gt;Unfuddle&lt;/a&gt;? For a hosted solution, Lighthouse and Unfuddle combine Trac and Basecamp. That&amp;#8217;s going in a decent direction. Anything which integrates different parts of the development process is addressing the need for management. But it&amp;#8217;s not enough, because it has no integration with the customer.&lt;/p&gt;


	&lt;p&gt;What about [insert megolithic answer to everything]? No. It&amp;#8217;s too complicated, has too many options and forms, too much information on each page. Something as complicated as Google Analytics, for example, is pushing the boundary of what is acceptable. Complicated applications get in the way of communication and understanding, even if they integrate everything. Simplicity first.&lt;/p&gt;


	&lt;p&gt;What all of these solutions lack is a focus on the different kinds of users for a project, ways of easily communicating their needs and ways of addressing those needs. Even when the developer, designer, marketer and manager are rolled into the same person, it is important to separate the roles, make them clear and integrate each one&amp;#8217;s concerns at the right points.&lt;/p&gt;


	&lt;p&gt;It&amp;#8217;s all about the development process.&lt;/p&gt;


	&lt;h3&gt;The Development Process&lt;/h3&gt;


	&lt;p&gt;I see that there are 9 stages in the development process:&lt;/p&gt;


	&lt;ol&gt;
	&lt;li&gt;&lt;strong&gt;Management&lt;/strong&gt;: find out the next need to address&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Specification/Testing&lt;/strong&gt;: specify how the need is addressed&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Coding&lt;/strong&gt;: code until the specification passes&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Continuous Integration&lt;/strong&gt;: combine the efforts of multiple coders&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Refactoring&lt;/strong&gt;: clean up the code&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Graphic Design&lt;/strong&gt; (if needed): make the new feature appealing&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Deployment&lt;/strong&gt;: release the latest revision&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Marketing&lt;/strong&gt;: advertise the latest feature&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Customer Feedback&lt;/strong&gt;: find out what is going well and what isn&amp;#8217;t&lt;/li&gt;
	&lt;/ol&gt;


	&lt;p&gt;If you see this process as &lt;strong&gt;organic&lt;/strong&gt;, the importance of management becomes much clearer. In fact, I think management is the most important stage, more important than the code itself, for a number of reasons:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Management is the first step; without management, testing/coding is arbitrary&lt;/li&gt;
		&lt;li&gt;Management brings everyone into the development process&lt;/li&gt;
		&lt;li&gt;Management gives everyone an overview so they can &lt;em&gt;see the wood for the trees&lt;/em&gt;&lt;/li&gt;
		&lt;li&gt;Management encourages communication between everyone&lt;/li&gt;
		&lt;li&gt;Management naturally focuses on the most important aspects&lt;/li&gt;
		&lt;li&gt;Management reinforces and rewards good development&lt;/li&gt;
		&lt;li&gt;Management operates organically, reflecting needs and their importance&lt;/li&gt;
		&lt;li&gt;Management begins the specification/coding cycle&lt;/li&gt;
		&lt;li&gt;Management draws from and feeds into all other stages of development&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;Disconnecting from these aspects of development is a serious mistake because it denies the organic element of development. Everything needs to converge at some point and management is the most natural way of acknowledging and converging all of a project&amp;#8217;s members, roles, ideas, problems and concerns.&lt;/p&gt;


	&lt;p&gt;Poor management will try to force the development process into a linear pattern. It will approach things as &amp;#8216;things to do&amp;#8217;, &amp;#8216;features to have&amp;#8217;, &amp;#8216;milestones to reach&amp;#8217;, &amp;#8216;deadlines to meet&amp;#8217;, &amp;#8216;code to test&amp;#8217;. Everything will have its place, need to be addressed by a particular person&amp;#8230; in short, it&amp;#8217;ll look like a Trac installation. The code will feel strained, regimented and will generally be a rather boring thing to deal with. The developer is being strait-jacketed.&lt;/p&gt;


	&lt;p&gt;On the other hand, a lack of management will result in the process becoming chaotic. The developer will code whatever takes his fancy. Occasionally e-mails or posts containing feedback will find their way into the code, but mostly the code will diverge from the customer&amp;#8217;s interests. The organic element has gone mad in this case because the developer is too isolated.&lt;/p&gt;


	&lt;p&gt;But good management acknowledges the organic aspect of development and lets the code flow. It translates ideas into specifications just as test-first translates specifications into code. Good project management will create and maintain strong channels of communication between developer, designer, marketer, manager and customer. The real needs for the project will appear of their own accord as different ideas converge in one place.&lt;/p&gt;


	&lt;p&gt;But that sounds much easier than it actually is. There isn&amp;#8217;t yet an application out there which integrates all of those roles together, but some are closer than others. I think &lt;a href="http://retrospectiva.org/"&gt;Retrospectiva&lt;/a&gt; could be the one which gets there first.&lt;/p&gt;


	&lt;h3&gt;What is needed for good management&lt;/h3&gt;


	&lt;p&gt;The three major aspects of project management are development, collaboration and integration. There needs to be a way to develop, track that development, and focus it. There needs to be collaboration and communication surrounding that development. There needs to be a process of integration between management and the other stages of development.&lt;/p&gt;


	&lt;p&gt;Ideally, a project management system will have the following aspects:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Stories&lt;/strong&gt;: isolated stories to be resolved (bug, feature, question, idea)&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Dynamic properties&lt;/strong&gt;: status, milestone, persona, feature, assigned user, assigned group&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Reinforcement&lt;/strong&gt;: aspects of the application &lt;span class="caps"&gt;NOT&lt;/span&gt; to change (positive feedback, robust code)&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Personae&lt;/strong&gt;: ability to define personae, like power users, buyers, sellers, novices, etc.&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Milestones&lt;/strong&gt;: rough organisation of stories and deadlines&lt;/li&gt;
	&lt;/ul&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Messages&lt;/strong&gt;: site-wide (like Basecamp) and for each milestone&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Roles&lt;/strong&gt;: developer, guest, manager, designer, customer, marketer, administrator&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Groups&lt;/strong&gt;: optional story development by groups&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Interfaces&lt;/strong&gt;: different interfaces for each role and/or group&lt;/li&gt;
	&lt;/ul&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Cross-referencing&lt;/strong&gt;: referencing between stories, messages, milestones, source&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Testing integration&lt;/strong&gt;: update stories with progress on tests (e.g. Tesly)&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Coverage Integration&lt;/strong&gt;: stories for area of test coverage, whether 100% covered or not&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;&lt;span class="caps"&gt;SCM&lt;/span&gt; integration&lt;/strong&gt;: update stories through commit logs&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Continuous Build Integration&lt;/strong&gt;: create stories for failed builds&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Error Notification Integration&lt;/strong&gt;: create stories for application errors&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Customer Integration&lt;/strong&gt;: create stories for customer feedback (positive &amp;#38; negative)&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;Most importantly, the interface(s) needs to be extremely clean. Lighthouse goes a long way in making a highly readable, even pleasurable interface. Most of the aspects I list above can exist on their own, meaning the application itself will have many facets, but each very easily understood. Cross-referencing is probably the most important of them all, since it will bring together the various aspects.&lt;/p&gt;


	&lt;p&gt;The shift in perspective I suggest for project management is to not focus on managing people (like Basecamp) or code (like Trac) or users, but ideas. Those ideas will never disappear from view, unlike tickets on Trac or todo lists on Basecamp. As stories grow and connect with new stories (like associating tickets), everyone will see the evolution and development of features, in which way the project is going, and be able to react better to the movement of the project. In a way, project management also begins to document the project, but more importantly, it shows in black and white how ideas become code and how they evolve. At the centre of the project should be a cloud of ideas which each role can see differently.&lt;/p&gt;


	&lt;p&gt;Just to give a little hypothetical situation: at the beginning of your project you had a simple user authentication system. Over time, users talked about adding Open ID. Management wanted an authorisation system and an admin interface. Designers wanted a cute widget that pops down with &lt;span class="caps"&gt;AJAX&lt;/span&gt;. Developers wanted to extract it into a plugin. All of these ideas would be associated and appear together in a good management system, showing the time each one was added and completed, the role which initially suggested it, and the group or user responsible for implementing it. All the bugs, notes, support questions, requests and feature stories will clump together and naturally point towards what is needed next, if anything.&lt;/p&gt;


	&lt;p&gt;This form of project management could very well revolutionise the way development is perceived. Or is it a load of bullshit?&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=QTLhzVwrYfE:spNrmhDVeZA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=QTLhzVwrYfE:spNrmhDVeZA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=QTLhzVwrYfE:spNrmhDVeZA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=QTLhzVwrYfE:spNrmhDVeZA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=QTLhzVwrYfE:spNrmhDVeZA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=QTLhzVwrYfE:spNrmhDVeZA:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/07/27/project-management-and-best-practices-in-retrospect-iva</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:339979fa-8efa-41cc-a69a-c16c1600e4c5</id>
    <published>2007-07-27T09:02:09-05:00</published>
    <updated>2007-07-27T17:33:49-05:00</updated>
    <title type="html">Capistrano 2.0, upgrading &amp;amp; fitting into a size 0 dress</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/iRK8rmoQGro/capistrano-20-first-impressions-upgrading-from-141" rel="alternate" type="text/html" />
    <category term="capistrano" scheme="http://www.mathewabonyi.com/articles/category/capistrano" label="Capistrano" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    
  <content type="html">&lt;p&gt;The improvements to Capistrano are much welcomed. My deployment recipe is now half the length it used to be and it is much easier to follow what is happening for my many types of deployment. I love the new features added, mostly dealing with manipulating scopes and enhancing the user&amp;#8217;s ability to extend the core framework.&lt;/p&gt;


	&lt;h3&gt;Review of new features&lt;/h3&gt;


	&lt;p&gt;&lt;strong&gt;namespaces&lt;/strong&gt;: Like Rake, you can namespace your tasks and group them together more sensibly. This feature alone is worth upgrading for just to make your scripts more sensible and easier to read.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;events&lt;/strong&gt;: Like Rails, you can now perform tasks &lt;ins&gt;before&lt;/ins&gt; or &lt;ins&gt;after&lt;/ins&gt; other ones rather than using the hacky &amp;#8216;before_something&amp;#8217; and &amp;#8216;after_something&amp;#8217;. Much cleaner and much faster too.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;strategies&lt;/strong&gt;: In addition to checkout, you can now deploy via &lt;ins&gt;export&lt;/ins&gt; and &lt;ins&gt;copy&lt;/ins&gt; and use different strategies for deployment, such as using export for your &lt;ins&gt;copy_strategy&lt;/ins&gt; rather than zips and tarballs.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;scoping&lt;/strong&gt;: All sorts of scoping has been introduced in Capistrano 2.0, from namespacing to single execution of &amp;#8220;run&amp;#8221; and &amp;#8220;sudo&amp;#8221;, allowing you to define specific roles or hosts in which your commands run.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;help&lt;/strong&gt;: Capistrano 2.0 now has a more verbose way of explaining tasks with &lt;ins&gt;cap -e task_name&lt;/ins&gt;. You&amp;#8217;ll realise how useful this is when you use it for the built-ins as well as your own.&lt;/p&gt;


	&lt;p&gt;All in all, Capistrano is pretty simple, but it is the way it is written that makes it appear so much simpler than it really is. Capistrano 2.0 takes that to a new level, not groundbreaking perhaps, but definitely a lot cleaner than its previous releases.&lt;/p&gt;


	&lt;h3&gt;Upgrading from 1.4.1&lt;/h3&gt;


	&lt;p&gt;&lt;strong&gt;There is no need to change config/deploy.rb out of the box.&lt;/strong&gt; Capistrano 2.0 is nicely backwards compatible, unlike other things out there, and, at least for me, nothing broke because of the upgrade.&lt;/p&gt;


	&lt;p&gt;You can look at &lt;a href="http://capify.org/upgrade"&gt;Capistrano&amp;#8217;s instructions for upgrading&lt;/a&gt;, if you want to know what is being done, but for the impatient, here are the steps you have to follow before we can start drying up your deploy script.&lt;/p&gt;


1. Install the new version of capistrano: 
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;sudo&lt;/span&gt; &lt;span class="ident"&gt;gem&lt;/span&gt; &lt;span class="ident"&gt;install&lt;/span&gt; &lt;span class="ident"&gt;capistrano&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

2. cd project_root &amp;#38; run capify
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;~# cd projroot
projroot# capify .&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

3. Upgrade previous deployments to use the new revision tracking system
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;projroot# cap -f upgrade -f Capfile upgrade:revisions&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;4. Rinse and repeat for each of your deployment targets&lt;/p&gt;


	&lt;h3&gt;Getting your deploy.rb into its new size 0 dress&lt;/h3&gt;


	&lt;p&gt;You may now have the very understandable urge to slim down your deployment recipes. With the introduction of Capistrano 2.0, I found my deploy.rb reduced to less than half the size. Below, I cover the areas which you should focus on to get that deploy script into its new size 0 dress.&lt;/p&gt;


	&lt;h4&gt;Anatomy of my deploy.rb&lt;/h4&gt;


	&lt;ol&gt;
	&lt;li&gt;requires: capistrano-ext, mongrel_cluster, etc.&lt;/li&gt;
		&lt;li&gt;global, stage and custom variables&lt;/li&gt;
		&lt;li&gt;event chains&lt;/li&gt;
		&lt;li&gt;rewriting built-ins: web:disable and web:enable&lt;/li&gt;
		&lt;li&gt;extra tasks: fixing permissions, copying mongrel confs, etc.&lt;/li&gt;
		&lt;li&gt;custom deploy tasks: long, normal, quick&lt;/li&gt;
		&lt;li&gt;maintenance tasks: backup, restore&lt;/li&gt;
	&lt;/ol&gt;


	&lt;h4&gt;Variables&lt;/h4&gt;


	&lt;p&gt;More than before, variables are the lynch-pin of slimming everything down. The first thing you should do is look over every task rewrite or custom task and see how it can be turned into a simple &lt;ins&gt;set :var, true/false/whatever&lt;/ins&gt;. Capistrano 2.0 will make it very easy to do this.&lt;/p&gt;


	&lt;p&gt;With Capistrano 2.0, you should use the &lt;ins&gt;set&lt;/ins&gt; command religiously, both for built-in and custom tasks.&lt;/p&gt;


	&lt;p&gt;I personally set the following at the top of my recipe.&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Global variables: &lt;ins&gt;stages&lt;/ins&gt;, &lt;ins&gt;deploy_via&lt;/ins&gt;&lt;/li&gt;
		&lt;li&gt;Application specific: &lt;ins&gt;application&lt;/ins&gt;, &lt;ins&gt;repository&lt;/ins&gt;, &lt;ins&gt;user&lt;/ins&gt;, &lt;ins&gt;scm_username&lt;/ins&gt;&lt;/li&gt;
		&lt;li&gt;Deployment specific: &lt;ins&gt;deploy_to&lt;/ins&gt;, &lt;ins&gt;rails_env&lt;/ins&gt;&lt;/li&gt;
		&lt;li&gt;Custom variables: &lt;ins&gt;serving_via&lt;/ins&gt;, &lt;ins&gt;suexec&lt;/ins&gt;, &lt;ins&gt;suexec_user&lt;/ins&gt;, &lt;ins&gt;suexec_group&lt;/ins&gt;, &lt;ins&gt;disable_template&lt;/ins&gt;&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h4&gt;Deployment Strategy&lt;/h4&gt;


	&lt;p&gt;I would personally suggest using &lt;ins cite="e"&gt;xport&lt;/ins&gt; for your &lt;ins&gt;deploy_via&lt;/ins&gt; strategy unless you have a reason for using &lt;ins cite="c"&gt;heckout&lt;/ins&gt; or &lt;ins&gt;copy&lt;/ins&gt;.&lt;/p&gt;


	&lt;h4&gt;Using Namespaces&lt;/h4&gt;


	&lt;p&gt;Namespaces make it dead simple to group common tasks, like different restart methodologies. I use a &lt;ins&gt;serving_via&lt;/ins&gt; variable which translates into the reload:whatever task to run for restarting the application. For example:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;namespace&lt;/span&gt; &lt;span class="symbol"&gt;:reload&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;desc&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Default reloading procedure&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:default&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;mongrels&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;desc&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Reload an FCGI application&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:fcgi&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:roles&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:app&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;sudo&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{current_path}&lt;/span&gt;/script/process/reaper -a graceful -d &lt;span class="expr"&gt;#{current_path}&lt;/span&gt;/public/dispatch.fcgi&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;desc&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Reload an LSAPI application&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:lsapi&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:roles&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:app&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;sudo&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/usr/local/litespeed/bin/lswsctrl restart&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;desc&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Give the mongrels a bath&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:mongrels&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:roles&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:app&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;restart_mongrel_cluster&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I warn against using &lt;ins&gt;restart&lt;/ins&gt; as a namespace because it clashes with the built-in task and, in certain instances, results in infinite recursion.&lt;/p&gt;


	&lt;h4&gt;Maintenance Splash&lt;/h4&gt;


	&lt;p&gt;The biggest change in Capistrano you may need to worry about is the removal of &lt;ins&gt;delete&lt;/ins&gt; and &lt;ins&gt;render&lt;/ins&gt;. Don&amp;#8217;t despair, though, because creating a maintenance splash is still easy. This is my rewrite:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;desc&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Generate a maintenance.html to disable requests to the application.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="ident"&gt;deploy&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;web&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:disable&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:roles&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:web&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;remote_path&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{shared_path}&lt;/span&gt;/system/maintenance.html&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;on_rollback&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;run&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;rm &lt;span class="expr"&gt;#{remote_path}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
  &lt;span class="ident"&gt;template&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;read&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;disable_template&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;deadline&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;reason&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;ENV&lt;/span&gt;&lt;span class="punct"&gt;[&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;UNTIL&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt; &lt;span class="constant"&gt;ENV&lt;/span&gt;&lt;span class="punct"&gt;[&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;REASON&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;]&lt;/span&gt;
  &lt;span class="ident"&gt;maintenance&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;ERB&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;template&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;result&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;binding&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;put&lt;/span&gt; &lt;span class="ident"&gt;maintenance&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{remote_path}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="symbol"&gt;:mode&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;0644&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;desc&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Re-enable the web server by deleting any maintenance file.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="ident"&gt;deploy&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;web&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:enable&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:roles&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:web&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;run&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;rm &lt;span class="expr"&gt;#{shared_path}&lt;/span&gt;/system/maintenance.html&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h4&gt;Using events&lt;/h4&gt;


	&lt;p&gt;Like the before and after filters in Rails, you can now cleanly chain together tasks. I&amp;#8217;m a sucker for one-line solutions and these are really so simple that it makes my heart bleed:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;before&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;deploy:restart&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;fix:permissions&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="ident"&gt;before&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;deploy:migrate&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;db:backup&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="ident"&gt;after&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;deploy:symlink&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;deploy:cleanup&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="ident"&gt;after&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;deploy:update_code&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;deploy:web:disable&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="ident"&gt;after&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;deploy:restart&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;deploy:web:enable&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h4&gt;capistrano-ext &amp;#38; multistage&lt;/h4&gt;


	&lt;p&gt;I highly recommend the use of multistage. It comes with the capistrano-ext gem (which has been upgraded to Capistrano 2.0, of course).&lt;/p&gt;


	&lt;p&gt;Basically, it separates the concerns of different deployments. If, like me, you like having a few other versions of your application out there, like a staging area, a testing area for bleeding edge features, and, of course, the production site, separating these in Capistrano before 2.0 was very irritating. Multistage sorts that out very nicely.&lt;/p&gt;


	&lt;p&gt;By default, you must specify the stage you wish to deploy. This behaviour can be overridden by setting the &lt;ins&gt;default_stage&lt;/ins&gt; variable, but I like being explicit. This is what using stages looks like:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;# cap production deploy&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;If you don&amp;#8217;t provide &amp;#8216;production&amp;#8217;, it&amp;#8217;ll complain and abort.&lt;/p&gt;


	&lt;p&gt;Using multistage is dead easy. Put this at the top of your deploy.rb:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;  require 'capistrano/ext/multistage'
  set :stages, %w(staging production testing)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Run the task for generating your stage deploy files:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;projroot# cap multistage:prepare&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;This will create a recipe file for each stage in a new config/deploy directory (exactly like Rails environments). Now, in each stage recipe, add all of your stage-specific tasks and variables. For example:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;set&lt;/span&gt; &lt;span class="symbol"&gt;:rails_env&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;stage&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="ident"&gt;set&lt;/span&gt; &lt;span class="symbol"&gt;:application&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;staging.example.com&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="ident"&gt;set&lt;/span&gt; &lt;span class="symbol"&gt;:deploy_to&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/var/www/&lt;span class="expr"&gt;#{application}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Now switching between different deployments is a breeze. Just make a new recipe file for it with the necessary variables and you&amp;#8217;re set.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=iRK8rmoQGro:NMjubtEvzRk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=iRK8rmoQGro:NMjubtEvzRk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=iRK8rmoQGro:NMjubtEvzRk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=iRK8rmoQGro:NMjubtEvzRk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=iRK8rmoQGro:NMjubtEvzRk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=iRK8rmoQGro:NMjubtEvzRk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/07/27/capistrano-20-first-impressions-upgrading-from-141</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:a96979b1-ad0f-432b-8ff7-5844ba630ddb</id>
    <published>2007-03-28T22:13:06-05:00</published>
    <updated>2007-03-28T22:23:53-05:00</updated>
    <title type="html">Kathy and Chris sitting in a tree...</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/EqGsIoHl4c4/kathy-and-chris-sitting-in-a-tree" rel="alternate" type="text/html" />
    <category term="politics" scheme="http://www.mathewabonyi.com/articles/category/politics" label="Politics" />
    
  <content type="html">&lt;p&gt;Really hot stuff this Kathy Sierra nonsense. Oh wait, a tasteless pun. Is everyone going to hate me now? Grow up!&lt;/p&gt;


	&lt;p&gt;I usually don&amp;#8217;t wade into political debate because it is full of morons, but it&amp;#8217;s the middle of the night and I&amp;#8217;m really bored. In fact, annoyed. Like I was with Rails 1.2 (how&amp;#8217;s that for continuity!)&lt;/p&gt;


	&lt;h4&gt;Who I Sympathise With and Who I Support&lt;/h4&gt;


	&lt;p&gt;I&amp;#8217;ll give, frankly, my sympathy to Kathy (who is manic, slightly weak-willed, has some good ideas, some other ideas a little dodgy, and has all the traits of a modern American woman, for good or ill) but I give my serious support to &lt;a href="http://www.scripting.com/stories/2007/03/27/chorusOfCowardice.html"&gt;those calling for uncommon sense&lt;/a&gt;. This situation is so cliched and yet has slipped passed a few people I respect that I&amp;#8217;m going to kill some time here and point out that Dave Winer, who I never heard of before, is right to be skeptical. He made just one mistake: he wasn&amp;#8217;t confident enough to spell out why others should be. Please, please stop fuelling the media. It&amp;#8217;s just evil.&lt;/p&gt;


	&lt;p&gt;So, without further ado, the train of thought:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://blog.labnotes.org/2007/03/26/wtf/"&gt;Stunned&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://headrush.typepad.com/creating_passionate_users/2007/03/as_i_type_this_.html"&gt;Scared&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://news.bbc.co.uk/2/hi/technology/6502643.stm"&gt;The Beeb: big fuck-off story about IT woman getting death threats&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://www.scripting.com/stories/2007/03/27/chorusOfCowardice.html" title="ergo, lampooned"&gt;Skeptical&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://www.rageboy.com/2007/03/re-kathy-sierras-allegations.html"&gt;Reaction&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;The gist of my view:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Threats and offensive comments on the internet are newsworthy, therefore not worth my time (unless I&amp;#8217;m killing it).&lt;/li&gt;
		&lt;li&gt;Being a high-profile speaker in the IT world will have its share of &amp;#8216;acerbic&amp;#8217; commentary&lt;/li&gt;
		&lt;li&gt;Being a blogger will get you the same&lt;/li&gt;
		&lt;li&gt;Putting your details online is just dumb&lt;/li&gt;
		&lt;li&gt;Men abuse each other using sexual language, but we&amp;#8217;ve got used to it over the last five millenia and now think it&amp;#8217;s kind of amusing&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;For example, the Bile Blog, which is an institution in slagging shit off. Thankfully it still exists. Poor Hani doesn&amp;#8217;t get a &lt;span class="caps"&gt;BBC&lt;/span&gt; article because the Bile stays strictly asexual. But in a post-feminist, emasculated society, you can get really hated for saying anything remotely gender-specific. I&amp;#8217;m sorry, but I don&amp;#8217;t buy that. It&amp;#8217;s proscriptive and &lt;del&gt;- for the same reason I think I should be able to take a pie or bottle of water on a plane even at the risk of being blown up -&lt;/del&gt; molly-coddling. Anyone with some self-respect and decency should feel a little spineless and self-indulgent if they coddle Kathy. A mild dose of feminist support is fine, because I like women to be people rather than objects, but indulging in the mass opinion is, well, like getting a labotomy. Some of the points Kathy made were, of course, right: it&amp;#8217;s pretty sick some of those things she cited. However, it&amp;#8217;s not rocket science to see she is also over-reacting and taking the majestic &amp;#8216;blogosphere&amp;#8217; with her. Her avid fans who want to hang her are most likely infantile, possibly autistic twits in the middle of nowhere, with the social graces of a louse.&lt;/p&gt;


	&lt;h4&gt;What&amp;#8217;s the truth?&lt;/h4&gt;


	&lt;p&gt;The situation is as plain as a Bulgarian pin-up. Kathy takes the net seriously and flips out. Her readers, which are many, believe her. Others who see those believe her think the readers must be right because there are so many of them. A few people are skeptical and they are lampooned, making the case even stronger that Kathy is being persecuted. It goes to the media, which means it must be true!&lt;/p&gt;


	&lt;h4&gt;Qualifications&lt;/h4&gt;


	&lt;p&gt;Please realise that there are minorities of both idiots and intelligent people in the real world just as much as on the internet, with one slight difference: &amp;#8216;law&amp;#8217; isn&amp;#8217;t firmly established online. That&amp;#8217;s quite literally fantastical, but true, and we should cherish that infinite freedom. I don&amp;#8217;t want a jackboot in my face for telling someone online they can stick their ideas up their cuntflap or hang by their genitals, nor see a person cuffed and imprisoned for behaving as amorally as Sodom. Your morals and theirs may not mix, but that&amp;#8217;s neither here nor there. Morality is for religion, not law and the internet is lawless.&lt;/p&gt;


	&lt;p&gt;All this lovely lawlessness is because of its anonymity. People use that anonymity out of cowardice (yes) but also safety. Uh huh, that&amp;#8217;s right. The louse should be protected too, &lt;strong&gt;all the way up until&lt;/strong&gt; he actually clubs Kathy over the head and brings her back to his cave. That&amp;#8217;s real free speech. It&amp;#8217;s not pleasant sometimes, but it&amp;#8217;s very, very important to realise how extreme an idea it is (the original one, not the hashed up piece of shit served by &amp;#8216;democracies&amp;#8217; around the world).&lt;/p&gt;


	&lt;h4&gt;Summary&lt;/h4&gt;


	&lt;p&gt;Kathy thinks threats are equal to actions, for which she loses my support, though I still hope she pulls out of her mire of self-doubt and back into posting for her readers. Maybe during that period she should read some comforting Spinozan ideas on epistemology and semantics.&lt;/p&gt;


	&lt;p&gt;Resorting to qualified (that is, reasoned) support and sympathy is quite different from just jumping on the wagon of lynchers, to use Chris Locke&amp;#8217;s own words.&lt;/p&gt;


	&lt;p&gt;Just as a reminder, this is &lt;a href="http://news.bbc.co.uk/2/hi/south_asia/6502305.stm"&gt;where enforcing morality leads&lt;/a&gt;. &lt;span class="caps"&gt;BBC&lt;/span&gt; are really getting lazy; they&amp;#8217;ve got two articles on mass hysteria. But they are the news and cater to the lowest common denominator. It&amp;#8217;s quite different when respected, intelligent individuals are the cause of the stir. I only hope some of you will give it a little more consideration next time and do the intelligent thing: be as long-winded as I am. Then no journalist could bear reading it.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=EqGsIoHl4c4:biYkZksHJPU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=EqGsIoHl4c4:biYkZksHJPU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=EqGsIoHl4c4:biYkZksHJPU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=EqGsIoHl4c4:biYkZksHJPU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=EqGsIoHl4c4:biYkZksHJPU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=EqGsIoHl4c4:biYkZksHJPU:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/03/28/kathy-and-chris-sitting-in-a-tree</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:b535d596-8e68-44bf-bf21-fe6cdb6e0cd9</id>
    <published>2007-02-18T12:15:27-06:00</published>
    <updated>2007-03-28T18:51:10-05:00</updated>
    <title type="html">Initial reactions to Rails 1.2... annoyed</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/PWMhg2S0EU0/initial-reactions-to-rails-12-annoyed" rel="alternate" type="text/html" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    
  <content type="html">&lt;p&gt;I&amp;#8217;ve held off my opinion about Rails 1.2 until I had a little more experience with it. First of all, I&amp;#8217;m going to say I&amp;#8217;m glad Rails has put out this release, because some of the features in Edge I&amp;#8217;ve been needing for a while and, in cases where I couldn&amp;#8217;t wait, like my simply_restful_backport, I hacked a ported version to 1.1.x. I also had unpublished ports for &lt;span class="caps"&gt;REST&lt;/span&gt;, routing, and a few deprecations to prepare myself. Most of the added features are bang on what is needed when following the Rails methodology of web development: singleton resources, nesting resources, the emphasis on &lt;span class="caps"&gt;REST&lt;/span&gt;, the respond_to(&amp;#38;block), etc.&lt;/p&gt;


	&lt;p&gt;But my optimism and preparations were in vain. I&amp;#8217;m annoyed. My initial reaction is that Rails 1.2 is a &lt;a href="http://en.wikipedia.org/wiki/Fascist"&gt;fascist&lt;/a&gt;. The front-facing libraries, &lt;code&gt;ActiveRecord&lt;/code&gt; and &lt;code&gt;ActionPack&lt;/code&gt;, are not the cause of my annoyance. It&amp;#8217;s the underbelly, &lt;code&gt;ActiveSupport&lt;/code&gt;, which has acquired a rank smell.&lt;/p&gt;


	&lt;h4&gt;How Deprecations are handled&lt;/h4&gt;


	&lt;p&gt;I&amp;#8217;ll be brief. Backward compatibility is beautiful and when you can&amp;#8217;t maintain it, you prepare developers for the transition. Deprecrations are usually the answer: you say it is going to be removed for a long time, encourage people away from it, then finally remove it in either the next major release or the one after that, safe in the knowledge that everyone who will be affected by the change has got the message.&lt;/p&gt;


	&lt;p&gt;Rails has not held to the time-honoured traditions of deprecation. It does one of three things: fill up the logs with warning messages, raise annoying exceptions that apologise something is gone, or just plain crash because the old &lt;span class="caps"&gt;API&lt;/span&gt; had changed so much. That&amp;#8217;s not deprecation. That&amp;#8217;s just &lt;span class="caps"&gt;API&lt;/span&gt; upheaval.&lt;/p&gt;


	&lt;p&gt;And it&amp;#8217;s unacceptable to have so many differences in the framework which, first, are undocumented, and second, change development so much. I would expect this behaviour from a 1.3 to 2.0 upgrade, but not between two minor releases. Maybe Rails Core should have re-numbered the release to warn users that Rails has changed quite drastically under the hood. The changes are fine, but it&amp;#8217;s the way they came unannounced that is annoying.&lt;/p&gt;


	&lt;p&gt;Especially after so much time between the two minor versions, there was ample opportunity to tell developers&amp;#8230; 1.1.6 was the last opportunity, and a better time was back at 1.1.2, when these additions already existed or were being considered. That is still too quick, but to give no warning, not even a proviso &amp;#8216;this may change&amp;#8217;, until it is too late is just insufferable.&lt;/p&gt;


	&lt;h4&gt;ActiveSupport::Dependencies &amp;lt; ActiveSupport::Devil&lt;/h4&gt;


	&lt;p&gt;For example, I don&amp;#8217;t know why an innocuous &lt;code&gt;ObjectSpace&lt;/code&gt; block in routes.rb should cause a blocking &lt;strong&gt;exception&lt;/strong&gt; from &lt;code&gt;ActiveSupport::Dependencies&lt;/code&gt;. It prevented my application from even loading and took half a day to track down. Or why I needed to alter ExceptionNotifier to require all its libs. Or track down 6 other &amp;#8216;dependencies&amp;#8217; and make them explicit&amp;#8212;I thought Rails was supposed to make this stuff easier, not more of a pain than C&amp;#8217;s &lt;code&gt;#include &amp;lt;hardtofind.h&amp;gt;&lt;/code&gt;?&lt;/p&gt;


	&lt;p&gt;It&amp;#8217;s ridiculous for an &amp;#8216;improvement&amp;#8217; release to be so fragile. Rails 1.1.x was pretty flexible on these terms. However, Rails, in its growing focus on encouraging programmers into standardised practices, is looking more like 37signals practices. And to say they are &amp;#8216;best practices&amp;#8217; or good &amp;#8216;conventions&amp;#8217; is a bit rich, because &lt;a href="http://eigenclass.org/hiki.rb?ruby-warnings-SEX-and-stds"&gt;Rails is not the cleanest set of libraries&lt;/a&gt;. Just run it with &lt;code&gt;$VERBOSE = true&lt;/code&gt; and you&amp;#8217;ll see what I mean.[1]&lt;/p&gt;


	&lt;p&gt;In fact, after using &lt;code&gt;ActiveSupport::Dependencies&lt;/code&gt; for a while, I&amp;#8217;ve come to the conclusion that it is too clever for its own good and I foresee lots of bugs in Rails Core Trac about it. It&amp;#8217;s too prying. It&amp;#8217;s too observant of everything that is happening. It infects the production environment. It&amp;#8217;s a glorified &lt;code&gt;const_set&lt;/code&gt; &lt;code&gt;trace_func&lt;/code&gt;.&lt;/p&gt;


	&lt;h4&gt;ActiveSupport.include :bloat&lt;/h4&gt;


	&lt;p&gt;What kind of overhead does &lt;code&gt;ActiveSupport::Dependencies&lt;/code&gt; have? How many orders of magnitude slower is it than the previous &lt;code&gt;Reloadable&lt;/code&gt; module, which just included a method, &lt;code&gt;reloadable?&lt;/code&gt;, and was found via &lt;code&gt;ObjectSpace&lt;/code&gt;? First of all, it didn&amp;#8217;t need a revolution like Dependencies; it was clean, simple, elegant and extendable. It didn&amp;#8217;t solve all the problems and had room for improvement, but removing it I think was a mistake. &lt;code&gt;ActiveSupport::Dependencies&lt;/code&gt;, unlike it&amp;#8217;s predecessor, is a beast. Here&amp;#8217;s a quick &lt;span class="caps"&gt;LOC&lt;/span&gt; comparison between activesupport 1.3.1 and 1.4.1 (ignoring hooks in the other gems):&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;$ grep -v '^\s*#' activesupport-1.3.1/lib/active_support/reloadable.rb | wc -l
28
$ grep -v '^\s*#' activesupport-1.3.1/lib/active_support/dependencies.rb | wc -l
172

$ grep -v '^\s*#' activesupport-1.4.1/lib/active_support/reloadable.rb | wc -l
56
$ grep -v '^\s*#' activesupport-1.4.1/lib/active_support/dependencies.rb | wc -l
538&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Not the most reliable comparison, but it gives a good idea of the &lt;strong&gt;bloat&lt;/strong&gt; introduced by, first, dependency tracking and, second, simple deprecating. Most of those lines of code in Reloadable are about how it is not just deprecated, but downright crippled and how evil it was. This is not a good practice for a developing an open source framework which is not easy to upgrade (and we live with it, because Rails is useful). The difficulty of upgrading is precisely because of these badly planned deprecations and backward incompatibilities.&lt;/p&gt;


	&lt;h4&gt;Enough ranting&lt;/h4&gt;


	&lt;p&gt;In this sense, Rails 1.2 is by the far worst gem update I&amp;#8217;ve experienced so far. How many blogs have posted about the many little things that needed to be changed to upgrade? How many forward-thinking developers looked in cringing, abject horror when their tests failed left and right after that (apparently) over-optimistic &lt;code&gt;gem update rails&lt;/code&gt;? How many people flew to &lt;span class="caps"&gt;IRC&lt;/span&gt; and the mailing lists for help? Let me be so conceited as to introduce a &amp;#8216;best practice&amp;#8217;: &lt;em&gt;discuss major changes with your users&lt;/em&gt;.&lt;/p&gt;


	&lt;p&gt;I realise that Rails is first and foremost a 37signals project, evolved from &lt;span class="caps"&gt;DHH&lt;/span&gt;&amp;#8217;s work there and is primarily altered and improved for that company&amp;#8217;s purposes, but as an open source project trying to get more users, it&amp;#8217;s important to either branch those changes which are special to that company or to be more considerate to the user base. I think Rails development would benefit from:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;a separate experimental branch (different from Edge trunk) for &lt;span class="caps"&gt;API&lt;/span&gt; changes&amp;#8212;Radiant does this quite nicely&lt;/li&gt;
		&lt;li&gt;a highly visible discussion board on rubyonrails.org to discuss major changes with a link to it in &lt;span class="caps"&gt;IRC&lt;/span&gt; and regular updates to the Rails mailing lists&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;Granted, much of my qualms with Rails 1.2 is with Dependencies and with some good old fashioned hacking, they can fix it. However, I think it and the library from which it has spawned are worth my comment. Dependencies is the most invasive addition to the framework I&amp;#8217;ve seen to-date. I appreciate the attempt at innovation, but Rails needs to stop doing this kind of widespread deprecation and fundamentalist intolerance to the unconventional.&lt;/p&gt;


	&lt;p&gt;I wish the development team would take more pleasure in the diversity of uses of Rails and less perverse pleasure in the militant drilling of young, transitional and experienced programmers. I like the framework, but I don&amp;#8217;t work (or want to work) at 37signals.&lt;/p&gt;


	&lt;p id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; I&amp;#8217;ve been aware of my own transgressions for a while. I&amp;#8217;m in complete agreement with &lt;a href="http://eigenclass.org/hiki.rb?cmd=view&amp;#38;p=ruby-warnings-SEX-and-stds"&gt;Mauricio on the topic of warnings&lt;/a&gt;. They are there for a reason: to create semantically correct Ruby. We should all be able to run our fantastically short, readable, meta-programmed, overloaded and mixin-loving code with &lt;code&gt;$VERBOSE = true&lt;/code&gt;. It isn&amp;#8217;t difficult to achieve and it helps in the long-run to make that lovely little code more readable, usable and quiet.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=PWMhg2S0EU0:aVqrEsc2NJI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=PWMhg2S0EU0:aVqrEsc2NJI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=PWMhg2S0EU0:aVqrEsc2NJI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=PWMhg2S0EU0:aVqrEsc2NJI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=PWMhg2S0EU0:aVqrEsc2NJI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=PWMhg2S0EU0:aVqrEsc2NJI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/02/18/initial-reactions-to-rails-12-annoyed</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:849362c7-daa9-461c-a463-b88f0e405482</id>
    <published>2007-02-10T15:45:43-06:00</published>
    <updated>2007-02-10T15:48:32-06:00</updated>
    <title type="html">Syspec: System Specifications &amp;amp; Tests using a Rake-like batch file</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/tASQJ99ZcoA/syspec-system-specifications-tests-using-a-rake-like-batch-file" rel="alternate" type="text/html" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="gems" scheme="http://www.mathewabonyi.com/articles/category/gems" label="Gems" />
    
  <content type="html">&lt;p&gt;Syspec lets you write a batch file a lot like a Rake file, but in common &lt;span class="caps"&gt;HTTP&lt;/span&gt;-style language, to test the existence and proper functioning of all the domains, subdomains, their pages and responses. This project is still very much in its infancy, but I decided to get it out there to get some feedback while I use it to track production servers. This is a sample Syspecfile:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;check&lt;/span&gt; &lt;span class="symbol"&gt;:&amp;quot;&lt;/span&gt;&lt;span class="symbol"&gt;www.rubyonrails.org&lt;/span&gt;&lt;span class="symbol"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;accept&lt;/span&gt; &lt;span class="number"&gt;200&lt;/span&gt;
  &lt;span class="ident"&gt;get&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;accept&lt;/span&gt; &lt;span class="number"&gt;404&lt;/span&gt;
  &lt;span class="ident"&gt;get&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/syspec&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;get&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/lacrima&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;The above will check that http://www.rubyonrails.org/ responds with 200 and http://www.rubyonrails.org/syspec and http://www.rubyonrails.org/lacrima respond with 404. You can have multiple checks with the same domain and it does not have to be a symbol, though I prefer it that way.&lt;/p&gt;


Currently, Syspec features the following:
	&lt;ul&gt;
	&lt;li&gt;accept(*codes) &amp;#8211; response code should be among these codes&lt;/li&gt;
		&lt;li&gt;get, post, delete, put (uses fetch(:request_method, url))&lt;/li&gt;
	&lt;/ul&gt;


Upcoming features:
	&lt;ul&gt;
	&lt;li&gt;login &amp;#8220;username&amp;#8221;, &amp;#8220;password&amp;#8221; &lt;/li&gt;
		&lt;li&gt;body matching&lt;/li&gt;
		&lt;li&gt;forms&lt;/li&gt;
	&lt;/ul&gt;


You can grab a copy of it directly from my &lt;span class="caps"&gt;SVN&lt;/span&gt; repository here:
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;  svn co http://mabs29.googlecode.com/svn/trunk/gems/syspec .&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

Or download the gem from within the &lt;span class="caps"&gt;SVN&lt;/span&gt; repository (later will be moved to rubyforge):
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;  svn export http://mabs29.googlecode.com/svn/trunk/gems/syspec/versions/syspec-0.2.1.gem .&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Enjoy!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=tASQJ99ZcoA:U19CUJBycso:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=tASQJ99ZcoA:U19CUJBycso:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=tASQJ99ZcoA:U19CUJBycso:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=tASQJ99ZcoA:U19CUJBycso:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=tASQJ99ZcoA:U19CUJBycso:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=tASQJ99ZcoA:U19CUJBycso:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/02/10/syspec-system-specifications-tests-using-a-rake-like-batch-file</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:22d6653a-a3ce-4bda-899d-0804f4f4660a</id>
    <published>2007-02-07T01:32:22-06:00</published>
    <updated>2007-02-07T01:32:24-06:00</updated>
    <title type="html">ANSI Strings (2nd Edition)</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/ptrXhWwejd4/ansi-strings-2nd-edition" rel="alternate" type="text/html" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    
  <content type="html">&lt;p&gt;I had a couple of instances where I was adding to the &lt;span class="caps"&gt;ANSI&lt;/span&gt;_CODES constant and creating all sorts of ugly errors. Then a script of mine whispered in my ear its desire for meta-aliases. That prompted a small rewrite of the library.&lt;/p&gt;


	&lt;p&gt;Ansi codes are now dynamically defined and more easily manipulated without overcomplicating things. Also, I added an RSpec test to the repository for my own piece of mind and to help with refactoring a few things. I don&amp;#8217;t know, should I give it a version number? Nah. Just &amp;#8216;Second Edition&amp;#8217;. I don&amp;#8217;t envisage any other serious revisions to it any more. What I changed:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;dumped the array structure for ansi codes&lt;/li&gt;
		&lt;li&gt;added a few non-colour &lt;span class="caps"&gt;ANSI&lt;/span&gt; codes&lt;/li&gt;
		&lt;li&gt;dynamic ansi code definitions: String.define_ansi :alias =&amp;gt; &amp;#8220;123m&amp;#8221; &lt;/li&gt;
		&lt;li&gt;meta-aliases: String.define_ansi :clear_both =&amp;gt; [:clear_line, :clear_screen]&lt;/li&gt;
		&lt;li&gt;String#method definitions only if it doesn&amp;#8217;t exist already&lt;/li&gt;
		&lt;li&gt;String#method definitions on the fly&lt;/li&gt;
		&lt;li&gt;spec test&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;And here&amp;#8217;s the code:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="global"&gt;$ANSI&lt;/span&gt; &lt;span class="punct"&gt;||=&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;String&lt;/span&gt;

  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;

    &lt;span class="ident"&gt;attr_accessor&lt;/span&gt; &lt;span class="symbol"&gt;:ansi_codes&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;ansi_codes&lt;/span&gt;
      &lt;span class="attribute"&gt;@ansi_codes&lt;/span&gt; &lt;span class="punct"&gt;||=&lt;/span&gt; &lt;span class="punct"&gt;{}&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;define_ansi&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;definitions&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{})&lt;/span&gt;
      &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;ansi_codes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;ansi_codes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;merge&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;definitions&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="ident"&gt;define_shortcuts&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;definitions&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;define_shortcuts&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;definitions&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{})&lt;/span&gt;
      &lt;span class="ident"&gt;definitions&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;keys&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;meth&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
        &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;instance_methods&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;include?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;meth&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
          &lt;span class="ident"&gt;define_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;meth&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;with_ansi&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;meth&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:normal&lt;/span&gt;         &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;0m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;      &lt;span class="symbol"&gt;:bold&lt;/span&gt;           &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;1m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:underline&lt;/span&gt;      &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;4m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;      &lt;span class="symbol"&gt;:blink&lt;/span&gt;          &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;5m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:reverse_video&lt;/span&gt;  &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;7m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;      &lt;span class="symbol"&gt;:invisible&lt;/span&gt;      &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;8m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:black&lt;/span&gt;          &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;30m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;     &lt;span class="symbol"&gt;:red&lt;/span&gt;            &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;31m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:green&lt;/span&gt;          &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;32m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;     &lt;span class="symbol"&gt;:yellow&lt;/span&gt;         &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;33m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:blue&lt;/span&gt;           &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;34m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;     &lt;span class="symbol"&gt;:magenta&lt;/span&gt;        &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;35m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:cyan&lt;/span&gt;           &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;36m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;     &lt;span class="symbol"&gt;:white&lt;/span&gt;          &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;37m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:black_bg&lt;/span&gt;       &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;40m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;     &lt;span class="symbol"&gt;:red_bg&lt;/span&gt;         &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;41m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:green_bg&lt;/span&gt;       &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;42m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;     &lt;span class="symbol"&gt;:yellow_bg&lt;/span&gt;      &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;43m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:blue_bg&lt;/span&gt;        &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;44m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;     &lt;span class="symbol"&gt;:magenta_bg&lt;/span&gt;     &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;45m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:cyan_bg&lt;/span&gt;        &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;46m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;     &lt;span class="symbol"&gt;:white_bg&lt;/span&gt;       &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;47m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:clear_line&lt;/span&gt;     &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;K&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;       &lt;span class="symbol"&gt;:clear_screen&lt;/span&gt;   &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;2J&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;define_ansi&lt;/span&gt; &lt;span class="symbol"&gt;:go_home&lt;/span&gt;        &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;0H&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;      &lt;span class="symbol"&gt;:go_to_end&lt;/span&gt;      &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;80L&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;

  &lt;span class="comment"&gt;# Wrap a string with an arbitrary ansi code and the ansi normal code&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;with_ansi&lt;/span&gt;&lt;span class="punct"&gt;(*&lt;/span&gt;&lt;span class="ident"&gt;codes&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;use_ansi?&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{sym_to_ansi(*codes)}#{self}#{sym_to_ansi(:normal)}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="comment"&gt;# Just a little metaprogramming shortcut&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;ansi_codes&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;class&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;ansi_codes&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;private&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;sym_to_ansi&lt;/span&gt;&lt;span class="punct"&gt;(*&lt;/span&gt;&lt;span class="ident"&gt;symbols&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;symbols&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;inject&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;string&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;symbol&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;code&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;ansi_codes&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;symbol&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
        &lt;span class="ident"&gt;string&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;code&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Array&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="ident"&gt;sym_to_ansi&lt;/span&gt;&lt;span class="punct"&gt;(*&lt;/span&gt;&lt;span class="ident"&gt;code&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\e&lt;/span&gt;[&lt;span class="expr"&gt;#{code}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="ident"&gt;string&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="comment"&gt;# determine whether we have ansi support or ANSI enabled&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;use_ansi?&lt;/span&gt;
    &lt;span class="global"&gt;$ANSI&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="constant"&gt;RUBY_PLATFORM&lt;/span&gt; &lt;span class="punct"&gt;!~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;win32&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;i&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;You can grab the new version from my &amp;#8216;other&amp;#8217; directory at Google Code:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;  svn co http://mabs29.googlecode.com/svn/trunk/other/ansi_strings .&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ptrXhWwejd4:6zEq9PGEaOw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ptrXhWwejd4:6zEq9PGEaOw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=ptrXhWwejd4:6zEq9PGEaOw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ptrXhWwejd4:6zEq9PGEaOw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=ptrXhWwejd4:6zEq9PGEaOw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ptrXhWwejd4:6zEq9PGEaOw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/02/07/ansi-strings-2nd-edition</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:ecf67916-8726-44de-86b2-6deb3a82c29a</id>
    <published>2007-02-01T02:54:08-06:00</published>
    <updated>2007-02-01T02:54:11-06:00</updated>
    <title type="html">ANSI Strings: your fun-loving, colourful pal (quick-fix colour library)</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/-9zu1OiaJ_Q/ansi-strings-your-fun-loving-colourful-pal-quick-fix-colour-library" rel="alternate" type="text/html" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    
  <content type="html">&lt;p&gt;You can make your command line interface look a little better than just a bungle of white on black (or however you or your users set your terminal). In my on-going development of a few little open source projects, all of them with CLIs, I abstracted this cute addition to the Ruby snippets junkyard and decided to air it.&lt;/p&gt;


	&lt;p&gt;With it, you can wrap any string with &lt;span class="caps"&gt;ANSI&lt;/span&gt; codes like this:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;I am going to be bold and cyan!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;.&lt;/span&gt;&lt;span class="ident"&gt;bold&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;cyan&lt;/span&gt;
  &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;I am going to be underlined. YAY!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;.&lt;/span&gt;&lt;span class="ident"&gt;underline&lt;/span&gt;
  &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Oh, for the love of God... please STOP BLINKING!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;.&lt;/span&gt;&lt;span class="ident"&gt;blink&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Make your command line interface glisten with choice and sparing use of &lt;span class="caps"&gt;ANSI&lt;/span&gt; codes not just with the above methods dynamically added to the String class, but also by using &lt;ins&gt;with_ansi(*ansi_aliases)&lt;/ins&gt;. It&amp;#8217;s really very simple. Also, all application of &lt;span class="caps"&gt;ANSI&lt;/span&gt; is dependent on $ANSI being true, which, by default, is false (as you can at the bottom of this article, in the first line of code). Lastly, &lt;span class="caps"&gt;ANSI&lt;/span&gt; Strings checks the current platform and won&amp;#8217;t be applied to strings on a win32 platform&amp;#8212;I have no way of testing whether win32 can in certain instances show colour, so I just disable it.&lt;/p&gt;


	&lt;p&gt;You can download &lt;span class="caps"&gt;ANSI&lt;/span&gt; Strings from my googlecode repository like so:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt; svn co http://mabs29.googlecode.com/svn/trunk/other/ansi_strings&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Or you can just look at it here, since it is so simple and small:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="global"&gt;$ANSI&lt;/span&gt; &lt;span class="punct"&gt;||=&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;String&lt;/span&gt;

  &lt;span class="constant"&gt;ANSI_CODES&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;
    &lt;span class="symbol"&gt;:normal&lt;/span&gt;         &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:bold&lt;/span&gt;           &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:underline&lt;/span&gt;      &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;4&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:blink&lt;/span&gt;          &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;5&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:reverse_video&lt;/span&gt;  &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;7&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:invisible&lt;/span&gt;      &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;8&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;

    &lt;span class="symbol"&gt;:black&lt;/span&gt;          &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;30&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:red&lt;/span&gt;            &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;31&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:green&lt;/span&gt;          &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;32&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:yellow&lt;/span&gt;         &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;33&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:blue&lt;/span&gt;           &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;34&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:magenta&lt;/span&gt;        &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;35&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:cyan&lt;/span&gt;           &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;36&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:white&lt;/span&gt;          &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;37&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;

    &lt;span class="symbol"&gt;:black_bg&lt;/span&gt;       &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;40&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:red_bg&lt;/span&gt;         &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;41&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:green_bg&lt;/span&gt;       &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;42&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:yellow_bg&lt;/span&gt;      &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;43&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:blue_bg&lt;/span&gt;        &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;44&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:magenta_bg&lt;/span&gt;     &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;45&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:cyan_bg&lt;/span&gt;        &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;46&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;],&lt;/span&gt;
    &lt;span class="symbol"&gt;:white_bg&lt;/span&gt;       &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;47&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;]&lt;/span&gt;
  &lt;span class="punct"&gt;}&lt;/span&gt;

  &lt;span class="comment"&gt;# Create shortcuts to wrap any string with ansi codes&lt;/span&gt;
  &lt;span class="constant"&gt;ANSI_CODES&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;keys&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;meth&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;define_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;meth&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;with_ansi&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;meth&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;

  &lt;span class="comment"&gt;# Wrap a string with an arbitrary ansi code and the ansi normal code&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;with_ansi&lt;/span&gt;&lt;span class="punct"&gt;(*&lt;/span&gt;&lt;span class="ident"&gt;codes&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;use_ansi?&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{sym_to_ansi(*codes)}#{self}#{sym_to_ansi(:normal)}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;private&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;sym_to_ansi&lt;/span&gt;&lt;span class="punct"&gt;(*&lt;/span&gt;&lt;span class="ident"&gt;symbols&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;symbols&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;inject&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;string&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;symbol&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;string&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\033&lt;/span&gt;[%s%s&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;%&lt;/span&gt; &lt;span class="constant"&gt;ANSI_CODES&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;symbol&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="constant"&gt;ANSI_CODES&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;symbol&lt;/span&gt;&lt;span class="punct"&gt;];&lt;/span&gt; &lt;span class="ident"&gt;string&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="comment"&gt;# determine whether we have ansi support or ANSI enabled&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;use_ansi?&lt;/span&gt;
    &lt;span class="global"&gt;$ANSI&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="constant"&gt;RUBY_PLATFORM&lt;/span&gt; &lt;span class="punct"&gt;!~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;win32&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;i&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=-9zu1OiaJ_Q:SF3QNVPGjHs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=-9zu1OiaJ_Q:SF3QNVPGjHs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=-9zu1OiaJ_Q:SF3QNVPGjHs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=-9zu1OiaJ_Q:SF3QNVPGjHs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=-9zu1OiaJ_Q:SF3QNVPGjHs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=-9zu1OiaJ_Q:SF3QNVPGjHs:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/02/01/ansi-strings-your-fun-loving-colourful-pal-quick-fix-colour-library</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:1eeb80a3-b7ea-43e9-84f0-ea262ea36d2a</id>
    <published>2007-01-24T01:33:07-06:00</published>
    <updated>2007-01-24T01:33:09-06:00</updated>
    <title type="html">MicroTest 1.3 / MicroStub 1.1: still under 4KB</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/Sx67C2Tc1LY/microtest-13-microstub-11-still-under-4kb" rel="alternate" type="text/html" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    
  <content type="html">&lt;p&gt;MicroTest has had a few tweaks, making it more brief and efficient at what it does. Also, I added a setup and teardown interface which can be used. For example, I removed the use of ObjectSpace and squeezed a few percent of performance just from that. On my 1.83GHz, the 6 tests of the self test now consistently take around 500 microseconds (or 0.0005 seconds). Pretty nippy.&lt;/p&gt;


	&lt;p&gt;MicroStub had a little problem with it&amp;#8212;it stubbed instance methods and didn&amp;#8217;t return the class back to normal. So, it violated one of the most basic principles of testing: isolated tests. That&amp;#8217;s all fixed now, using some Spartan back-and-forth aliasing. Obviously a double stub will fail. Maybe I&amp;#8217;ll fix that later.&lt;/p&gt;


	&lt;p&gt;Lastly, I added a benchmarking test case, really just useful for profiling.&lt;/p&gt;


	&lt;p&gt;Of course, the library is still under 4KB&amp;#8212;in fact, it&amp;#8217;s 3940 bytes.&lt;/p&gt;


	&lt;p&gt;Have a look at the repository (revision 25) for the latest version.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=Sx67C2Tc1LY:jk_xcZfZSIM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=Sx67C2Tc1LY:jk_xcZfZSIM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=Sx67C2Tc1LY:jk_xcZfZSIM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=Sx67C2Tc1LY:jk_xcZfZSIM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=Sx67C2Tc1LY:jk_xcZfZSIM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=Sx67C2Tc1LY:jk_xcZfZSIM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/01/24/microtest-13-microstub-11-still-under-4kb</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:1e2cc1e4-441e-4bcb-8ee8-1a0547eb544a</id>
    <published>2007-01-21T12:49:43-06:00</published>
    <updated>2007-01-21T13:10:42-06:00</updated>
    <title type="html">MicroTest 1.2 &amp;amp; MicroStub 1.0: Your microscopic testing pals</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/ZRLa9G6H_1g/microtest-12-microstub-10-your-microscopic-testing-pals" rel="alternate" type="text/html" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    
  <content type="html">&lt;p&gt;Just after writing the previous post, I discovered the need for a little bit of method stubbing and promptly fell upon the task with MicroStyle&lt;sup&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;. Also, I will officially keep MicroTest under 4 kilobytes&lt;sup&gt;&lt;a href="#fn2"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;


So, without further ado, here are the latest new features of MicroTest:
	&lt;ul&gt;
	&lt;li&gt;colour output&lt;/li&gt;
		&lt;li&gt;execution time in microseconds&lt;/li&gt;
		&lt;li&gt;MicroStub: overwrite a method with stub()&lt;/li&gt;
		&lt;li&gt;twice as fast (no sweat)&lt;/li&gt;
		&lt;li&gt;a self-test! (&lt;code&gt;SELFTEST=true ruby microtest.rb&lt;/code&gt;)&lt;/li&gt;
		&lt;li&gt;and its own home. Awwww.&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;MicroTest now lives in my Google Code &lt;span class="caps"&gt;SVN&lt;/span&gt; repository.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;  svn co http://mabs29.googlecode.com/svn/trunk/other/microtest/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; By the way, if you haven&amp;#8217;t noticed yet, I&amp;#8217;ve been a little inspired by &lt;a href="http://code.whytheluckystiff.net/camping/"&gt;Camping&lt;/a&gt;.&lt;/p&gt;


	&lt;p id="fn2"&gt;&lt;sup&gt;2&lt;/sup&gt;  Unlike Camping (because MicroTest is less complicated), I think it only fair that the 4 kilobytes include comments and the self-test.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ZRLa9G6H_1g:I-FHIb6_iiY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ZRLa9G6H_1g:I-FHIb6_iiY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=ZRLa9G6H_1g:I-FHIb6_iiY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ZRLa9G6H_1g:I-FHIb6_iiY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=ZRLa9G6H_1g:I-FHIb6_iiY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ZRLa9G6H_1g:I-FHIb6_iiY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/01/21/microtest-12-microstub-10-your-microscopic-testing-pals</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:e6eb6dc0-483d-4f8e-84d5-c66a605b2b8e</id>
    <published>2007-01-20T07:16:02-06:00</published>
    <updated>2007-01-20T07:16:05-06:00</updated>
    <title type="html">MicroTest just got smaller</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/Mjy2j79iEOg/microtest-just-got-smaller" rel="alternate" type="text/html" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    
  <content type="html">&lt;p&gt;My little library (the smallest ever?) for bootstrapping and lightning-fast testing has just become smaller. I changed the interface a little: now everything is dependent on method name! So you still have two tests, one which expects true and the other false, and which one is determined by using &amp;#8216;def should_&amp;#8217; or &amp;#8216;def should_not_&amp;#8217;. Oh happy days!&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;
&lt;span class="comment"&gt;# A super-small testing suite based on method names.&lt;/span&gt;
&lt;span class="comment"&gt;# It has only two types of expectations: should and should_not.&lt;/span&gt;
&lt;span class="comment"&gt;# These two expectations begin the method name, after which you can&lt;/span&gt;
&lt;span class="comment"&gt;# place anything you like. A 'should_...' method will expect true as&lt;/span&gt;
&lt;span class="comment"&gt;# the result of calling the method. A 'should_not_...' method will,&lt;/span&gt;
&lt;span class="comment"&gt;# conversely, expect false.&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="comment"&gt;# This is MicroTest's self-test:&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="comment"&gt;#   TEST_ROOT = File.dirname(__FILE__) + '/..'&lt;/span&gt;
&lt;span class="comment"&gt;#   require TEST_ROOT + '/lib/microtest.rb'&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="comment"&gt;#   class TestMicroTest &amp;lt; MicroTest&lt;/span&gt;
&lt;span class="comment"&gt;#     def should_expect_true&lt;/span&gt;
&lt;span class="comment"&gt;#       true&lt;/span&gt;
&lt;span class="comment"&gt;#     end&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="comment"&gt;#     def should_not_expect_false&lt;/span&gt;
&lt;span class="comment"&gt;#       false&lt;/span&gt;
&lt;span class="comment"&gt;#     end&lt;/span&gt;
&lt;span class="comment"&gt;#   end&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="comment"&gt;# An example:&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="comment"&gt;#   class BinaryMicroTest &amp;lt; MicroTest&lt;/span&gt;
&lt;span class="comment"&gt;#     def should_exit_cleanly&lt;/span&gt;
&lt;span class="comment"&gt;#       `ruby #{MYEXEC}`; $? == 0&lt;/span&gt;
&lt;span class="comment"&gt;#     end&lt;/span&gt;
&lt;span class="comment"&gt;#     def should_not_exit_cleanly&lt;/span&gt;
&lt;span class="comment"&gt;#       `ruby #{MYEXEC} -garbage`; $? == 0&lt;/span&gt;
&lt;span class="comment"&gt;#     end&lt;/span&gt;
&lt;span class="comment"&gt;#   end&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;MicroTest&lt;/span&gt;

  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;MicroFailure&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;StandardError&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;

    &lt;span class="ident"&gt;attr_accessor&lt;/span&gt; &lt;span class="symbol"&gt;:passed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:failed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:executed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:planned&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:plan&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:failures&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;run&lt;/span&gt;
      &lt;span class="ident"&gt;init&lt;/span&gt;
      &lt;span class="ident"&gt;execute&lt;/span&gt;
      &lt;span class="ident"&gt;report&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;init&lt;/span&gt;
      &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;passed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;executed&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;
      &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;planned&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plan&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;harvest&lt;/span&gt;
      &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failures&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;harvest&lt;/span&gt;
      &lt;span class="ident"&gt;plan&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;
      &lt;span class="constant"&gt;ObjectSpace&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each_object&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Class&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;k&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
        &lt;span class="ident"&gt;plan&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;k&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;k&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;instance_methods&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;grep&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;^should_&lt;/span&gt;&lt;span class="punct"&gt;/)]&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;k&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;MicroTest&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="ident"&gt;planned&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;plan&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;inject&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;c&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;ary&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;c&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="ident"&gt;ary&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;size&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;c&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
      &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;planned&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;plan&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;execute&lt;/span&gt;
      &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Running &lt;span class="expr"&gt;#{self.planned}&lt;/span&gt; test(s)...&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plan&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;tests&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
        &lt;span class="ident"&gt;tests&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
          &lt;span class="ident"&gt;expectation&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;test&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;^should_not&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
          &lt;span class="ident"&gt;instance&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;
          &lt;span class="ident"&gt;r&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;catch_failures&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;expect&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;expectation&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;instance&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
          &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;r&lt;/span&gt;
            &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;passed&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
            &lt;span class="ident"&gt;print&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
            &lt;span class="constant"&gt;STDOUT&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;flush&lt;/span&gt;
          &lt;span class="keyword"&gt;else&lt;/span&gt;
            &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failed&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
            &lt;span class="ident"&gt;print&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;F&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
            &lt;span class="constant"&gt;STDOUT&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;flush&lt;/span&gt;
          &lt;span class="keyword"&gt;end&lt;/span&gt;
          &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;executed&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;expect&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;expected&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ident"&gt;block&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;raise&lt;/span&gt; &lt;span class="constant"&gt;MicroFailure&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should be &lt;span class="expr"&gt;#{expected}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;block&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="ident"&gt;expected&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;catch_failures&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;begin&lt;/span&gt;
        &lt;span class="keyword"&gt;yield&lt;/span&gt;
        &lt;span class="constant"&gt;true&lt;/span&gt;
      &lt;span class="keyword"&gt;rescue&lt;/span&gt; &lt;span class="constant"&gt;Exception&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;e&lt;/span&gt;
        &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failures&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;e&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
        &lt;span class="constant"&gt;false&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;report&lt;/span&gt;
      &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\n\n&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;%d planned, %d executed, %d passed, %d failed.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;%&lt;/span&gt;
        &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;planned&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;executed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;passed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failed&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
      &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failures&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;empty?&lt;/span&gt;
        &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\n&lt;/span&gt;FAILURE REPORT&lt;span class="escape"&gt;\n&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failures&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;report_failure&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;report_failure&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Suite: &lt;span class="expr"&gt;#{f[0]}&lt;/span&gt;&lt;span class="escape"&gt;\n&lt;/span&gt;Test: &lt;span class="expr"&gt;#{f[1]}&lt;/span&gt;&lt;span class="escape"&gt;\n&lt;/span&gt;Exception: &lt;span class="expr"&gt;#{f[2].class}&lt;/span&gt;&lt;span class="escape"&gt;\n&lt;/span&gt;Message: &lt;span class="expr"&gt;#{f[2].message}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\t&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;2&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;backtrace&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\n\t&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;))&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;at_exit&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="constant"&gt;MicroTest&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;run&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=Mjy2j79iEOg:94OMmUtLqDw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=Mjy2j79iEOg:94OMmUtLqDw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=Mjy2j79iEOg:94OMmUtLqDw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=Mjy2j79iEOg:94OMmUtLqDw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=Mjy2j79iEOg:94OMmUtLqDw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=Mjy2j79iEOg:94OMmUtLqDw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/01/20/microtest-just-got-smaller</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:9e51ec4b-709f-40f1-ab98-85ee6afe59ab</id>
    <published>2007-01-11T03:42:37-06:00</published>
    <updated>2007-01-11T03:42:40-06:00</updated>
    <title type="html">Surprise 10min Benchmark: eval, class_eval, instance_eval, define_method, bind</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/4LVWK4pOiFg/surprise-10min-benchmark-eval-class_eval-instance_eval-define_method-bind" rel="alternate" type="text/html" />
    <category term="discoveries" scheme="http://www.mathewabonyi.com/articles/category/discoveries" label="Discoveries" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    
  <content type="html">&lt;p&gt;What is the fastest way to dynamically define a method? &lt;code&gt;define_method&lt;/code&gt;? &lt;code&gt;class_eval&lt;/code&gt;? &lt;code&gt;instance_eval&lt;/code&gt;? With strings or procs? Maybe we can pull out some arcane magic and use &lt;code&gt;instance_method&lt;/code&gt; and &lt;code&gt;bind&lt;/code&gt; (depending on what we want to do).&lt;/p&gt;


	&lt;p&gt;We all need to use some kind of eval at some point and, while it will not usually effect performance when used sparingly, sometimes it just has to make a serious appearance. When I saw how slow &lt;a href="http://rspec.rubyforge.org/"&gt;RSpec&lt;/a&gt; is in comparison to the venerable Test::Unit, I discovered the way it handles its contexts: each one is individually instance_eval&amp;#8217;d into an empty &lt;code&gt;ContextEvalModule&lt;/code&gt;. It isn&amp;#8217;t the only reason for being slower, but it is one and one which can really mount up on big projects.&lt;/p&gt;


	&lt;p&gt;So, moving on from RSpec, Test::Unit and my baseline, MicroTest (see previous post), I became perversely interested in benchmarking all the ways of doing it and found some surprises.&lt;/p&gt;


	&lt;h4&gt;Results (Ruby 1.8.4)&lt;/h4&gt;


&lt;pre&gt;
                            user     system      total        real
eval/str              112.540000   0.060000 112.600000 (112.615607)
instance_eval/str      99.510000   0.040000  99.550000 ( 99.564661)
class_eval/str        112.750000   0.060000 112.810000 (112.824374)
instance_eval/proc     56.750000   0.040000  56.790000 ( 56.802483)
class_eval/proc        56.440000   0.020000  56.460000 ( 56.464818)
define_method/class   112.420000   0.060000 112.480000 (112.489430)
define_method/send    111.540000   0.050000 111.590000 (111.610502)
def/unbind/bind        73.750000   0.060000  73.810000 ( 73.875800)
def                    52.670000   0.030000  52.700000 ( 52.705886)
method/bind            13.520000   0.010000  13.530000 ( 13.523194)
&lt;/pre&gt;

	&lt;h4&gt;Analysis&lt;/h4&gt;


	&lt;p&gt;I put it roughly in the order which I originally expected, with the slowest at the top.&lt;/p&gt;


	&lt;p&gt;The first thing that stunned me was the speed of &lt;code&gt;define_method&lt;/code&gt;. It&amp;#8217;s as slow as a snail. It&amp;#8217;s practically &lt;code&gt;eval&lt;/code&gt;, despite using a proc. I&amp;#8217;ve been using it in preference before, thinking it was faster than any of the evals (proc or string). Apparently not.&lt;/p&gt;


	&lt;p&gt;The second revelation was how proc-based evals are about 50% faster than a def/unbind/bind cycle, which only has &lt;code&gt;def&lt;/code&gt; as the evaluating part. That any eval is close to &lt;code&gt;def&lt;/code&gt; is simply surprising. The &lt;code&gt;instance_eval&lt;/code&gt; variety is the one used by RSpec, so I guess they picked the best one already.&lt;/p&gt;


	&lt;p&gt;The third shock was how slow &lt;code&gt;def&lt;/code&gt; is! Granted, this is a lot of iterations, but it should be quicker than that, surely?&lt;/p&gt;


	&lt;p&gt;So it seems pretty plain that method unbinding as a tuning technique is &lt;em&gt;rubbish&lt;/em&gt;, despite not evaluating anything, whereas a proc-based &lt;code&gt;instance_eval&lt;/code&gt; or simple &lt;code&gt;def&lt;/code&gt; are the clear winners.&lt;/p&gt;


	&lt;h4&gt;But wait&amp;#8230; what about Ruby 1.8.5?&lt;/h4&gt;


	&lt;p&gt;Seeing the decrepitude of &lt;code&gt;define_method&lt;/code&gt; made me wonder about the latest release of Ruby. I downloaded it, compiled it, got it working and ran the same bench:&lt;/p&gt;


	&lt;h4&gt;Results (Ruby 1.8.5)&lt;/h4&gt;


&lt;pre&gt;
                            user     system      total        real
instance_eval/str     109.620000   0.050000 109.670000 (109.685393)
class_eval/str        113.840000   0.060000 113.900000 (113.910749)
eval/str              114.520000   0.060000 114.580000 (114.596238)
define_method/class    70.220000   0.040000  70.260000 ( 70.264506)
define_method/send     68.030000   0.040000  68.070000 ( 68.066688)
def/unbind/bind        73.690000   0.030000  73.720000 ( 73.741549)
instance_eval/proc     59.050000   0.030000  59.080000 ( 59.084445)
class_eval/proc        59.530000   0.030000  59.560000 ( 59.566885)
def                    52.870000   0.030000  52.900000 ( 52.895184)
method/bind            12.470000   0.010000  12.480000 ( 12.484069)
&lt;/pre&gt;

	&lt;h4&gt;Anaylsis&lt;/h4&gt;


	&lt;p&gt;I guess they fixed the problem and more besides. &lt;code&gt;define_method&lt;/code&gt; is now marginally faster than def/unbind/bind (which has not budged an inch). I think I&amp;#8217;ll continue to take the performance hit on &lt;code&gt;define_method&lt;/code&gt; with 1.8.4, knowing I&amp;#8217;ll want to upgrade later in the future and not change techniques.&lt;/p&gt;


	&lt;h4&gt;The old recommendations hold true&lt;/h4&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;instance_eval&lt;/code&gt; with a proc for class, instance or singleton evaluation&lt;/li&gt;
		&lt;li&gt;&lt;code&gt;instance_eval&lt;/code&gt; for changing proc bindings&lt;/li&gt;
		&lt;li&gt;&lt;code&gt;define_method&lt;/code&gt; for converting procs into methods&lt;/li&gt;
		&lt;li&gt;&lt;code&gt;def&lt;/code&gt; for everything else&lt;/li&gt;
		&lt;li&gt;&lt;code&gt;eval&lt;/code&gt; when you really know you need it&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; These findings are only for &lt;code&gt;def meth; true; end&lt;/code&gt; and may vary in complex cases.&lt;/p&gt;


	&lt;h4&gt;Benchmark Code&lt;/h4&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;benchmark&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;meth1&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="comment"&gt;# defining using eval/class/instance&lt;/span&gt;
&lt;span class="ident"&gt;str&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;def meth2; true; end&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="ident"&gt;a_binding&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:binding&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

&lt;span class="comment"&gt;# proc for class/instance&lt;/span&gt;
&lt;span class="ident"&gt;proc1&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Proc&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;meth2&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;

&lt;span class="comment"&gt;# proc for define_method&lt;/span&gt;
&lt;span class="global"&gt;$proc2&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Proc&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;

&lt;span class="comment"&gt;# unbound method for bind&lt;/span&gt;
&lt;span class="ident"&gt;um&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;instance_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:meth1&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

&lt;span class="comment"&gt;# number of iterations&lt;/span&gt;
&lt;span class="ident"&gt;n&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;12000&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt; &lt;span class="number"&gt;600&lt;/span&gt;

&lt;span class="constant"&gt;Benchmark&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;bm&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;22&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;x&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;

  &lt;span class="ident"&gt;x&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;report&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;instance_eval/str&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="constant"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;instance_eval&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;str&lt;/span&gt;&lt;span class="punct"&gt;);&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;x&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;report&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;class_eval/str&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="constant"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;class_eval&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;str&lt;/span&gt;&lt;span class="punct"&gt;);&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;x&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;report&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;eval/str&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;eval&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;str&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;a_binding&lt;/span&gt;&lt;span class="punct"&gt;);&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;x&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;report&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;define_method/class&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;define_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:meth2&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&lt;/span&gt;&lt;span class="global"&gt;$proc2&lt;/span&gt;&lt;span class="punct"&gt;);&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;x&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;report&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;define_method/send&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="constant"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:define_method&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:meth2&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&lt;/span&gt;&lt;span class="global"&gt;$proc2&lt;/span&gt;&lt;span class="punct"&gt;);&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;x&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;report&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;def/unbind/bind&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;
      &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;meth2&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="constant"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;instance_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:meth2&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;bind&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;x&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;report&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;instance_eval/proc&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="constant"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;instance_eval&lt;/span&gt;&lt;span class="punct"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="ident"&gt;proc1&lt;/span&gt;&lt;span class="punct"&gt;);&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;x&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;report&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;class_eval/proc&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="constant"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;class_eval&lt;/span&gt;&lt;span class="punct"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="ident"&gt;proc1&lt;/span&gt;&lt;span class="punct"&gt;);&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;x&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;report&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;def&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;meth2&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;x&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;report&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;method/bind&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;um&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;bind&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;A&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;);&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=4LVWK4pOiFg:iX9n72CPTFw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=4LVWK4pOiFg:iX9n72CPTFw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=4LVWK4pOiFg:iX9n72CPTFw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=4LVWK4pOiFg:iX9n72CPTFw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=4LVWK4pOiFg:iX9n72CPTFw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=4LVWK4pOiFg:iX9n72CPTFw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/01/11/surprise-10min-benchmark-eval-class_eval-instance_eval-define_method-bind</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:ffb8c8b1-be3f-46e2-a96b-512c8b006057</id>
    <published>2007-01-10T16:04:32-06:00</published>
    <updated>2007-01-10T16:04:34-06:00</updated>
    <title type="html">Introducing: MicroTest</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/ZJVAEDXpAP8/introducing-microtest" rel="alternate" type="text/html" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    
  <content type="html">&lt;p&gt;Something I cooked up in an hour for bootstrap testing a testing framework.&lt;/p&gt;


	&lt;p&gt;If anyone actually wants me to maintain it, just give a shout and I&amp;#8217;ll gemify it.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;

&lt;span class="comment"&gt;# A super-small testing suite.&lt;/span&gt;
&lt;span class="comment"&gt;# Just two ways to assert:&lt;/span&gt;
&lt;span class="comment"&gt;#   expect { (whatever must return true) }&lt;/span&gt;
&lt;span class="comment"&gt;#   do_not_expect { (whatever must return false) }&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="comment"&gt;# A microtest looks like this:&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="comment"&gt;#   class BinaryMicroTest &amp;lt; MicroTest&lt;/span&gt;
&lt;span class="comment"&gt;#     def should_exit_cleanly&lt;/span&gt;
&lt;span class="comment"&gt;#       expect do&lt;/span&gt;
&lt;span class="comment"&gt;#         `ruby #{myexec}`&lt;/span&gt;
&lt;span class="comment"&gt;#         $? == 0&lt;/span&gt;
&lt;span class="comment"&gt;#       end&lt;/span&gt;
&lt;span class="comment"&gt;#     end&lt;/span&gt;
&lt;span class="comment"&gt;#   end&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;MicroTest&lt;/span&gt;

  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;MicroFailure&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;StandardError&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;

    &lt;span class="ident"&gt;attr_accessor&lt;/span&gt; &lt;span class="symbol"&gt;:passed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:failed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:executed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:expectations&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:planned&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:plan&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:failures&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;run&lt;/span&gt;
      &lt;span class="ident"&gt;init&lt;/span&gt;
      &lt;span class="ident"&gt;execute&lt;/span&gt;
      &lt;span class="ident"&gt;report&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;init&lt;/span&gt;
      &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;passed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;executed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;expectations&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;
      &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;planned&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plan&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;harvest&lt;/span&gt;
      &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failures&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;harvest&lt;/span&gt;
      &lt;span class="ident"&gt;plan&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;
      &lt;span class="constant"&gt;ObjectSpace&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each_object&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Class&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;k&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
        &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;k&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;respond_to?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:superclass&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="ident"&gt;k&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;superclass&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="constant"&gt;MicroTest&lt;/span&gt;
          &lt;span class="ident"&gt;plan&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;k&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;k&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;instance_methods&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;grep&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;^should_&lt;/span&gt;&lt;span class="punct"&gt;/)]&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="ident"&gt;planned&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;plan&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;inject&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;c&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;ary&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;c&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="ident"&gt;ary&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;size&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;c&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
      &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;planned&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;plan&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;execute&lt;/span&gt;
      &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Running &lt;span class="expr"&gt;#{self.planned}&lt;/span&gt; test(s)...&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plan&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;tests&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
        &lt;span class="ident"&gt;tests&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
          &lt;span class="ident"&gt;instance&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;
          &lt;span class="ident"&gt;r&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;catch_failures&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;instance&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
          &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;r&lt;/span&gt;
            &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;passed&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
            &lt;span class="ident"&gt;print&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
            &lt;span class="constant"&gt;STDOUT&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;flush&lt;/span&gt;
          &lt;span class="keyword"&gt;else&lt;/span&gt;
            &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failed&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
            &lt;span class="ident"&gt;print&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;F&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
            &lt;span class="constant"&gt;STDOUT&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;flush&lt;/span&gt;
          &lt;span class="keyword"&gt;end&lt;/span&gt;
          &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;expectations&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="ident"&gt;instance&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;expectation_count&lt;/span&gt;
          &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;executed&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;catch_failures&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;begin&lt;/span&gt;
        &lt;span class="keyword"&gt;yield&lt;/span&gt;
        &lt;span class="constant"&gt;true&lt;/span&gt;
      &lt;span class="keyword"&gt;rescue&lt;/span&gt; &lt;span class="constant"&gt;Exception&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;e&lt;/span&gt;
        &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failures&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;e&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
        &lt;span class="constant"&gt;false&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;report&lt;/span&gt;
      &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\n\n&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;%d planned, %d executed, %d passed, %d failed.&lt;span class="escape"&gt;\n&lt;/span&gt;%d expectations found.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;%&lt;/span&gt;
        &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;planned&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;executed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;passed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;expectations&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
      &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failures&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;empty?&lt;/span&gt;
        &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\n&lt;/span&gt;FAILURE REPORT&lt;span class="escape"&gt;\n&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;failures&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;report_failure&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;report_failure&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Suite: &lt;span class="expr"&gt;#{f[0]}&lt;/span&gt;&lt;span class="escape"&gt;\n&lt;/span&gt;Test: &lt;span class="expr"&gt;#{f[1]}&lt;/span&gt;&lt;span class="escape"&gt;\n&lt;/span&gt;Exception: &lt;span class="expr"&gt;#{f[2].class}&lt;/span&gt;&lt;span class="escape"&gt;\n&lt;/span&gt;Message: &lt;span class="expr"&gt;#{f[2].message}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\t&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;2&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;backtrace&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\n\t&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;))&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;attr_accessor&lt;/span&gt; &lt;span class="symbol"&gt;:expectation_count&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;
    &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;expectation_count&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;expect&lt;/span&gt;&lt;span class="punct"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="ident"&gt;block&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;expectation_count&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
    &lt;span class="keyword"&gt;raise&lt;/span&gt; &lt;span class="constant"&gt;MicroFailure&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should be true&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;block&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;do_not_expect&lt;/span&gt;&lt;span class="punct"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="ident"&gt;block&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;expectation_count&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
    &lt;span class="keyword"&gt;raise&lt;/span&gt; &lt;span class="constant"&gt;MicroFailure&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should be false&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;block&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;at_exit&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="constant"&gt;MicroTest&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;run&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Just dump that in a helper file and require it, as per normal Test::Unit practices.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ZJVAEDXpAP8:c9bm-iwA7a8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ZJVAEDXpAP8:c9bm-iwA7a8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=ZJVAEDXpAP8:c9bm-iwA7a8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ZJVAEDXpAP8:c9bm-iwA7a8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=ZJVAEDXpAP8:c9bm-iwA7a8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ZJVAEDXpAP8:c9bm-iwA7a8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/01/10/introducing-microtest</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:6b073690-e347-4141-bf5c-ba7b04e819fc</id>
    <published>2007-01-02T09:55:27-06:00</published>
    <updated>2007-01-02T13:13:15-06:00</updated>
    <title type="html">ActiveTest::Redesign &amp;lt; ActiveTest::Examination</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/IpAfRavDIgQ/activetestredesign-activetestexamination" rel="alternate" type="text/html" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="gems" scheme="http://www.mathewabonyi.com/articles/category/gems" label="Gems" />
    
  <content type="html">&lt;p&gt;The following is taken straight from the first two sections of the ActiveTest Redesign Draft. Feel free to comment, ask for features, steer me away from the unnecessary or just say you&amp;#8217;re interested.&lt;/p&gt;


	&lt;h3 id="need"&gt;1.0: &lt;span class="caps"&gt;ADDRESSING A NEED&lt;/span&gt;&lt;/h3&gt;


	&lt;p&gt;This section takes a cursory look at the evolution of ActiveTest.&lt;/p&gt;


	&lt;h4 id="premise"&gt;1.1: The Original Premise&lt;/h4&gt;


	&lt;p&gt;I originally started writing ActiveTest to address problems I had with Test::Unit. It lacked the following for my projects:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;inheritance&lt;/li&gt;
		&lt;li&gt;modularity (adding)&lt;/li&gt;
		&lt;li&gt;flexible (redefining)&lt;/li&gt;
		&lt;li&gt;granular verbosity&lt;/li&gt;
		&lt;li&gt;observing&lt;/li&gt;
		&lt;li&gt;&lt;span class="caps"&gt;BDD&lt;/span&gt; interface&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;The most annoying omissions were the first two. I felt that no matter how much documenting my tests were doing, I hated breaking with the habit of not repeating myself. There are simply times when different parts of an application share a common interface or set of functions. That&amp;#8217;s all there needs to be said in the tests.&lt;/p&gt;


	&lt;h4 id="problems"&gt;1.2: Problems with ActiveTest 0.1.0&lt;/h4&gt;


	&lt;p&gt;The first beta of ActiveTest was a flop; it was too specific to Rails, especially controllers, and used too many metaprogramming techniques. Consequently, it created doubt about the failures in test cases&amp;#8212;were they ActiveTest or real? It mildly discouraged &lt;span class="caps"&gt;TDD&lt;/span&gt; and, as seen in my &lt;a href="http://www.mathewabonyi.com/articles/2006/12/31/activetest-examination-introduction-a-mistake-and-how-to-fix-it/"&gt;small series of articles on the mistakes I&amp;#8217;ve made&lt;/a&gt;, there was too much focus on the One-Liner&amp;#8482;.&lt;/p&gt;


	&lt;h4 id="current-need"&gt;1.3: Current State of Need&lt;/h4&gt;


	&lt;p&gt;From what I can tell, there are three related schools of thought about testing which have members in the Ruby community: Test Driven Development, Behaviour Driven Development and Story Driven Development. The Agile and XP groups of developers select whichever according to taste. ActiveTest can easily cater to all schools and groups.&lt;/p&gt;


	&lt;p&gt;Looking at Test::Unit and comparing it to other projects, it is apparent that Rubyists are being left high and dry. Concessions have come in the form of ZenTest for automation, RSpec for &lt;span class="caps"&gt;BDD&lt;/span&gt;, RFuzz for fuzzing, Mocha for mocking, and rcov for coverage. Each of them (except RSpec) are forced to extend an inherently closed model, Test::Unit::TestCase, and the arcane classes which drive it.&lt;/p&gt;


	&lt;h3 id="redesign"&gt;2.0: &lt;span class="caps"&gt;APPROACHING THE REDESIGN&lt;/span&gt;&lt;/h3&gt;


	&lt;p&gt;This section explains the purpose and approach for the new ActiveTest framework.&lt;/p&gt;


	&lt;h4 id="purpose"&gt;2.1: Purpose&lt;/h4&gt;


	&lt;p&gt;The new ActiveTest is to provide an advanced, backward-compatible replacement for Test::Unit. By this design, it is compatible with many projects already extending Test::Unit, allowing for quick uptake and usage. It is also to be at the forefront of testing theories and advanced testing techniques both within and outside the Ruby community. Ultimately, it&amp;#8217;s purpose is to make developers in all other languages very, very jealous.&lt;/p&gt;


	&lt;h4 id="summary"&gt;2.2: Summary&lt;/h4&gt;


	&lt;p&gt;ActiveTest must change from being a Rails plugin to a gem. It must be compatible with other gems and plugins within the Ruby community or, if compatibility is too complicated, incorporate their ideas with due credit. It must take head-on the latest in &lt;span class="caps"&gt;BDD&lt;/span&gt; and &lt;span class="caps"&gt;SDD&lt;/span&gt; styles. It must reuse as much of its old code to this purpose as possible. Finally, it must draw on projects in other languages, most notably Java and Python, to make use of the latest developments.&lt;/p&gt;


	&lt;h4 id="influences-ruby"&gt;2.3: Influenctial Ruby projects&lt;/h4&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://testunit.talbott.ws/"&gt;Test::Unit&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://www.zenspider.com/ZSS/Products/ZenTest/"&gt;ZenTest&lt;/a&gt; (especially autotest and Rails View testing)&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://mocha.rubyforge.org/"&gt;Mocha&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://rspec.rubyforge.org/"&gt;RSpec&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://rfuzz.rubyforge.org/"&gt;RFuzz&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://eigenclass.org/hiki.rb?rcov"&gt;rcov&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://atomicobject.com/pages/System+Testing+in+Ruby"&gt;Systir&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;Rails Fixtures&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h4 id="influences-other"&gt;2.4: Influential projects in other languages&lt;/h4&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://testoob.sourceforge.net/"&gt;Testoob&lt;/a&gt; (Python)&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://www.junit.org/"&gt;JUnit&lt;/a&gt; (Java)&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://clarkware.com/software/JUnitPerf.html"&gt;JUnitPerf&lt;/a&gt; (Java)&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://groboutils.sourceforge.net/"&gt;GroboUtils&lt;/a&gt; (Java)&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://www.lastcraft.com/simple_test.php"&gt;SimpleTest&lt;/a&gt; (PHP)&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://search.cpan.org/~petdance/TAP-1.00/TAP.pm"&gt;&lt;span class="caps"&gt;TAP&lt;/span&gt;&lt;/a&gt; (Perl)&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://search.cpan.org/~petdance/Test-Harness/"&gt;Test::Harness&lt;/a&gt; (Perl)&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://search.cpan.org/~mschwern/Test-Simple/lib/Test/More.pm"&gt;Test::More&lt;/a&gt; (Perl)&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h4 id="objectives"&gt;2.5: Objectives&lt;/h4&gt;


	&lt;p&gt;The following features from Test::Unit:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;assertions&lt;/li&gt;
		&lt;li&gt;setup &amp;#38; teardown&lt;/li&gt;
		&lt;li&gt;interfaces: Console, Fox, &lt;span class="caps"&gt;GTK&lt;/span&gt;, GTK2, TK&lt;/li&gt;
		&lt;li&gt;auto-running&lt;/li&gt;
		&lt;li&gt;filtering&lt;/li&gt;
		&lt;li&gt;backtrace&lt;/li&gt;
		&lt;li&gt;text report&lt;/li&gt;
		&lt;li&gt;unit tests&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;The following features from Ruby projects are compatible:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;mocking and stubbing (through Mocha)&lt;/li&gt;
		&lt;li&gt;fuzzing (through RFuzz)&lt;/li&gt;
		&lt;li&gt;coverage reports (through rcov)&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;The following features incorporated from other projects:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;data fixtures (from ActiveRecord)&lt;/li&gt;
		&lt;li&gt;automation (from ZenTest)&lt;/li&gt;
		&lt;li&gt;colorized output (from ZenTest &amp;#38; Testoob)&lt;/li&gt;
		&lt;li&gt;granular test selection (from Testoob)&lt;/li&gt;
		&lt;li&gt;different report formats (from Testoob)&lt;/li&gt;
		&lt;li&gt;test threading (from Testoob &amp;#38; GroboUtils)&lt;/li&gt;
		&lt;li&gt;Test Anything Protocol (from &lt;span class="caps"&gt;TAP&lt;/span&gt;, Test::Harness &amp;#38; Test::More)&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;The following features are specific to ActiveTest:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;co-existence of &lt;span class="caps"&gt;TDD&lt;/span&gt;, BDD &amp;#38; &lt;span class="caps"&gt;SDD&lt;/span&gt; testing styles&lt;/li&gt;
		&lt;li&gt;inheritance&lt;/li&gt;
		&lt;li&gt;test type variety: unit, integration, system, performance&lt;/li&gt;
		&lt;li&gt;granular verbosity (backtrace, debug flags)&lt;/li&gt;
		&lt;li&gt;interface: Web/AJAX&lt;/li&gt;
	&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=IpAfRavDIgQ:RdrUpZI1qC8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=IpAfRavDIgQ:RdrUpZI1qC8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=IpAfRavDIgQ:RdrUpZI1qC8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=IpAfRavDIgQ:RdrUpZI1qC8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=IpAfRavDIgQ:RdrUpZI1qC8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=IpAfRavDIgQ:RdrUpZI1qC8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/01/02/activetestredesign-activetestexamination</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:c856eed1-5749-4b55-9604-3ba9a93e6bd9</id>
    <published>2007-01-02T09:49:32-06:00</published>
    <updated>2007-01-02T09:50:14-06:00</updated>
    <title type="html">ActiveTest: Examination, Part V: Redesigning Weak Areas</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/OT8Kyn9g6p4/activetest-examination-part-v-redesigning-weak-areas" rel="alternate" type="text/html" />
    <category term="discoveries" scheme="http://www.mathewabonyi.com/articles/category/discoveries" label="Discoveries" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    <category term="plugins" scheme="http://www.mathewabonyi.com/articles/category/plugins" label="Plugins" />
    
  <content type="html">&lt;p style="text-align:justify;"&gt;There are three aspects of ActiveTest which really need to be cleaned up: the Subject abstract class, metaprogramming subjects, and the extension of Test::Unit.&lt;/p&gt;


	&lt;h4&gt;The Subject Abstraction&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;It is pretty obvious now, without a real need for &lt;code&gt;define_behavioral&lt;/code&gt;, that ActiveTest::Subject as an interface class is an unnecessary intermediary between Base and the subjects which inherit from it. So trim the fat and move all of Subject&amp;#8217;s functionality to Base. The result is a cleaner hierarchy and less temptation to put something in that middle-man model.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;ActiveTest::Controller&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveTest&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;This change in design clears up the association between subjects and the base. Base, which was originally a general abstract for any kind of test case, becomes a general abstract for subjects. The change is mostly conceptual. Functionally, it still provides the rudiments of an ActiveTest suite: inheritance and nested setup/teardown. The next logical step, then, is to start using those advantages with a concrete class: Controller, for example.&lt;/p&gt;


	&lt;h4&gt;Subject Metaprogramming&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;The only concrete subject written so far is Controller and it is a harbinger of doom for the others. It is bloated with metaprogramming, namely &lt;code&gt;succeeds_on&lt;/code&gt; and its peers, because of a misunderstanding of where to use macros. Do we need macros in tests? Not to define them&amp;#8212;Ruby is clean enough to do it already. If macros clean up readability, then when does something become muddied in a test? Consider the following real situation:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;test_should_add_quantity_for_product&lt;/span&gt;
    &lt;span class="attribute"&gt;@cart&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;carts&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:first&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@item&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@cart&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;line_items&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="ident"&gt;assert_no_difference&lt;/span&gt; &lt;span class="attribute"&gt;@cart&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;line_items&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:count&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
      &lt;span class="ident"&gt;assert_difference&lt;/span&gt; &lt;span class="attribute"&gt;@item&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:quantity&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:reload&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
        &lt;span class="attribute"&gt;@cart&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;add_product&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@item&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;product&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;blockquote&gt;
		&lt;p&gt;&lt;strong&gt;What&amp;#8217;s going on?&lt;/strong&gt; In this example from a simple test on shopping carts, you want to ensure that the quantity of an item in a cart is incremented when a duplicate item is added to the cart. The item and cart objects are associated through the line_items join model. Because the Cart model updates Item, the item instance needs to be reloaded to reflect changes in the database.&lt;/p&gt;
	&lt;/blockquote&gt;


	&lt;p style="text-align:justify;"&gt;Essentially, we want to test changes being propagated down a hierarchy of Record associations. This situation is not so uncommon and we can easily think of more instances. Once we have a repetition of this pattern, we can write a macro to open up the hierarchy:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="ident"&gt;expand_hierarchy&lt;/span&gt; &lt;span class="symbol"&gt;:cart_to_items&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:reload&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;cart&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;carts&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:first&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;line_items&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;cart&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;line_items&lt;/span&gt;
    &lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;item&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;line_items&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;test_should_add_item_quantity_for_product&lt;/span&gt;
    &lt;span class="ident"&gt;cart_to_items&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;assert_difference&lt;/span&gt; &lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;item&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:quantity&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
        &lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;cart&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;add_product&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;item&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;product&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;test_should_increment_line_items_for_product&lt;/span&gt;
    &lt;span class="ident"&gt;cart_to_items&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;assert_difference&lt;/span&gt; &lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;line_items&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:count&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
        &lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;cart&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;add_product&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;item&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;product&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;The &lt;code&gt;expand_hierarchy&lt;/code&gt; macro here helps us remove any extra tests or setup from one test case as well as make it clearer what we are testing for: we want to find, within a hierarchy, the correct change. The easiest way to do that is to expand out the parameters. You can think of it as a &amp;#8216;setup-on-demand&amp;#8217;. Also, when any one instance is updated, the others are out of sync. The &lt;code&gt;:reload&lt;/code&gt; flag tells the macro to offer up proxies of each object; when one changes, all of the other objects are refreshed automatically.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;&lt;code&gt;expand_hierarchy&lt;/code&gt;, then, does two things: it offers a standard interface for accessing hierarchical associations and it removes extra setup within a test case. Both make it easier to read what is happening, I&amp;#8217;d say, but don&amp;#8217;t effect the documentation quality. In fact, it seems to improve it. So rather than using macros to define the test cases themselves, a better use would be to package non-assertive elements of testing. These programmatic elements do not need to appear in each test case&amp;#8212;they belong in a library, which is what ActiveTest can standardise.&lt;/p&gt;


	&lt;h4&gt;Extending Test::Unit&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;The premise of ActiveTest began to sour when it became a tool to move test cases into stock meta-definitions. The idea of templating is fine, but the meta-programming aspect was a serious flaw. It hid all the guts in a &lt;span class="caps"&gt;DSL&lt;/span&gt; rather than gave a transparent way of writing tests faster and more easily. The reason it went in this direction is that it is the same direction taken by Test::Unit: linear testing.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;So the question is: should ActiveTest extend or evolve? When looking at the problems with Test::Unit and the original design of ActiveTest, it may not be so bad of an idea to address the real issue which has been skirted around by many Ruby programmers for a long time now: Test::Unit isn&amp;#8217;t flexible enough. I do not wish to trod over Nathaniel Talbott&amp;#8217;s work, but rather to address problems that arise in more complicated environments, like Rails, and are not suited to Test::Unit.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;The new version of ActiveTest, I think, should live up to its namesake and really be its own testing framework, written to be compatible with Test::Unit, but with a design that can be improved by others without lengthy review of its code. If the only change, in the worst case scenario, is to change &lt;code&gt;Test::Unit::TestCase&lt;/code&gt; to &lt;code&gt;ActiveTest::Base&lt;/code&gt;, then there is no issue of learning a new &lt;span class="caps"&gt;DSL&lt;/span&gt;. Instead, ActiveTest would use the same language as Test::Unit, but be completely different under the hood and consequently include much more.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;&lt;strong&gt;Coming Up Next: ActiveTest::Redesign&amp;#8230;&lt;/strong&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=OT8Kyn9g6p4:yx3TUZlWEDY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=OT8Kyn9g6p4:yx3TUZlWEDY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=OT8Kyn9g6p4:yx3TUZlWEDY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=OT8Kyn9g6p4:yx3TUZlWEDY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=OT8Kyn9g6p4:yx3TUZlWEDY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=OT8Kyn9g6p4:yx3TUZlWEDY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/01/02/activetest-examination-part-v-redesigning-weak-areas</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:8d6c2546-429c-4592-bcfe-5c1dc07e9f64</id>
    <published>2007-01-01T23:13:42-06:00</published>
    <updated>2007-01-02T08:47:47-06:00</updated>
    <title type="html">ActiveTest: Examination, Part IV: Salvaging Useful Ideas</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/fKpH20Yc3us/activetest-examination-part-iv-salvaging-useful-ideas" rel="alternate" type="text/html" />
    <category term="discoveries" scheme="http://www.mathewabonyi.com/articles/category/discoveries" label="Discoveries" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    <category term="plugins" scheme="http://www.mathewabonyi.com/articles/category/plugins" label="Plugins" />
    
  <content type="html">&lt;blockquote&gt;
		&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;DRY&lt;/span&gt; and Abstraction:&lt;/strong&gt; There is a difference between practices of &lt;span class="caps"&gt;DRY&lt;/span&gt; and Abstraction. When you are being &lt;span class="caps"&gt;DRY&lt;/span&gt;, you are either addressing a new repetition or continuing an earlier practice. The primary focus of being &lt;span class="caps"&gt;DRY&lt;/span&gt; is &lt;em&gt;not&lt;/em&gt; to create higher level tools for future use. It is a practice of improving past and present code. It replaces, deletes, compresses, encapsulates&amp;#8212;it is passive redesign. Abstraction, on the other hand, is active design. When you try to be &lt;span class="caps"&gt;DRY&lt;/span&gt; without a real model, you are actually abstracting. The process of abstraction is a wider programming activity which can lead quite naturally into over-design, because without real models and implementation, it often fails to address real issues.&lt;/p&gt;
	&lt;/blockquote&gt;


	&lt;p style="text-align:justify;"&gt;With that short sermon, let&amp;#8217;s make ActiveTest useful again. As I mentioned in the introduction to this mini-series, there are things to take away from the old version of ActiveTest. We&amp;#8217;ll now look at them in full and show why they are still useful.&lt;/p&gt;


	&lt;h4&gt;1. Test::Unit inheritance: filtering classes&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;One of the few patches in the old ActiveTest is to modify Test::Unit&amp;#8217;s very well hidden collector filter. The idea of filtration is critical to making inheritance possible. If you have a situation where many ActiveRecord models share many attributes, for example in a Single Table Inheritance for a &amp;#8216;Content&amp;#8217; model, and they all have a body, title, excerpt, owner, created_at, created_by, updated_at and updated_by, the tests for these attributes will be identical across all model tests. So why not create a &amp;#8216;ContentTestCase&amp;#8217; suite which each model test inherits? With Test::Unit you can make a &amp;#8216;StandardContentTests&amp;#8217; module and mix it in, but then you are looking for a place to put them and later looking around for those abstracted tests. Alternatively, if you want to use inheritance, those tests will be run immediately. You want to filter out the abstract class, but still be able to inherit from it. By modifying Test::Unit&amp;#8217;s collector filter, it is possible to put anything in the ActiveTest namespace and it will not be run:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;test/unit/autorunner&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Test::Unit::AutoRunner&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize_with_scrub&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;standalone&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;initialize_without_scrub&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;standalone&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@filters&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="ident"&gt;proc&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;^ActiveTest::&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;class&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;alias_method&lt;/span&gt; &lt;span class="symbol"&gt;:initialize_without_scrub&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:initialize&lt;/span&gt;
  &lt;span class="ident"&gt;alias_method&lt;/span&gt; &lt;span class="symbol"&gt;:initialize&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:initialize_with_scrub&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;I won&amp;#8217;t go on about the peculiarity of having the filter in AutoRunner, but the inaccessibility of the filter requires a monkey patch like the one above. Just poking into &lt;code&gt;initialize&lt;/code&gt; lets us add to the @filters instance variance on AutoRunner and tell it to ignore the ActiveTest namespace. This technique is more about just reading through the code and finding the right place to patch.&lt;/p&gt;


	&lt;h4&gt;2. Metaprogramming techniques (not all of them)&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;Wrapping &lt;code&gt;define_method&lt;/code&gt; has proven to be pretty pointless other than ensuring an unique method name, but developing a lower-level language to standardise class-level macros is a general idea which ActiveTest could keep with some minor adjustments. Ignoring what &lt;code&gt;define_behavioral&lt;/code&gt; does, this is a very short macro definition (from &lt;code&gt;ActiveTest::Controller&lt;/code&gt;):&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;assigns_records_on&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{})&lt;/span&gt;
    &lt;span class="ident"&gt;define_behavioral&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:assign_records_on&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
      &lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;call_&lt;span class="expr"&gt;#{action}&lt;/span&gt;_action&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="ident"&gt;assert_assigned&lt;/span&gt;&lt;span class="punct"&gt;((&lt;/span&gt;&lt;span class="ident"&gt;options&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:with&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="ident"&gt;plural_or_singular&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;)).&lt;/span&gt;&lt;span class="ident"&gt;to_sym&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;If &lt;code&gt;define_behavioral&lt;/code&gt; is made to register behaviors for a better parsing method than using &lt;code&gt;method_missing&lt;/code&gt;, then we give it some meaning, but not really enough to keep it. The rest of the method definition, however, is quite clean for creating macros for Subjects, especially if it is compressed in the way mentioned in &lt;a href="http://www.mathewabonyi.com/articles/2007/01/01/activetest-examination-part-iii-code-bloat/"&gt;Part &lt;span class="caps"&gt;III&lt;/span&gt;&lt;/a&gt;. Making test macros, however, needs to be dropped in the way it is being used here, or at least used more sparingly. We could keep the &lt;code&gt;setup&lt;/code&gt; macro because it sets up useful instance variables and makes reasonable guesses about the test suite&amp;#8217;s environment. So, the macro idea should be kept back for special cases, the current implementation entirely dropped.&lt;/p&gt;


	&lt;h4&gt;3. Nested, self-contained setup methods through &lt;del&gt;sorcery&lt;/del&gt; method unbinding &amp;#38; stacking&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;I can&amp;#8217;t help but be partial to the way I nested setup and teardown methods. It is my first actually useful innovation in Ruby&amp;#8212;a trickery of method unbinding and stacking. Perhaps this bias makes me think it is still useful, but I honestly think it is a useful way to wrap Test::Unit. Let&amp;#8217;s have a look at the way it is done in &lt;code&gt;ActiveTest::Base&lt;/code&gt;:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="ident"&gt;class_inheritable_array&lt;/span&gt; &lt;span class="symbol"&gt;:setup_methods&lt;/span&gt;
  &lt;span class="ident"&gt;class_inheritable_array&lt;/span&gt; &lt;span class="symbol"&gt;:teardown_methods&lt;/span&gt;

  &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;setup_methods&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;
  &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;teardown_methods&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;

  &lt;span class="comment"&gt;# Execute all defined setup methods beyond Test::Unit::TestCase.&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;setup_with_nesting&lt;/span&gt;
    &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;setup_methods&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;method&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;method&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;bind&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;alias_method&lt;/span&gt; &lt;span class="symbol"&gt;:setup&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:setup_with_nesting&lt;/span&gt;

  &lt;span class="comment"&gt;# Execute all defined teardown methods beyond Test::Unit::TestCase.&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;teardown_with_nesting&lt;/span&gt;
    &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;teardown_methods&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;method&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;method&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;bind&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;alias_method&lt;/span&gt; &lt;span class="symbol"&gt;:teardown&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:teardown_with_nesting&lt;/span&gt;

  &lt;span class="comment"&gt;# Suck in every setup and teardown defined, unbind it, remove it and&lt;/span&gt;
  &lt;span class="comment"&gt;# execute it on the child. From here on out, we nest setup/teardown.&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.method_added&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;symbol&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="constant"&gt;ActiveTest&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
      &lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="ident"&gt;symbol&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;
      &lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;setup&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
        &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;setup_methods&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;instance_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:setup&lt;/span&gt;&lt;span class="punct"&gt;)]&lt;/span&gt;
        &lt;span class="ident"&gt;remove_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:setup&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;teardown&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
        &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;teardown_methods&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;instance_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:teardown&lt;/span&gt;&lt;span class="punct"&gt;)]&lt;/span&gt;
        &lt;span class="ident"&gt;remove_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:teardown&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;The first aspect which is useful is allowing setup and teardown nesting without affecting additions to Test::Unit made before or after loading ActiveTest. This is allowed by the code in &lt;code&gt;method_added&lt;/code&gt;. Before ActiveTest is loaded, the methods and aliases already exist on Test::Unit; after ActiveTest is loaded, all setup or teardown methods are stacked in a class inherited array and subsequently undefined from the original class. When setup is finally called on a subclass of ActiveTest::Base, it is the ActiveTest::Base method called first. Here is an example stack:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Test::Unit::TestCase&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;setup&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Test::Unit::TestCase&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;setup_with_fixtures&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;b&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="ident"&gt;alias_method_chain&lt;/span&gt; &lt;span class="symbol"&gt;:setup&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:fixtures&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;ActiveTest::Sample&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveTest&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;setup&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;c&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;SampleTest&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveTest&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Sample&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;setup&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;d&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;;&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;Upon execution, it will output: &lt;code&gt;d&lt;/code&gt;, &lt;code&gt;c&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, then &lt;code&gt;a&lt;/code&gt;. A caveat of this technique is that you must be absolutely certain that each unbound method &lt;code&gt;setup_with_nesting&lt;/code&gt; executes must be rebound to the original class or one of its descendants. Because of the way class inherited attributes work, this rule is not violated: &lt;code&gt;SampleTest&lt;/code&gt; can run the setup method from &lt;code&gt;ActiveTest::Sample&lt;/code&gt; because it is a descendant, but other classes inheriting from &lt;code&gt;ActiveTest::Sample&lt;/code&gt; will not have methods from &lt;code&gt;SampleTest&lt;/code&gt;.&lt;/p&gt;


	&lt;h4&gt;4. Specificity of breaking down things into Subjects&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;This is more of a design note than actual programming. Presently in Test::Unit, it is only possible to add to Test::Unit::TestCase, from which every test suite inherits. There is no way to say that, for example, &lt;code&gt;assert_difference&lt;/code&gt; for an ActiveRecord test is slightly different to &lt;code&gt;'assert_difference&lt;/code&gt; for an ActionController test. The functionality may be the same (for example, refreshing the Record or refreshing the Controller request), but implemented differently for each. By creating subclasses of a test suite that provide only the methods needed by that kind of test, the developer can easily encapsulate test methods and not leak public methods across different kinds of tests. It&amp;#8217;s just cleaner and will definitely form the backbone of test suites in the new ActiveTest framework.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;&lt;strong&gt;Coming Up Next: Redesigning Weak Areas&amp;#8230;&lt;/strong&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=fKpH20Yc3us:dlxV9T7nuMQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=fKpH20Yc3us:dlxV9T7nuMQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=fKpH20Yc3us:dlxV9T7nuMQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=fKpH20Yc3us:dlxV9T7nuMQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=fKpH20Yc3us:dlxV9T7nuMQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=fKpH20Yc3us:dlxV9T7nuMQ:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/01/01/activetest-examination-part-iv-salvaging-useful-ideas</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:c4770d0b-36a8-4c1b-9bbf-5340d0921e33</id>
    <published>2007-01-01T22:39:28-06:00</published>
    <updated>2007-01-02T08:47:44-06:00</updated>
    <title type="html">ActiveTest: Examination, Part III: Code Bloat</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/tbOgQWiotQc/activetest-examination-part-iii-code-bloat" rel="alternate" type="text/html" />
    <category term="discoveries" scheme="http://www.mathewabonyi.com/articles/category/discoveries" label="Discoveries" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    <category term="plugins" scheme="http://www.mathewabonyi.com/articles/category/plugins" label="Plugins" />
    
  <content type="html">&lt;p style="text-align:justify;"&gt;Another problem you can see from the final example in &lt;a href="http://www.mathewabonyi.com/articles/2006/12/31/activetest-examination-part-ii-abstracting-without-basis/"&gt;Part II&lt;/a&gt; is a hint of code bloat. I somehow managed to add way more lines of code to just define my original test case of 3 lines. Along the way, I made it very difficult to document what any single method did, because they all provided only pieces. If you wanted to find out what &lt;code&gt;succeeds_on :index&lt;/code&gt; actually does, you&amp;#8217;ll find yourself trawling through more than a handful of methods. This is the terror of too much premature extraction.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;Each Subject received the Spartan method &amp;#8216;define_behavioral&amp;#8217;, whose function is solely to ensure a new method&amp;#8217;s name is unique:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;define_behavioral&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;prefix&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;core&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;specifics&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{},&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ident"&gt;block&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

        &lt;span class="ident"&gt;proposed&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;test_should_&lt;span class="expr"&gt;#{prefix.to_s.underscore}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="ident"&gt;proposed&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;_&lt;span class="expr"&gt;#{core.to_s.underscore}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;core&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;respond_to?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:to_s&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

        &lt;span class="keyword"&gt;while&lt;/span&gt; &lt;span class="ident"&gt;method_defined?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;proposed&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
          &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;proposed&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;^(.*)(_variant_)(&lt;span class="escape"&gt;\d&lt;/span&gt;+)$&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;
            &lt;span class="ident"&gt;proposed&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{$1}#{$2}#{$3.to_i + 1}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; 
          &lt;span class="keyword"&gt;else&lt;/span&gt;
            &lt;span class="ident"&gt;proposed&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;_variant_2&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
          &lt;span class="keyword"&gt;end&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;

        &lt;span class="ident"&gt;define_method&lt;/span&gt; &lt;span class="ident"&gt;proposed&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ident"&gt;block&lt;/span&gt;

        &lt;span class="ident"&gt;proposed&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;The bloat is in determining such nondescript method names. It is a result of the poor design that any dynamically generated method would clash with another one. Ultimately, it is better if the developer defines the name of the method, because it would be used in a &lt;code&gt;rake agiledox&lt;/code&gt; call. After all, it is their documentation. It did not help matters that this problem was glossed over by writing my own &lt;code&gt;agiledox&lt;/code&gt; which loads in everything first, then uses ObjectSpace&amp;#8230; another rather &lt;del&gt;inelegant&lt;/del&gt; hideous solution.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;If we remove the unique naming, &lt;code&gt;define_behavioral&lt;/code&gt; is useless; it just wraps &lt;code&gt;define_method&lt;/code&gt;. Also, if we remove the dynamic &lt;code&gt;call_#{action}_action&lt;/code&gt; and instead use an instance method on &lt;code&gt;ActiveTest::Controller&lt;/code&gt;, we reduce &lt;code&gt;method_missing&lt;/code&gt; and drop a bunch of unnecessary methods:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;succeeds_on&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;method_name&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{})&lt;/span&gt;
    &lt;span class="ident"&gt;request_method&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;determine_request_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;define_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;method_name&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
      &lt;span class="ident"&gt;call_request_method&lt;/span&gt; &lt;span class="ident"&gt;request_method&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt;
      &lt;span class="ident"&gt;assert_response&lt;/span&gt; &lt;span class="symbol"&gt;:success&lt;/span&gt;
      &lt;span class="ident"&gt;assert_template&lt;/span&gt; &lt;span class="ident"&gt;action&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;Using a direct definition removes the flexibility, but all the code bloat is about meeting some unmentioned need of customising. Until an e-mail is received about it, why do it? Also, better solutions which apply to more than just ActiveTest::Controller may appear naturally. All in all, better not to be so flexible so prematurely.&lt;/p&gt;


	&lt;h4&gt;What Was I Thinking?&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;I really wanted to write a flexible and easily customised macro language. I thought I could make a clean, elegant macro writer for macro writers, with which test cases were created flexibly and efficiently. The flexibility is what caused all the bloat because I decided to use the expedient of &lt;code&gt;method_missing&lt;/code&gt; rather than design a register_behavior type of declaration. Also, I thought it would be better to cater to more edge cases than pigeon-hole ActiveTest to only the most common cases (which could potentially be very few, making ActiveTest pretty useless). The mistake was in trying to cater to these two exclusive cases (common and edge) in their specifics (what they include) rather than how they operate (make a request and check a response).&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;&lt;strong&gt;Coming Up Next: Salvaging Useful Ideas&amp;#8230;&lt;/strong&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=tbOgQWiotQc:KT80PmqchJ4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=tbOgQWiotQc:KT80PmqchJ4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=tbOgQWiotQc:KT80PmqchJ4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=tbOgQWiotQc:KT80PmqchJ4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=tbOgQWiotQc:KT80PmqchJ4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=tbOgQWiotQc:KT80PmqchJ4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2007/01/01/activetest-examination-part-iii-code-bloat</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:bc014641-dd9d-4d3c-a862-6f2143bf3156</id>
    <published>2006-12-31T23:37:50-06:00</published>
    <updated>2007-01-02T08:47:41-06:00</updated>
    <title type="html">ActiveTest: Examination, Part II: Abstracting Without Basis</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/pT2WHYO70yA/activetest-examination-part-ii-abstracting-without-basis" rel="alternate" type="text/html" />
    <category term="discoveries" scheme="http://www.mathewabonyi.com/articles/category/discoveries" label="Discoveries" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    <category term="plugins" scheme="http://www.mathewabonyi.com/articles/category/plugins" label="Plugins" />
    
  <content type="html">&lt;p style="text-align:justify;"&gt;My first mistake started the following cascade effect:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;move all assert_x_success and assert_x_failure to class methods&lt;/li&gt;
		&lt;li&gt;begin defining any test case as a class method&lt;/li&gt;
		&lt;li&gt;extract the definition of an instance method to the superclass level (&lt;code&gt;ActiveTest::Controller&lt;/code&gt;)&lt;/li&gt;
		&lt;li&gt;extract those superclass methods to abstract methods (&lt;code&gt;ActiveTest::Subject&lt;/code&gt;)&lt;/li&gt;
		&lt;li&gt;fragment assert conditions to be passed up the hierarchy (&lt;code&gt;define_behavioural&lt;/code&gt;)&lt;/li&gt;
		&lt;li&gt;extract fragments to the class level (&lt;code&gt;assert_restful_get_success&lt;/code&gt;, &lt;code&gt;method_missing&lt;/code&gt;, etc.)&lt;/li&gt;
		&lt;li&gt;rinse &amp;#38; repeat for each Subject&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p style="text-align:justify;"&gt;Rather than inheriting from a single class with all its assertions self-contained, I now inherited from ActiveTest::Base -&amp;gt; Subject -&amp;gt; Controller. Along the way there were sprinkled methods on the class and eigenclass level which helped define the test cases (&lt;code&gt;ActiveTest::Subject.define_behavioral&lt;/code&gt;) or provide extra assertions. I had extracted so much that it was a nightmare to understand what was actually happening (a rare problem when writing in Ruby).&lt;/p&gt;


	&lt;h4&gt;An Extraction Case Study from 0.1.0 Beta (abridged)&lt;/h4&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;ActiveTest&lt;/span&gt;
  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Controller&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;Subject&lt;/span&gt;

    &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;

      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;succeeds_on&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{})&lt;/span&gt;
        &lt;span class="ident"&gt;define_behavioral&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:succeed_on&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
          &lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;call_&lt;span class="expr"&gt;#{action}&lt;/span&gt;_action&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
          &lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;assert_&lt;span class="expr"&gt;#{action}&lt;/span&gt;_success&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="punct"&gt;...&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;assert_restful_get_success&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{})&lt;/span&gt;
      &lt;span class="ident"&gt;assert_response&lt;/span&gt; &lt;span class="symbol"&gt;:success&lt;/span&gt;
      &lt;span class="ident"&gt;assert_template&lt;/span&gt; &lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="punct"&gt;...&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;method_missing&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;method&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="ident"&gt;options&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;last&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Hash&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;dup&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;pop&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="punct"&gt;{}&lt;/span&gt;
      &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;method&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;^assert_(.*)_(success|failure)$&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;
        &lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;sf&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="global"&gt;$1&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="global"&gt;$2&lt;/span&gt;
        &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;action&lt;/span&gt; &lt;span class="punct"&gt;!~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;get|post|put|delete&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;
          &lt;span class="ident"&gt;request_method&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;class&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;determine_request_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
          &lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;assert_restful_&lt;span class="expr"&gt;#{request_method}&lt;/span&gt;_&lt;span class="expr"&gt;#{sf}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="keyword"&gt;elsif&lt;/span&gt; &lt;span class="ident"&gt;method&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;^call_(.*)_action$&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;
        &lt;span class="ident"&gt;request_method&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;class&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;determine_request_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;action&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="global"&gt;$1&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;call_request_method&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="ident"&gt;request_method&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;else&lt;/span&gt;
        &lt;span class="keyword"&gt;super&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;call_request_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;request_method&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{})&lt;/span&gt;
      &lt;span class="ident"&gt;options&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;expand_options&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;options&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;request_method&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:parameters&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;As you can see, I left out quite a few secondary methods which are being called. The first problem here is in the design of defining a behaviour: it gives too many hooks to the user that allow them to customise. If I am trying to address common cases, this should have been a red flag to a bull&amp;#8212;I was blind. The result was a ton of method_missing calls because I extracted every call into a dynamic method, including the assertions in a dynamically defined test case (twice, in fact). Awful.&lt;/p&gt;


	&lt;h4&gt;What Was I Thinking?&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;When I first began writing ActiveTest, I focused solely on making test cases a one-line business. ActiveTest contained just a simple class method on ActiveTest::Subject to define any kind of test case and a number of classes inheriting from it that defined common groups of assertions. Then, I abstracted the entire process into defining &amp;#8216;behaviours&amp;#8217;, approaching each test case as a behaviour request-response pair (in fact, it is a context if you use &lt;span class="caps"&gt;BDD&lt;/span&gt; terminology). I went through a number of phases looking for the cleanest way to implement this idea of behaviours (I aped RSpec, Test::Rails and others). None of them seemed appropriate, so my own metalanguage was written that looked clean (but only when I did not provide parameters). I thought the metaprogramming of tests with a descriptive class method call would be self-explanatory enough.[1]&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;Take away from this one single maxim, by which you should live and die when you refactor: &lt;em&gt;do not abstract without real-life need&lt;/em&gt;. If you don&amp;#8217;t base your abstraction on something real, you are theorizing&amp;#8212;and we all know that the problem with theories is that they have not been tested.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;&lt;strong&gt;Coming Up Next: Code Bloat&amp;#8230;&lt;/strong&gt;&lt;/p&gt;


	&lt;h4&gt;Footnotes&lt;/h4&gt;


	&lt;p id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; Recently I encountered a situation where parameters were impossible to pass. This unforeseen situation (where I needed RFuzz:http://rfuzz.rubyforge.org/) contributed to my realisation that ActiveTest&amp;#8217;s current design is useless for anything moderately dynamic or complex.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=pT2WHYO70yA:mEM1SMWKIV8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=pT2WHYO70yA:mEM1SMWKIV8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=pT2WHYO70yA:mEM1SMWKIV8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=pT2WHYO70yA:mEM1SMWKIV8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=pT2WHYO70yA:mEM1SMWKIV8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=pT2WHYO70yA:mEM1SMWKIV8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2006/12/31/activetest-examination-part-ii-abstracting-without-basis</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:5d8d7084-a21d-4b8e-9795-497e5d35fd09</id>
    <published>2006-12-31T22:34:58-06:00</published>
    <updated>2007-01-02T08:47:38-06:00</updated>
    <title type="html">ActiveTest: Examination, Part I: Removing Transparency</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/ouoW96k2dhA/activetest-examination-part-i-removing-transparency" rel="alternate" type="text/html" />
    <category term="discoveries" scheme="http://www.mathewabonyi.com/articles/category/discoveries" label="Discoveries" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    <category term="plugins" scheme="http://www.mathewabonyi.com/articles/category/plugins" label="Plugins" />
    
  <content type="html">&lt;p style="text-align:justify;"&gt;The first abstraction I made was to change &lt;code&gt;assert_get_success&lt;/code&gt; and its equivalents (&lt;code&gt;get_failure&lt;/code&gt;, &lt;code&gt;post_success&lt;/code&gt;, etc.) into an abstract idea of common test cases. Obviously every application is going to be treated differently, but to make a suite of tools to remove the most common tests seemed like a useful thing to extract&amp;#8212;it&amp;#8217;s &lt;span class="caps"&gt;DRY&lt;/span&gt;.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;Consequently, I had the following in &lt;code&gt;Test::Unit::TestCase&lt;/code&gt;:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;test_should_index_items&lt;/span&gt;
      &lt;span class="ident"&gt;define_method&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:test_should_index_items&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
        &lt;span class="ident"&gt;assert_get_success&lt;/span&gt; &lt;span class="symbol"&gt;:index&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;I then abstracted it a little:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;test_should_get_with_success&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;action&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="symbol"&gt;:index&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;parameters&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{})&lt;/span&gt;
      &lt;span class="ident"&gt;define_method&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;test_should_&lt;span class="expr"&gt;#{action}&lt;/span&gt;_item&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
        &lt;span class="ident"&gt;assert_get_success&lt;/span&gt; &lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;parameters&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;This is basic DRYing up. However, I have at this point already lost sight of the simplest benefit of writing out test cases with &lt;code&gt;Test::Unit&lt;/code&gt;: &lt;strong&gt;documentation&lt;/strong&gt;. If someone else uses this, the only thing they know is that their &lt;span class="caps"&gt;GET&lt;/span&gt; succeeds&amp;#8230; but in what way? I began documenting what each method does and extracting more and more complex cases which may be &amp;#8216;common&amp;#8217;. Documenting these methods hid the general fault, which was making the entire testing process opaque to the user. Tests started to look like this:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;TestSomething&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveTest&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="ident"&gt;test_should_get_with_success&lt;/span&gt; &lt;span class="symbol"&gt;:index&lt;/span&gt;
  &lt;span class="ident"&gt;test_should_get_with_success&lt;/span&gt; &lt;span class="symbol"&gt;:show&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:id&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
  &lt;span class="ident"&gt;test_should_get_with_success&lt;/span&gt; &lt;span class="symbol"&gt;:new&lt;/span&gt;
  &lt;span class="ident"&gt;test_should_get_with_success&lt;/span&gt; &lt;span class="symbol"&gt;:edit&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:id&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;So my first mistake was removing transparency without thinking about what I was beginning to lose. This is quite project-specific. In most cases, making methods opaque and useful is a good thing, but in the case of ActiveTest, it needs to be flexible &lt;strong&gt;and&lt;/strong&gt; transparent. It is hard to beat a series of asserts.[1]&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;This mistake is probably my least offensive, because some, including myself, may like this feature just to remove all those irritating &lt;code&gt;test_should_index_items&lt;/code&gt;, especially when you are writing new controllers left and right.&lt;/p&gt;


	&lt;h4&gt;What Was I Thinking?&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;I thought that the documenting aspect of testing was purely outlining what everything should do, rather than describing what everything should do. My original idea was to make it easier to document and test at the same time by outlining the cases in each suite. Just in terms of line ratios, I now had a 5-line method which let me write a test case in 1 line. Not bad. It quickly paid for itself because it catered to &lt;code&gt;:index&lt;/code&gt;, &lt;code&gt;:new:&lt;/code&gt;, &lt;code&gt;:show&lt;/code&gt; and &lt;code&gt;:edit&lt;/code&gt;. However, I lost all the expressiveness of showing what is being tested.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;&lt;strong&gt;Coming Up Next: Abstracting Without Basis&amp;#8230;&lt;/strong&gt;&lt;/p&gt;


	&lt;h4&gt;Footnotes&lt;/h4&gt;


	&lt;p id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; It can be done. In fact, the next generation of Active Test, while rather different, frees up the tester to think completely about the behaviour of his code. I will trade in the brevity of test cases for the flexibility of defining them (for anything, whether Rails application, plugin, gem or 60-line file).&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ouoW96k2dhA:-Ko-K9XhkKo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ouoW96k2dhA:-Ko-K9XhkKo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=ouoW96k2dhA:-Ko-K9XhkKo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ouoW96k2dhA:-Ko-K9XhkKo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=ouoW96k2dhA:-Ko-K9XhkKo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=ouoW96k2dhA:-Ko-K9XhkKo:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2006/12/31/activetest-examination-part-i-removing-transparency</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:1d0521b4-6a4b-4064-8e4a-ba1621315aab</id>
    <published>2006-12-31T17:21:17-06:00</published>
    <updated>2007-01-02T08:47:08-06:00</updated>
    <title type="html">ActiveTest: Examination, Introduction: A Mistake and How To Fix It</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/EXLBArOot5o/activetest-examination-introduction-a-mistake-and-how-to-fix-it" rel="alternate" type="text/html" />
    <category term="discoveries" scheme="http://www.mathewabonyi.com/articles/category/discoveries" label="Discoveries" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    <category term="plugins" scheme="http://www.mathewabonyi.com/articles/category/plugins" label="Plugins" />
    
  <content type="html">&lt;p style="text-align:justify;"&gt;In trying to bring ActiveTest to a state resembling my &lt;a href="http://www.mathewabonyi.com/articles/2006/11/18/first-release-activetest-rails-style-testing/"&gt;original article about ActiveTest&lt;/a&gt;, I realised that &amp;#8230; it&amp;#8217;s a piece of crap. It just isn&amp;#8217;t the kind of code which many but myself would find useful and now even I don&amp;#8217;t find it that helpful anymore. A total cowpat of a project, sadly.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;To help both myself and the community, I will be analysing my mistake in full and mercilessly revealing my thinking process, development and design along the way. Because it may get a little&amp;#8230; lengthy, I&amp;#8217;ll break it down into a number of articles. The last article will be dedicated entirely to making Active Test useful (which itself is three parts: salvaging useful ideas, redesigning weak areas, and changing purpose).&lt;/p&gt;


	&lt;h4&gt;Obituary to Active Test 0.1.0 Beta&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;In its death-throes, the Active Test Plugin can still be useful: as an example of what &lt;em&gt;not&lt;/em&gt; to do. Before I start laying into my sad miscarriage of an idea, I&amp;#8217;ll outline useful ideas which came out of it:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Finding a way to extend &lt;code&gt;Test::Unit&lt;/code&gt; which is safe and allows inheritance&lt;/li&gt;
		&lt;li&gt;Learning different ways of metaprogramming (especially ways to wrap &lt;code&gt;define_method&lt;/code&gt;)&lt;/li&gt;
		&lt;li&gt;Extracting a more granular set of things to test in an application&lt;/li&gt;
		&lt;li&gt;Learning techniques for self-testing a test library&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p style="text-align:justify;"&gt;As with many mistakes, more than half of it is about learning rather than providing something useful. That&amp;#8217;s kind of the point, I guess! I didn&amp;#8217;t receive a single support e-mail or comment that it has helped anyone. Then I began to realise what I was doing was wrong and had managed to air my dirty laundry in the process.&lt;/p&gt;


	&lt;h4&gt;An overview of the mistakes&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;With that small obituary to ActiveTest in its current incarnation, let&amp;#8217;s look at the problems in the order in which I&amp;#8217;ll be discussing them:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Removing test case transparency (an advantage of Test::Unit)&lt;/li&gt;
		&lt;li&gt;Extracting and abstracting without a real-world need or basis&lt;sup&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
		&lt;li&gt;Excessive metaprogramming, which introduces ambiguity into tests (&lt;b&gt;the cardinal sin&lt;/b&gt;)&lt;/li&gt;
		&lt;li&gt;Code bloat, plain and simple&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h4&gt;The Idea: the original test case&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;I first came up with ActiveTest when I looked at this test case (from an old revision of one of my projects):&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;test_should_index_items&lt;/span&gt;
     &lt;span class="ident"&gt;assert_get_success&lt;/span&gt; &lt;span class="symbol"&gt;:index&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;At first glance, this looks almost identical to what ActiveTest currently does. I have a case where I want to ensure that a typical &lt;span class="caps"&gt;GET&lt;/span&gt; request receives a &lt;span class="caps"&gt;HTTP 200&lt;/span&gt; response. I extrapolated &lt;span class="caps"&gt;HTTP 200&lt;/span&gt; to &lt;span class="caps"&gt;HTTP GET&lt;/span&gt; into a request-response &amp;#8216;success&amp;#8217; condition. The method was given a parameter (an action to call) that was passed to &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;assert_template&lt;/code&gt;. All perfectly fine, a little &lt;span class="caps"&gt;DRY&lt;/span&gt;, but not unreadable. However, this test case and a bunch of others exactly like it formed almost the entire basis of my initial version of Active Test.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;&lt;strong&gt;Coming Up Next: My First Mistake&amp;#8230;&lt;/strong&gt;&lt;/p&gt;


	&lt;h4&gt;Footnotes&lt;/h4&gt;


	&lt;p id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; For a milder case of this, called premature extraction, see &lt;a href="http://www.therailsway.com/2006/11/15/tracks-part-1"&gt;this article&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=EXLBArOot5o:DLBR9ZF9ET0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=EXLBArOot5o:DLBR9ZF9ET0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=EXLBArOot5o:DLBR9ZF9ET0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=EXLBArOot5o:DLBR9ZF9ET0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=EXLBArOot5o:DLBR9ZF9ET0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=EXLBArOot5o:DLBR9ZF9ET0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2006/12/31/activetest-examination-introduction-a-mistake-and-how-to-fix-it</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:c836ac9b-68c4-4964-9f63-6dbfed2b5c52</id>
    <published>2006-11-26T16:15:49-06:00</published>
    <updated>2007-01-02T08:58:58-06:00</updated>
    <title type="html">Superfast Testing How To: In-Memory SQLite3</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/A1UCluwfwo0/superfast-testing-how-to-in-memory-sqlite3" rel="alternate" type="text/html" />
    <category term="discoveries" scheme="http://www.mathewabonyi.com/articles/category/discoveries" label="Discoveries" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    
  <content type="html">&lt;p style="text-align:justify;"&gt;&lt;strong&gt;Audience&lt;/strong&gt;: This is for both application and plugin developers. Especially plugin developers. Always use SQLite3 in-memory databases as your default for plugin tests which require a database.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;Some of you may have heard of SQLite3, played with it and decided it is too limited or don&amp;#8217;t like the files which it creates in your project tree. There is a less-sung alternative which is much, much faster (actual benchmarks vary because of memory speeds, but for me PostgreSQL is about 33% slower and MySQL about 10% slower). Over a large testing library with thousands of assertions, you will feel that meagre 10% by a comfortable minute. &lt;strong&gt;Slight disadvantage&lt;/strong&gt;: you can&amp;#8217;t get that coffee during testing anymore!&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;There are a handful of sites which have spoken of SQLite3 in-memory databases this year, but to get it working you&amp;#8217;ll find the information is a little scattered. The following is a step-by-step &amp;#8216;how to&amp;#8217; on making your application and plugin tests superfast and lightweight, but please read over these pages first:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://nubyonrails.com/articles/2006/06/01/san-francisco-sqlite3-memory-tests-asteroids"&gt;TopFunky on fixing Rails support for SQLite in memory&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://martinfowler.com/bliki/InMemoryTestDatabase.html"&gt;Martin Fowler on memory databases&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://code.whytheluckystiff.net/camping/wiki/BeAlertWhenOnSqlite3"&gt;Why on Being Alert on SQLite&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://wiki.rubyonrails.com/rails/pages/HowtoUseSQLite"&gt;Rails Wiki on using SQLite&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h4&gt;Set Up&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;A lot of this doesn&amp;#8217;t need much explanation, so I&amp;#8217;m just going to dump out the procedure to get you from MySQL/PostgreSQL/whatever testing to SQLite3 in-memory database testing, making you wait less and code more.&lt;/p&gt;


	&lt;h4&gt;Step 1: Install SQLite3 and friends&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;If you do not have SQLite3 already, you need to go to the &lt;a href="http://www.sqlite.org/"&gt;SQLite website&lt;/a&gt; and download the appropriate package. If you use Mac &lt;span class="caps"&gt;OS X&lt;/span&gt;, you need to install the &lt;span class="caps"&gt;SWIG&lt;/span&gt; library too.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;Once you have the latest SQLite3 distribution, you need to install the sqlite3-ruby gem:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;  user@host railsproj$ sudo gem install sqlite3-ruby --source code.whytheluckystiff.net&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;Select the highest &amp;#8216;(ruby)&amp;#8217; version (1.1.0.1 at the time of this article). You should see it compile a native extension and install without a hitch.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you already have sqlite3-ruby installed but do not remember seeing a native extension being compiled, you should uninstall (`gem uninstall sqlite3-ruby`) and install it again with the &lt;span class="caps"&gt;SWIG&lt;/span&gt; library.&lt;/p&gt;


	&lt;h4&gt;Step 2: Install the Memory Test Fix plugin&lt;/h4&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;  user@host railsproj$ ./script/plugin install http://topfunky.net/svn/plugins/memory_test_fix&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h4&gt;Step 3: Change &lt;span class="caps"&gt;RAILS&lt;/span&gt;_ROOT/config/database.yml&lt;/h4&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_yaml "&gt;&lt;span class="key"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
  &lt;span class="key"&gt;adapter&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; sqlite3
  &lt;span class="key"&gt;database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;:memory:&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="key"&gt;verbosity&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; quiet&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p style="text-align:justify;"&gt;&lt;strong&gt;Note&lt;/strong&gt;: remove `verbosity: quiet` at the beginning to make sure SQLite3 is loading your db/schema.rb file. If you are not using Migrations or db/schema.rb, you will also need to set `config.active_record.schema_format = :sql` in your environment files and point Rails to the correct schema file.&lt;/p&gt;


	&lt;h4&gt;Step 4: Test it&lt;/h4&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;  user@host railsproj$ rake test&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h4&gt;Notes&lt;/h4&gt;


	&lt;p style="text-align:justify;"&gt;As TopFunky says: if you use database specific &lt;span class="caps"&gt;SQL&lt;/span&gt;, using SQLite3 will only get in the way.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=A1UCluwfwo0:gNdW3V4tpnI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=A1UCluwfwo0:gNdW3V4tpnI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=A1UCluwfwo0:gNdW3V4tpnI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=A1UCluwfwo0:gNdW3V4tpnI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=A1UCluwfwo0:gNdW3V4tpnI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=A1UCluwfwo0:gNdW3V4tpnI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2006/11/26/superfast-testing-how-to-in-memory-sqlite3</feedburner:origLink></entry>
  <entry>
    <author>
      <name>Mathew Abonyi</name>
    </author>
    <id>urn:uuid:4c7331bf-0a4c-4d94-92cd-5ed2d36a2546</id>
    <published>2006-11-26T15:46:18-06:00</published>
    <updated>2006-11-26T15:46:18-06:00</updated>
    <title type="html">ActiveTest: TestHelper Bug Fix</title>
    <link href="http://feedproxy.google.com/~r/WoodForTheTrees/~3/C1w2ticwRZY/activetest-testhelper-bug-fix" rel="alternate" type="text/html" />
    <category term="ruby" scheme="http://www.mathewabonyi.com/articles/category/ruby" label="Ruby" />
    <category term="rails" scheme="http://www.mathewabonyi.com/articles/category/rails" label="Rails" />
    <category term="plugins" scheme="http://www.mathewabonyi.com/articles/category/plugins" label="Plugins" />
    
  <content type="html">&lt;p style="text-align:justify;"&gt;There was an issue with test_helper variables set on Test::Unit::TestCase not being set properly. Values would always be the default because of the way class inherited attributes are passed down the hierarchy. You now need to require the active_test framework and any other plugins you have installed at the end of &lt;span class="caps"&gt;RAILS&lt;/span&gt;_ROOT/test/test_helper.rb.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;Also, plugin developers should never require their libraries through init. Instead, there should be a file with the name of their plugin in &lt;span class="caps"&gt;PLUGIN&lt;/span&gt;_ROOT/lib. For example, active_test_authentication has a file at &lt;span class="caps"&gt;PLUGIN&lt;/span&gt;_ROOT/lib/active_test_authentication.rb. This is the file which is loaded when `require &amp;#8216;active_test_authentication&amp;#8217;` is called at the end of test_helper.&lt;/p&gt;


	&lt;p style="text-align:justify;"&gt;This fix actually alleviates more than &lt;a href="http://code.google.com/p/mabs29/issues/detail?id=2"&gt;google issue 2&lt;/a&gt;. When loaded plugins with a name lexicographically earlier than active_test, active_test would automatically be loaded. This is trivial, but not intended. In any case, you must now require both the active_test framework and any plugins you use at the end of test_helper.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=C1w2ticwRZY:_pupD9s5ZK8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=C1w2ticwRZY:_pupD9s5ZK8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=C1w2ticwRZY:_pupD9s5ZK8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=C1w2ticwRZY:_pupD9s5ZK8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?i=C1w2ticwRZY:_pupD9s5ZK8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/WoodForTheTrees?a=C1w2ticwRZY:_pupD9s5ZK8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/WoodForTheTrees?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content><feedburner:origLink>http://www.mathewabonyi.com/articles/2006/11/26/activetest-testhelper-bug-fix</feedburner:origLink></entry>
</feed>
