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

<channel>
	<title>Virtuous Code &#187; Rails</title>
	<atom:link href="http://devblog.avdi.org/category/rails-2/feed/" rel="self" type="application/rss+xml" />
	<link>http://devblog.avdi.org</link>
	<description>&#34;The three virtues of a programmer: laziness, impatience, and hubris&#34; -- Larry Wall</description>
	<lastBuildDate>Tue, 21 May 2013 18:19:00 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>RubyTapas Episode 21: Domain Model Events</title>
		<link>http://devblog.avdi.org/2012/11/12/rubytapas-episode-21-domain-model-events/</link>
		<comments>http://devblog.avdi.org/2012/11/12/rubytapas-episode-21-domain-model-events/#comments</comments>
		<pubDate>Mon, 12 Nov 2012 14:00:19 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[RubyTapas Samples]]></category>
		<category><![CDATA[Screencasts]]></category>

		<guid isPermaLink="false">http://devblog.avdi.org/?p=4219</guid>
		<description><![CDATA[Today&#8217;s free Monday episode features a rare delving into Rails code. We take a look at a fat controller, and thin it down by identifying the model lifecycle events that are hiding inside it. This episode garnered a lot of &#8230; <a href="http://devblog.avdi.org/2012/11/12/rubytapas-episode-21-domain-model-events/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Today&#8217;s free Monday episode features a rare delving into Rails code. We take a look at a fat controller, and thin it down by identifying the model lifecycle events that are hiding inside it.</p>
<p><script type='text/javascript' src='http://content.bitsontherun.com/players/AZd6XIvV-uGtfOrbJ.js'></script></p>
<p>This episode garnered a lot of discussion after I initially sent it out to RubyTapas subscribers. Ryan Bates posted <a href="https://gist.github.com/4048918">an alternative refactoring</a>, as did <a href="https://github.com/elight/rubytapas_21_alternative/blob/master/refactoring.rb">Evan Light</a>.</p>
<div class="boilerplate">
Like what you see? This is just a taste of <a href="http://rubytapas.com/?utm_source=devblog&utm_medium=article&utm_campaign=sample">RubyTapas</a>! Sign up today to get three videos a week, along with full source code and transcripts.
<div class="cta ">
<p class="small"><a href="https://rubytapas.dpdcart.com/subscriber/add?plan_id=10&amp;plan_term_id=17&utm_source=devblog&utm_medium=article&utm_campaign=sample">Subscribe Now!</a></p>
</div>
Or <a href="http://rubytapas.com">click here to learn more</a>.
</div>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/11/12/rubytapas-episode-21-domain-model-events/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Configuring database_cleaner with Rails, RSpec, Capybara, and Selenium</title>
		<link>http://devblog.avdi.org/2012/08/31/configuring-database_cleaner-with-rails-rspec-capybara-and-selenium/</link>
		<comments>http://devblog.avdi.org/2012/08/31/configuring-database_cleaner-with-rails-rspec-capybara-and-selenium/#comments</comments>
		<pubDate>Fri, 31 Aug 2012 04:00:00 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[capybara]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[rspec]]></category>
		<category><![CDATA[selenium]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://devblog.avdi.org/?p=2620</guid>
		<description><![CDATA[How I avoid finding myself in database bizarro world while testing Rails apps. <a href="http://devblog.avdi.org/2012/08/31/configuring-database_cleaner-with-rails-rspec-capybara-and-selenium/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>If you write Rails code, or any Ruby code that interacts with a database, and you also write automated tests, chances are you have heard of or used the <a href="https://github.com/bmabey/database_cleaner">database_cleaner</a> gem. It&#8217;s a terrific gem that abstracts away the various ORM APIs for getting the DB into a &#8220;blank slate&#8221; state. </p>
<p> Periodically I start a new project using Rails, with RSpec, Capybara, and Selenium for acceptance testing, and a short way into it I find myself banging my head against bizarre inconsistencies with the test database. I&#8217;ll set up some records in the test DB, only to have the Selenium-driven browser-based tests act like those records never existed. Eventually, I&#8217;ll realize what I did wrong and curse my feeble brain for not remembering the last time I solved the same problem. </p>
<p> The problem is always the same: the tests are being wrapped in database transactions, so any code running outside the actual test process (like, say, a server process servicing a Selenium-driven browser request) <em>does not see</em> the database fixture I&#8217;ve so carefully assembled. </p>
<p> I just walked a <a href="http://devblog.avdi.org/pair-programming-services/">pairing</a> client through the same fix, so in the interests of remembering the steps, and hopefully preventing some other folks from tearing their hair out, here are the steps needed. </p>
<p> First of all, and this is very important, go into <code>spec/spec_helper.rb</code> and change this line: </p>
<pre class="src src-ruby">config.use_transactional_fixtures = <span class="org-variable-name">true</span>
</pre>
<p> To: </p>
<pre class="src src-ruby">config.use_transactional_fixtures = <span class="org-variable-name">false</span>
</pre>
<p> This will disable rspec-rails&#8217; implicit wrapping of tests in a database transaction. Without disabling this, none of the following configuration will matter. </p>
<p> Now configure <code>database_cleaner</code>. I usually create a separate file called <code>spec/support/database_cleaner.rb</code> for this. Inside, I put something like this: </p>
<pre class="src src-ruby"><span class="org-type">RSpec</span>.configure <span class="org-keyword">do</span> |config|

  config.before(<span class="org-constant">:suite</span>) <span class="org-keyword">do</span>
    <span class="org-type">DatabaseCleaner</span>.clean_with(<span class="org-constant">:truncation</span>)
  <span class="org-keyword">end</span>

  config.before(<span class="org-constant">:each</span>) <span class="org-keyword">do</span>
    <span class="org-type">DatabaseCleaner</span>.strategy = <span class="org-constant">:transaction</span>
  <span class="org-keyword">end</span>

  config.before(<span class="org-constant">:each</span>, <span class="org-constant">:js</span> =&gt; <span class="org-variable-name">true</span>) <span class="org-keyword">do</span>
    <span class="org-type">DatabaseCleaner</span>.strategy = <span class="org-constant">:truncation</span>
  <span class="org-keyword">end</span>

  config.before(<span class="org-constant">:each</span>) <span class="org-keyword">do</span>
    <span class="org-type">DatabaseCleaner</span>.start
  <span class="org-keyword">end</span>

  config.after(<span class="org-constant">:each</span>) <span class="org-keyword">do</span>
    <span class="org-type">DatabaseCleaner</span>.clean
  <span class="org-keyword">end</span>

<span class="org-keyword">end</span>
</pre>
<p> Let&#8217;s take that step by step. </p>
<pre class="src src-ruby">config.before(<span class="org-constant">:suite</span>) <span class="org-keyword">do</span>
  <span class="org-type">DatabaseCleaner</span>.clean_with(<span class="org-constant">:truncation</span>)
<span class="org-keyword">end</span>
</pre>
<p> This says that before the entire test suite runs, clear the test database out completely. This gets rid of any garbage left over from interrupted or poorly-written tests&mdash;a common source of surprising test behavior. </p>
<pre class="src src-ruby">config.before(<span class="org-constant">:each</span>) <span class="org-keyword">do</span>
  <span class="org-type">DatabaseCleaner</span>.strategy = <span class="org-constant">:transaction</span>
<span class="org-keyword">end</span>
</pre>
<p> This part sets the default database cleaning strategy to be transactions. Transactions are very fast, and for all the tests where they <em>do</em> work&mdash;that is, any test where the entire test runs in the RSpec process&mdash;they are preferable. </p>
<pre class="src src-ruby">config.before(<span class="org-constant">:each</span>, <span class="org-constant">:js</span> =&gt; <span class="org-variable-name">true</span>) <span class="org-keyword">do</span>
  <span class="org-type">DatabaseCleaner</span>.strategy = <span class="org-constant">:truncation</span>
<span class="org-keyword">end</span>
</pre>
<p> This line <em>only</em> runs before examples which have been flagged <code>:js =&gt; true</code>. By default, these are the only tests for which Capybara fires up a test server process and drives an actual browser window via the Selenium backend. For these types of tests, transactions won&#8217;t work, so this code overrides the setting and chooses the &#8220;truncation&#8221; strategy instead. </p>
<pre class="src src-ruby">config.before(<span class="org-constant">:each</span>) <span class="org-keyword">do</span>
  <span class="org-type">DatabaseCleaner</span>.start
<span class="org-keyword">end</span>

config.after(<span class="org-constant">:each</span>) <span class="org-keyword">do</span>
  <span class="org-type">DatabaseCleaner</span>.clean
<span class="org-keyword">end</span>
</pre>
<p> These lines hook up <code>database_cleaner</code> around the beginning and end of each test, telling it to execute whatever cleanup strategy we selected beforehand. </p>
<p> And that&#8217;s it! </p>
<p> Note that this is all for RSpec, and does not cover Cucumber configuration. </p>
<p> Hopefully this will help someone else out there avoid the frustrations I&#8217;ve run into! </p>
<p> EDIT: A few people have asked me why I don&#8217;t just force all threads to share the same ActiveRecord connection, as demonstrated <a href="https://gist.github.com/470808">in this Gist</a>. A few reasons: </p>
<ul>
<li>Using <code>database_cleaner</code> implies that I want ORM   neutrality. <code>database_cleaner</code> supports ActiveRecord, DataMapper,   MongoMapper, and others. The solution linked above only works for   ActiveRecord.  </li>
<li>It&#8217;s a monkey-patched kludge which will only work so long as AR   refrains from changing its connection-sharing internals. And frankly   I&#8217;m not sure I trust it to work across all Ruby VM and database   combinations (UPDATE: And indeed, I&#8217;ve now seen two different people   say there are race conditions with the current Postgres   adapter). I&#8217;d be more inclined to use it if ActiveRecord had a   published configuration option which was known to work in all   contexts.  </li>
<li>As I stressed above, I&#8217;m careful to set things up so that only the   tests that need them fall back to truncation. Since <code>:js =&gt; true</code>   tests generally don&#8217;t form the bulk of my suite (and since they are   unavoidably slow anyway, due to the overhead of driving a browser),   I&#8217;m not overly concerned about the added overhead. Perhaps if all of   my acceptance tests drove a live browser I&#8217;d be more worried about   it.  </li>
<li>In cases where database truncation <em>is</em> taking up a significant   amount of test time, you can usually speed things up with some   judicious control of which subset of tables get truncated for a   given test. This is something <code>database_cleaner</code> makes pretty   easy. Maybe that would make a good topic for a followup post.  </li>
<li>UPDATE: Oh yeah, and as <a href="https://twitter.com/donaldball/status/241548932415635456">@donaldball points out</a>, sharing a   transaction between test and test server means acceptance tests   don&#8217;t run quite the same as they would in production. Specifically,   they&#8217;ll never trigger <code>after_commit</code> hooks. </li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/08/31/configuring-database_cleaner-with-rails-rspec-capybara-and-selenium/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>ActiveRecord Default Association Extensions</title>
		<link>http://devblog.avdi.org/2012/01/19/activerecord-default-association-extensions/</link>
		<comments>http://devblog.avdi.org/2012/01/19/activerecord-default-association-extensions/#comments</comments>
		<pubDate>Thu, 19 Jan 2012 19:07:00 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[activerecord]]></category>

		<guid isPermaLink="false">http://avdi.org/devblog/?p=1991</guid>
		<description><![CDATA[How to add helper methods to all associations of a given type, as well as to the model class. <a href="http://devblog.avdi.org/2012/01/19/activerecord-default-association-extensions/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Let&#8217;s say we want a helper method <code>#build_fuzzy</code> everywhere we have a collection of socks. </p>
<pre class="src src-ruby"><span class="org-type">Sock</span>.build_fuzzy <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; #&lt;Sock type: "fuzzy" id: nil drawer_id: nil&gt;</span>
sock_drawer.socks.build_fuzzy <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; #&lt;Sock type: "fuzzy" id: nil drawer_id: 123&gt;</span>
</pre>
<p> At first we might think to put it in an a collection extension module: </p>
<pre class="src src-ruby"><span class="org-keyword">class</span> <span class="org-type">Sock</span> &lt; <span class="org-type">ActiveRecord</span>::<span class="org-type">Base</span>
  <span class="org-keyword">module</span> <span class="org-type">SockCollectionHelpers</span>
    <span class="org-keyword">def</span> <span class="org-function-name">build_fuzzy</span>
      build(type: <span class="org-string">"fuzzy"</span>)
    <span class="org-keyword">end</span>
  <span class="org-keyword">end</span>
  <span class="org-comment-delimiter"># </span><span class="org-comment">...</span>
<span class="org-keyword">end</span>
</pre>
<p> Now we have to remember to add it to every <code>Sock</code> association: </p>
<pre class="src src-ruby"><span class="org-keyword">class</span> <span class="org-type">Drawer</span> &lt; <span class="org-type">ActiveRecord</span>::<span class="org-type">Base</span>
  has_many <span class="org-constant">:socks</span>, extend: <span class="org-type">Sock</span>::<span class="org-type">SockCollectionHelpers</span>
  <span class="org-comment-delimiter"># </span><span class="org-comment">...</span>
<span class="org-keyword">end</span>
</pre>
<p> And we also need to include it in <code>Sock</code>: </p>
<pre class="src src-ruby"><span class="org-keyword">class</span> <span class="org-type">Sock</span> &lt; <span class="org-type">ActiveRecord</span>::<span class="org-type">Base</span>
  <span class="org-comment-delimiter"># </span><span class="org-comment">...</span>
  extend <span class="org-type">SockCollectionHelpers</span>
<span class="org-keyword">end</span>
</pre>
<p> &hellip;but this doesn&#8217;t work: </p>
<pre class="src src-ruby">sock_drawer.socks.build_fuzzy <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; #&lt;Sock type: "fuzzy" id: nil drawer_id: 123&gt;</span>
<span class="org-type">Sock</span>.build_fuzzy <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; Raises no method exception for Sock.build()</span>
</pre>
<p> And anyway, we want this to be available globally without explicitly extending associations. </p>
<p> Next, we try adding a class method to the <code>Sock</code> class. Since ActiveRecord association proxies delegate missing methods to the assoication class, it seems like this should work. </p>
<pre class="src src-ruby"><span class="org-keyword">class</span> <span class="org-type">Sock</span> &lt; <span class="org-type">ActiveRecord</span>::<span class="org-type">Base</span>
  <span class="org-keyword">def</span> <span class="org-function-name">self.build_fuzzy</span>
    build(type: <span class="org-string">"fuzzy"</span>)
  <span class="org-keyword">end</span>
  <span class="org-comment-delimiter"># </span><span class="org-comment">...</span>
<span class="org-keyword">end</span>
</pre>
<p> This is a complete failure. While the association does forward the <code>.build_fuzzy</code> call to <code>Sock</code>, once in the call it is operating in the context of the class object, which as we saw before has no <code>#build</code> method. </p>
<pre class="src src-ruby">sock_drawer.socks.build_fuzzy <span class="org-comment-delimiter"># </span><span class="org-comment">Raises no method exception for Sock.build()</span>
<span class="org-type">Sock</span>.build_fuzzy <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; Raises no method exception for Sock.build()</span>
</pre>
<p> However, a slight change makes everything work as hoped: </p>
<pre class="src src-ruby"><span class="org-keyword">class</span> <span class="org-type">Sock</span> &lt; <span class="org-type">ActiveRecord</span>::<span class="org-type">Base</span>
  <span class="org-keyword">def</span> <span class="org-function-name">self.build_fuzzy</span>
    scoped.build(type: <span class="org-string">"fuzzy"</span>)
  <span class="org-keyword">end</span>
<span class="org-keyword">end</span>
</pre>
<p> Now when we call <code>#build_fuzzy</code> on the class it builds a fuzzy sock unassociated with any drawer, and when we call it on an association it builds a fuzzy sock with the appropriate drawer ID set: </p>
<pre class="src src-ruby"><span class="org-type">Sock</span>.build_fuzzy <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; #&lt;Sock type: "fuzzy" id: nil drawer_id: nil&gt;</span>
sock_drawer.socks.build_fuzzy <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; #&lt;Sock type: "fuzzy" id: nil drawer_id: 123&gt;</span>
</pre>
<p> All this thanks to the <a href="http://apidock.com/rails/ActiveRecord/NamedScope/ClassMethods/scoped"><code>#scoped</code></a> method, which is aware of the current scope. </p>
<p> Thank you to <a href="https://github.com/dkubb">Dan Kubb</a> for figuring this out for me. </p>
<p> UPDATE: Fixed <code>def build_fuzzy</code> to be <code>def self.build_fuzzy</code> in the last two examples. </p>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/01/19/activerecord-default-association-extensions/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>DRYing up your validations using DB reflection</title>
		<link>http://devblog.avdi.org/2011/10/20/drying-up-your-validations-using-db-reflection/</link>
		<comments>http://devblog.avdi.org/2011/10/20/drying-up-your-validations-using-db-reflection/#comments</comments>
		<pubDate>Thu, 20 Oct 2011 13:00:00 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[activerecord]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[validations]]></category>

		<guid isPermaLink="false">http://avdi.org/devblog/?p=1884</guid>
		<description><![CDATA[Avoiding silent truncation of your model fields requires putting length validations on them. But this can introduce duplication of knowledge. In this post I demonstrate how to pull limit information directly from the DB into your model validations. <a href="http://devblog.avdi.org/2011/10/20/drying-up-your-validations-using-db-reflection/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Out of the box, ActiveRecord will silently truncate attribute values which exceed their column width. </p>
<pre class="src src-ruby">require <span class="org-string">'active_record'</span>

<span class="org-keyword">class</span> <span class="org-type">Greeting</span> &lt; <span class="org-type">ActiveRecord</span>::<span class="org-type">Base</span>
  establish_connection(adapter:  <span class="org-string">'mysql'</span>, 
                       database: <span class="org-string">'scratch'</span>, 
                       password: <span class="org-type">ENV</span>[<span class="org-string">'MYSQL_PASSWORD'</span>])

  connection.create_table(<span class="org-constant">:greetings</span>) <span class="org-keyword">do</span> |t|
    t.string <span class="org-constant">:text</span>, limit: 10
  <span class="org-keyword">end</span>
<span class="org-keyword">end</span>

g = <span class="org-type">Greeting</span>.create(text: <span class="org-string">"Greetings and salutations, esteemed world!"</span>)
g.reload
puts g.text <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; "Greetings "</span>
</pre>
<p> This is potentially surprising to users. To improve usability, we can set a validation: </p>
<pre class="src src-ruby">require <span class="org-string">'active_record'</span>

<span class="org-keyword">class</span> <span class="org-type">Greeting</span> &lt; <span class="org-type">ActiveRecord</span>::<span class="org-type">Base</span>
  validates <span class="org-constant">:text</span>, length: { maximum: 10 }
<span class="org-keyword">end</span>

g = <span class="org-type">Greeting</span>.create(text: <span class="org-string">"Greetings and salutations, esteemed world!"</span>)
g.valid? <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; false</span>
g.errors.full_messages <span class="org-comment-delimiter"># </span><span class="org-comment">=&gt; ["Text is too long (maximum is 10 characters)"]</span>
</pre>
<p> Now we have a new problem: duplication of the length cap. The number &#8220;10&#8243; now appears in both the database migrations, and the model validations. </p>
<p> We could extract it out into a constant, although referencing models can be problematic in migrations. But that still doesn&#8217;t account for the (many) cases where we don&#8217;t specify an explicit field limit in the migration, and instead rely on the built-in ActiveRecord defaults for field limits. </p>
<p> Here&#8217;s a solution that uses database reflection to validate a field is within its DB column size limit: </p>
<pre class="src src-ruby">validates <span class="org-constant">:text</span>,
          <span class="org-constant">:length</span> =&gt; {
              <span class="org-constant">:maximum</span> =&gt; columns_hash[<span class="org-string">'text'</span>].limit
          },
</pre>
<p> ActiveRecord provides the <code>columns_hash</code> to get at column metadata gathered from the database backend. In the code above we query it for the <code>limit</code> attribute, and use that as the max field length. By pulling the limit from the DB, we avoid duplication of knowledge. </p>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2011/10/20/drying-up-your-validations-using-db-reflection/feed/</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
		<item>
		<title>Your Code is My Hell</title>
		<link>http://devblog.avdi.org/2011/08/22/your-code-is-my-hell/</link>
		<comments>http://devblog.avdi.org/2011/08/22/your-code-is-my-hell/#comments</comments>
		<pubDate>Mon, 22 Aug 2011 13:30:26 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Rants]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[legacy]]></category>
		<category><![CDATA[maintenance]]></category>
		<category><![CDATA[patterns]]></category>
		<category><![CDATA[ruby. rails]]></category>

		<guid isPermaLink="false">http://avdi.org/devblog/?p=1453</guid>
		<description><![CDATA[It occurred to me recently that my experience as a Rails developer may be somewhat unique. I often get brought in to help preexisting Ruby/Rails projects evolve and mature in a sustainable way. As a result, the vast majority of &#8230; <a href="http://devblog.avdi.org/2011/08/22/your-code-is-my-hell/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>It occurred to me recently that my experience as a Rails developer may be somewhat unique.</p>
<p>I often get brought in to help preexisting Ruby/Rails projects evolve and mature in a sustainable way. As a result, the vast majority of Ruby projects I&#8217;ve worked on have been well-established by the time I arrived. In fact, offhand I can only think of <em>one</em> commercial greenfield Ruby project I&#8217;ve participated in. All the rest have been &#8220;legacy&#8221; from my perspective, in the sense that there was a sizable codebase in production before I showed up. (I&#8217;m not counting personal and internal projects.)</p>
<p>I&#8217;ve realized that in this my experience may be somewhat unusual among Ruby and Rails developers. With its fast churn and startup-heavy community, a lot of Rubyists are working on projects that they started recently.  My work is more in the codebases where the original programmers have moved on.</p>
<h3>Rails&#8217; dirty secret</h3>
<p>In the days before I got paid to write Ruby, I worked on some legacy codebases that had histories spanning multiple decades and 100s of KLOCs. That&#8217;s a lot of opportunity for bad code to accumulate; and in some cases, the accumulations were impressive.</p>
<p>But here&#8217;s the dirty little secret of Rails development:<strong> the messiest, nastiest big-ball-of-mud code I have seen in my entire career has been in Ruby on Rails projects</strong>. I&#8217;ve seen Rails projects that accumulated enough <a href="http://avdi.org/devblog/2011/08/15/the-coding-wasteland/">technical debt and waste</a> in two years to make 10 year-old C/C++ programs look clean and elegant by comparison. And it wasn&#8217;t just one project. I&#8217;ve seen it over and over.</p>
<p>In a way I think this is a testament to the power of the platform. If you&#8217;re getting a 500 error in a Rails app, you can keep adding kludge after kludge and hitting &#8220;reload&#8221; until it works. No need to ever write a test or refactor. In languages and frameworks with a slower turnaround time, this kind of tweak-it-till-it-works workflow is simply impractical. Ruby on Rails has an impressively low barrier to fiddling.</p>
<p>Unfortunately, as a result a lot of projects I come to on have hit what I think of as the <em>productivity crash</em>. At some point the cumulative effect of all those little shortcuts catches up with the development team, and changes that would once have taken a day start taking two weeks as all the dependencies and unintended consequences are sorted out.</p>
<p>As a somewhat ranty aside: this is also the point where, often, original members of the team start moving on to bigger and better things. Meanwhile the crew that inherited the codebase is left to field questions from management about why they can&#8217;t seem to push out changes nearly as fast as the old team. The new team is confronted with the problem of getting the codebase under better test coverage and a little more modularized before they can ramp velocity back up; thus perpetuating the notion among the business types that testing and refactoring just slows things down. And/or that the original team were some kind of wizards.</p>
<p>Okay, rant over.</p>
<h3>But Rails is different!</h3>
<p>Rails developers are sometimes accused of being arrogant and judgmental. I&#8217;m not sure how true this is; I don&#8217;t see it all that much, but maybe I&#8217;m too close to the community and/or arrogant and judgemental myself to be a fair observer.</p>
<p>What I <em>do</em> see is a kind of &#8220;Rails exceptionalism&#8221;.  Remember back in the first dot-com boom, when some economists were saying that no, this time it was <em>different</em>, the Internet had changed the game this time the markets would just keep going up and up? The phenomenon I see is similar in spirit. it&#8217;s a belief, perhaps not fully conscious, that Ruby on Rails development is somehow <em>different</em>, and not subject to the forces affecting other software projects.</p>
<p>Here are a few examples, just to give you an idea of what I&#8217;m talking about:</p>
<ul>
<li>&#8220;Design Patterns are a Java thing. In Ruby you just write code.&#8221;</li>
<li>&#8220;The warnings Ruby produces are dumb; just disable them.&#8221;</li>
<li>&#8220;Sure they aren&#8217;t technically Unit Tests, but isolating objects turned out to be kind of hard and besides nobody else is doing it.&#8221;</li>
<li>&#8220;Monkeypatching is frowned on in other languages, but in Ruby it&#8217;s fine. The downsides almost never materialize.&#8221;</li>
<li>&#8220;Stuff like the <a href="http://avdi.org/devblog/2011/07/05/demeter-its-not-just-a-good-idea-its-the-law/">Law of Demeter</a> isn&#8217;t really as important in Ruby code&#8221;</li>
<li>&#8220;Dividing methods into private and public is for control freaks, you don&#8217;t need it in Ruby&#8221;</li>
<li>&#8220;That&#8217;s only a code smell when it&#8217;s in Java code&#8221;</li>
<li>&#8220;That&#8217;s only a problem in large projects&#8221; (implying that this project will never become large).</li>
</ul>
<p>I also see a fair amount of project or subsystem-level exceptionalism: &#8220;I know they say classes shouldn&#8217;t be this big, but for this class it just makes sense for all of that stuff to be in one place&#8221;.</p>
<h3>Welcome to Lilliput</h3>
<p>The truth is, Ruby on Rails projects <em>are</em> exceptional in a way: <strong>they are really small</strong>. In James Gray&#8217;s terrific keynote at <a href="http://lonestarrubyconf.com/">Lone Star Ruby Conf</a> this past week, he mentioned &#8220;huge projects&#8221; of 40+ KLOC. That gave me a smile, because the first two Rails projects I was ever paid to work on were 50KLOC and 70KLOC, respectively. And while that may seem like a lot of code, that&#8217;s <em>small</em> by industry standards.</p>
<p>There are a few reasons for this. Ruby <em>is</em> a more expressive language than, say, Java, so to some degree Rails projects will always be smaller than equivalent projects in higher-ceremony languages.</p>
<p>It&#8217;s also possible that Rails programmers have embraced the wisdom of breaking systems many, small, intercommunicating apps. I&#8217;d like to believe this, but experience suggests this strategy has seen only spotty uptake.</p>
<p>No, I think the biggest reason for the diminutive nature of Rails apps is also the most obvious: they are all pretty young. It&#8217;s a young framework, and there&#8217;s a lot of churn in this community. A Rails app that lasts three years is ancient.</p>
<p>I think it&#8217;s safe to say that this situation won&#8217;t last. We&#8217;re going to see larger and larger codebases. And here&#8217;s a not-very-daring prediction: a lot of projects are going to hit the very same architectural roadblocks that Lisp, Smalltalk, Pascal, C++, and Java projects hit before them.</p>
<h3>You are not a special snowflake</h3>
<p>It&#8217;s funny reading<a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.84.6924&amp;rep=rep1&amp;type=pdf"> programming literature from the 80s</a>. Dynamic, object-oriented systems navigating the transition from &#8220;small&#8221; to &#8220;medium-sized&#8221;. Sound familiar?</p>
<p>Every revolutionary believes <em>his</em> revolution is special, and won&#8217;t devolve into the partisan bickering and venal bureaucracy that the <em>last</em> revolution led to. And it&#8217;s easy to believe at first. Everyone&#8217;s excited and eager to help; the problems are relatively small; and the marketing drones haven&#8217;t latched onto the movement yet.</p>
<p>The truth is, the problem you are solving probably isn&#8217;t as special as you think it is. And those <a href="http://nicksda.apotomo.de/2011/08/rails-misapprehensions-the-dependency-injection-pattern/">byzantine patterns</a> you thought were a relic of a bygone age were invented by people using <a href="http://www.amazon.com/Smalltalk-Best-Practice-Patterns-Kent/dp/013476904X%3FSubscriptionId%3DAKIAIRXKO4LLU2ACVMRQ%26tag%3Dthlafa-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D013476904X">languages surprisingly similar to Ruby</a>.</p>
<h3>Don&#8217;t panic</h3>
<p>Relax. I&#8217;m not here to tell you that the last few years were all just a lovely dream, and you&#8217;re really still strapped to a chair in the Ministry of UML.</p>
<p>Ruby is still a wonderful language, and the terrific thing about it is that it adapts to large-system design patterns remarkably easily, and with very little ceremony. Dependency Injection? It&#8217;s a one-liner. Object delegation and composition? Piece of cake. Contrary to misconceptions, Ruby doesn&#8217;t obviate solid design patterns and SOLID principles; what it does is make them very easy to express. In fact, the ease of expressing robust architectural styles was what attracted some of us early-adopters to the language in the first place.</p>
<p>Just please, do me a favor: before you tell me that Ruby and Rails doesn&#8217;t need any of this discipline, have a chat with the guy or gal who is still maintaining the <em>first</em> Rails app you worked on.</p>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2011/08/22/your-code-is-my-hell/feed/</wfw:commentRss>
		<slash:comments>125</slash:comments>
		</item>
		<item>
		<title>View Sandboxes for Rails</title>
		<link>http://devblog.avdi.org/2011/08/16/view-sandboxes-for-rails/</link>
		<comments>http://devblog.avdi.org/2011/08/16/view-sandboxes-for-rails/#comments</comments>
		<pubDate>Tue, 16 Aug 2011 21:45:30 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Howto]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[views]]></category>

		<guid isPermaLink="false">http://avdi.org/devblog/?p=1557</guid>
		<description><![CDATA[Designing HTML views is an iterative, interactive process by nature. And anything that slows down the iterations, slows down development. I was working on a form recently where the steps to show it went something like this: Go to the &#8230; <a href="http://devblog.avdi.org/2011/08/16/view-sandboxes-for-rails/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Designing HTML views is an iterative, interactive process by nature. And anything that slows down the iterations, slows down development.</p>
<p>I was working on a form recently where the steps to show it went something like this:</p>
<ol>
<li>Go to the home page</li>
<li>Log in</li>
<li>Click &#8220;New Thingit&#8221; (domain terms have been changed to protect the innocent)</li>
<li>Click &#8220;Edit&#8221; on the Thingit</li>
<li>Click &#8220;Settings&#8221;</li>
<li>Click &#8220;Enable Facebook integration&#8221;</li>
<li>Click &#8220;Save&#8221;</li>
<li>Click &#8220;New Wodget&#8221;</li>
<li>Fill in the &#8220;New Wodget&#8221; form</li>
<li>Click &#8220;Save&#8221;</li>
<li>Click &#8220;Edit&#8221; on the saved wodget</li>
</ol>
<p>All this just to get to the &#8220;Edit&#8221; view of a Wodget. Once you got this far the iterations were a little tighter, but because the forms were presented via AJAX, you couldn&#8217;t just hit F5 to refresh the form after making a view change. You still had to take a few steps:</p>
<ol>
<li>Click &#8220;Cancel&#8221;</li>
<li>Click &#8220;Edit&#8221;</li>
</ol>
<p>All this rigmarole got in the way of quickly seeing the results of view tweaks.</p>
<p>The way I finally decided to address the issue was to create a &#8220;view sandbox&#8221; inside the app where I could easily fiddle with arbitrary views.</p>
<p>I created a routing section for the sandbox which would only be enabled in the &#8220;development&#8221; environment:</p>
<pre name="code" class="ruby">
if Rails.env.development? 
  match 'sandbox/:action' =&gt; 'sandbox' 
end
</pre>
<p>Then I created a very basic SandboxController, with an action dedicated to the view I wanted to play with:</p>
<pre name="code" class="ruby">class SandboxController &lt; ApplicationController
  helper 'thingit'
  helper 'wodget'
  
  layout 'application'

  def edit_wodget
    @thingit = Thingit.find_or_create_by_name("SANDBOX_THINGIT") do |thingit|
      thingit.wodgets.create(...)
    end
    @wodget = @thingit.wodgets.first
  end
end</pre>
<p>The controller action created just enough example model data to keep the view happy.</p>
<p>Finally, I created a <tt>views/sandbox/edit_wodget.html.haml</tt> view corresponding to the action, which simply included the Wodget form partial.</p>
<p>With this in place, I was able to simply navigate to http://localhost:3000/sandbox/edit_wodget to experiment with changes to the view, hitting reload when I wanted to see the effect.</p>
<p>Maybe there&#8217;s a better way to do this, and if there is I hope you&#8217;ll clue me in. I present this in case anyone else has run into a situation where the change-&gt;preview-&gt;change roundtrip is just too darn slow.</p>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2011/08/16/view-sandboxes-for-rails/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Object Oriented Programming Comes to Rails</title>
		<link>http://devblog.avdi.org/2010/12/21/object-oriented-programming-comes-to-rails/</link>
		<comments>http://devblog.avdi.org/2010/12/21/object-oriented-programming-comes-to-rails/#comments</comments>
		<pubDate>Tue, 21 Dec 2010 16:20:37 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Rants]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[controllers]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[views]]></category>

		<guid isPermaLink="false">http://avdi.org/devblog/?p=919</guid>
		<description><![CDATA[decent_exposure helps you program to an interface, rather than an implementation in your Rails controllers. Sharing state via instance variables in controllers promotes close coupling with views. decent_exposure gives you a declarative manner of exposing an interface to the state &#8230; <a href="http://devblog.avdi.org/2010/12/21/object-oriented-programming-comes-to-rails/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<blockquote><p>decent_exposure helps you program to an interface, rather than an implementation in your Rails controllers.</p>
<p>Sharing state via instance variables in controllers promotes close coupling with views. decent_exposure gives you a declarative manner of exposing an interface to the state that controllers contain, thereby decreasing coupling and improving your testability and overall design.</p></blockquote>
<p>via <a href="https://github.com/voxdolo/decent_exposure#">voxdolo/decent_exposure &#8211; GitHub</a>.</p>
<p>It&#8217;s almost as if the program were composed out of some kind of discrete &#8220;objects&#8221;, which only communicated with each other via well-defined &#8220;interfaces&#8221;, and &#8220;encapsulated&#8221; their implementation details. It&#8217;s like a <a href="http://en.wikipedia.org/wiki/Object-oriented_programming">whole new paradigm</a> of programming!</p>
<p>(Just in case anyone misses the joke, this is not an indictment of decent_exposure. It&#8217;s a commentary on just how broken the Rails Controller/View interface is and has always been. I find it ironic that one of the most OO languages in modern use, the notion of a public method interface has to be hacked into Rails controllers with a special syntax.)</p>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2010/12/21/object-oriented-programming-comes-to-rails/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Counters for Partials</title>
		<link>http://devblog.avdi.org/2010/11/26/counters-for-partials/</link>
		<comments>http://devblog.avdi.org/2010/11/26/counters-for-partials/#comments</comments>
		<pubDate>Fri, 26 Nov 2010 17:01:38 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[counters]]></category>
		<category><![CDATA[partials]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[views]]></category>

		<guid isPermaLink="false">http://avdi.org/devblog/?p=862</guid>
		<description><![CDATA[Sometimes I like to number rows using a counter when rendering lists of things: &#60;% @products.each_with_index do &#124;product, i&#124; %&#62; &#60;li class="product_&#60;%= i %&#62;"&#62;&#60;%= product.name %&#62;&#60;/li&#62; &#60;% end %&#62; They are handy for testing, among other things. Of course, if &#8230; <a href="http://devblog.avdi.org/2010/11/26/counters-for-partials/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Sometimes I like to number rows using a counter when rendering lists of things:</p>
<pre>
&lt;% @products.each_with_index do |product, i| %&gt;
  &lt;li class="product_&lt;%= i %&gt;"&gt;&lt;%= product.name %&gt;&lt;/li&gt;
&lt;% end %&gt;
</pre>
<p>They are handy for testing, among other things.</p>
<p>Of course, if the content of the block gets too big I&#8217;ll factor it out into a partial. The idiomatic Rails way of doing this is to factor the block contents out into a partial and then render it with the <code>:collection</code> option:</p>
<pre>
< %= render :partial => "product", :collection => @products %>
</pre>
<p>Which is very clean, except now where do we get our row counter? As it turns out, Rails provides one for us:</p>
<pre>
  &lt;li class="product_&lt;%= product_counter %&gt;"&gt;&lt;%= product.name %&gt;&lt;/li&gt;
</pre>
<p>Note the use of <code>product_counter</code>. Rails takes the name of the partial and adds <code>_counter</code> to it when providing the built-in counter.</p>
<p><del datetime="2010-11-26T17:07:13+00:00">Just be aware that unlike the counter provided by <code>each_with_index</code>, this counter is <em>1-based</em>.</del> I have seen some references to this counter being 1-based, but at least in the version of Rails I am using (2.3.9) the counter is 0-based.</p>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2010/11/26/counters-for-partials/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>The Trifecta of FAIL; or, how to patch Rails 2.0 for Ruby 1.8.7</title>
		<link>http://devblog.avdi.org/2008/08/07/the-trifecta-of-fail-or-how-to-patch-rails-20-for-ruby-187/</link>
		<comments>http://devblog.avdi.org/2008/08/07/the-trifecta-of-fail-or-how-to-patch-rails-20-for-ruby-187/#comments</comments>
		<pubDate>Thu, 07 Aug 2008 17:47:47 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Rants]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[debian]]></category>
		<category><![CDATA[macosx]]></category>
		<category><![CDATA[macports]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://avdi.org/devblog/2008/08/07/the-trifecta-of-fail-or-how-to-patch-rails-20-for-ruby-187/</guid>
		<description><![CDATA[It&#8217;s an oft-stated fact that most disasters result not from a single point of failure but from a combination of failures reinforcing each other. I wouldn&#8217;t term the problem I ran into last Friday a disaster, but it certainly cost &#8230; <a href="http://devblog.avdi.org/2008/08/07/the-trifecta-of-fail-or-how-to-patch-rails-20-for-ruby-187/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>It&#8217;s an oft-stated fact that most disasters result not from a single point of failure but from a combination of failures reinforcing each other.  I wouldn&#8217;t term the problem I ran into last Friday a disaster, but it certainly cost me several hours of time trying to find a workaround.</p>
<h4>Culprit #1: Rails</h4>
<p>Rails&#8217; ActiveSupport added a handy little method called <code>#chars</code> to the <code>String</code> class.  In and of itself this doesn&#8217;t seem like such a bad thing, and a lot of other handy methods in ActiveSupport are built on top of <code>#chars</code>.  However, as we&#8217;ll see, taking advantage of Ruby&#8217;s open classes to extend core types has a way of drawing the unwanted attention from the Law of Unintended Consequences.</p>
<h4>Culprit #2: Ruby</h4>
<p>It&#8217;s not set in stone anywhere, but there&#8217;s a fairly well accepted convention in open source projects that versions are divided into a <dfn>major</dfn> version, a <dfn>minor</dfn> version, and a <dfn>tiny</dfn> or <dfn>patch</dfn> version.  New major versions indicate <span class="caps">API</span>-breaking changes.  A new minor version may introduce new features, but existing code should continue to work as-is.  And a new tiny version indicates that the <span class="caps">API</span> remains fixed; the only difference is that bugs have been fixed and security holes patched.</p>
<p><a href="http://www.ruby-lang.org/en/news/2008/05/31/ruby-1-8-7-has-been-released/">Ruby 1.8.7</a> is a minor release masquerading as a tiny release.  Among the <a href="http://svn.ruby-lang.org/repos/ruby/tags/v1_8_7/NEWS">features backported into 1.8.7 from Ruby 1.9</a> is a new <code>#chars</code> attribute.  Unfortunately, it is incompatible with the Rails 2.0 implementation of <code>#chars</code>.  This, incidentally, is a prime example of one of the subtler ways that patching the core classes can bite you.  Even if you are adding new methods rather than re-writing existing ones, the chances are good that someone else will have the same idea only with a slightly different implementation and semantics.  Bang, incompatibility.</p>
<h4>Culprit #3: <a href="http://www.macports.org/">MacPorts</a></h4>
<p>We have an app which has not yet been ported to Rails 2.1.  This, in itself, would not have been a problem; we can keep running it under Ruby 1.8.6 with Rails 2.0, no problem.  However, I have a nasty habit of trying to keep my software up to date.  So I run <code>sudo port upgrade outdated</code> periodically, and watch all the errors from unmaintained ports go scrolling across my terminal for 24 hours or so.</p>
<p>The last time I did this, one of the ports that did manage to build was Ruby.  Version 1.8.7.  The next time I ran our app, it of course promptly crashed.</p>
<p>This is the point at which I discovered something I hadn&#8217;t realized about MacPorts: <em>it has no downgrade path</em>.  Coming from the world of Debian, Ubuntu, and apt-get, I just expected any package management system to handle the case where the user specifies an older version to be installed.</p>
<p>In fact, there&#8217;s a way to do it in MacPorts, but it&#8217;s <a href="http://journal.bitshaker.com/articles/2007/10/20/install-old-versions-of-ports-using-macports/">painful</a>.</p>
<h4>Fail.</h4>
<p><a href="http://failblog.org/2008/08/07/exit-fail-2/"><img src="http://failblog.wordpress.com/files/2008/07/fail-owned-exit-fail.jpg" alt="fail owned pwned pictures" class="alignnone size-full wp-image-2693" /></a><br />
So there I was with a broken app, no time in the iteration to upgrade it to Rails 2.1, and no easy way to get back to Ruby 1.8.6.  Lame.</p>
<h4>Rescue</h4>
<p>After bitching and moaning on Twitter for awhile, I decided Bob helps those who help themselves, so I took a look at the crash backtrace I was getting.  I traced it back to a line in <code>vendor/rails/activesupport/lib/activesupport/core_ext/string/access.rb</code>:</p>
<pre lang="ruby">
        def first(limit = 1)
          chars[0..(limit - 1)].to_s
        end
</pre>
<p>In the Rails 2.0 version of <code>String#chars</code>, <code>#chars</code> returns an Array or Array-like object which can be subscripted with <code>#[]</code>.  The Ruby 1.8.7 version, by contrast, returns an <code>Enumerable::Enumerator</code>.</p>
<p>&#8220;That&#8217;s easy enough&#8221; thought I, and, fully expecting that patching this one issue would just reveal another incompatibility, and another, and another&#8230;, I changed the code to:</p>
<pre lang="ruby">
        def first(limit = 1)
          chars.to_a[0..(limit - 1)].to_s
        end
</pre>
<p>Lo and behold, the app worked perfectly.</p>
<p>Of course, <span class="caps">YMMV</span>.  But as a quick kludge this one was surprisingly painless.</p>
<h4>Lessons Learned</h4>
<p>Here&#8217;s what I took away from this experience:</p>
<ol>
<li> Be wary of adding methods to core classes.  What could possibly go wrong?  More than you think.</li>
<li> Patch releases should be true patch releases.  It&#8217;s tempting to include a neat new feature  as a bonus &#8212; <br />
    again, what could possibly go wrong?  Resist this urge.</li>
<li> Macs are shiny, but for industrial-strength development support, nothing beats a <a href="http://www.ubuntu.com/">Debian-based system</a> with <span class="caps">APT</span>.</li>
<li>Every now and then taking a clawhammer to vendor code is the shortest (<em>short-term</em>) way from point A to point B.  Personally I prefer to either keep this kind of change local or, if necessary, version it with something like <a href="http://piston.rubyforge.org/">Piston</a>, rather than maintaining it as a monkey-patch.</li>
</ol>
<p>[ad#PostInline] </p>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2008/08/07/the-trifecta-of-fail-or-how-to-patch-rails-20-for-ruby-187/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
	</channel>
</rss>
