<?xml version="1.0" encoding="UTF-8"?>
<posts>
  <post>
    <id type="integer">92</id>
    <title>Nested attributes, becomes, and foreign keys</title>
    <content>
<p>I love me some nested attributes in Rails, but I&#8217;ve run into some gotchas in the past few days on 2.3.4 that I&#8217;m documenting here in hopes of saving time for some wayward programmer.</p>

<h4>STI + pre-built association + AR#becomes = FAIL</h4>

<p>Ground rules: it&#8217;s common to pre-build associated objects in the controller in order to pass a model to the form helper, like so:</p>

<pre><code># In the controller

def new
  @child = Child.new
  @child.build_widget
end
</code></pre>

<p><code>AR#becomes</code> is helpful when building forms for STI models, since it <em>casts</em> your child object as the parent class, thereby generating a route that points to the parent controller. In the common case where you have a form with a drop-down that lets you select the child class, this is the route you want:</p>

<pre><code># In the view

- form_for @child.becomes(Parent) do |form|    # Posts to parents_path
  - form.fields_for :widget do |widget_form|
</code></pre>

<p>But behold, in this case, the built Widget object will not be available to the form helper in the <code>fields_for</code> call. Instead, <code>nil</code> is passed and you&#8217;re certain to witness a <code>NoMethodError</code>. What happened?</p>

<p>It turns out that <code>AR#becomes</code> creates a new object of the specified class and clones three ivars to maintain state &#8211; <code>@attributes</code>, <code>@attributes_cache</code>, and <code>@new_record</code>. When you call <code>#build_widget</code> on the child object, however, it creates a new ivar, <code>@widget</code>, to hold the new record. Since this doesn&#8217;t get copied over, our associated Widget never makes it to the form helper and kablooey.</p>

<p>The workaround? Coerce the correct route another way:</p>

<pre><code>- form_for :parent, @child do |form|    # Also posts to parents_path
</code></pre>

<p>Not as expressive, but it gets the job done. Finally, I&#8217;m not positively sure that this is a bug in Rails. That is, it may be too dangerous to copy over every ivar to the new object, and it may be too cumbersome to try and detect the <em>safe</em> ones.</p>

<h4>belongs_to and nullifying the foreign key</h4>

<p>Say you have the following:</p>

<pre><code>class Book &lt; ActiveRecord::Base

  belongs_to :person

  accepts_nested_attributes_for :person, :allow_destroy =&gt; true
</code></pre>

<p>When you delete the nested object:</p>

<pre><code>book.person_attributes = {:id =&gt; book.person_id, :_delete =&gt; true}
book.save
</code></pre>

<p>The person <em>will</em> be deleted, but the <code>person_id</code> on the book will retain the now-invalid id. This is especially problematic if foreign key constraints are involved and the database will certainly yell at you for breaking integrity.</p>

<p>The solution is creating the mirrored association and specifying that the foreign key should be nullified:</p>

<pre><code>class Person &lt; ActiveRecord::Base

  has_many :books, :dependent =&gt; :nullify
</code></pre>

<p>Now, <code>book.save</code> updates the Book first, setting <code>person_id</code> to nil, before it destroys the Person. Again, this doesn&#8217;t <em>seem</em> like a bug, since the mirrored association appears to be the <em>Right Thing To Do</em> anyway. Then again, maybe I&#8217;ll change my mind and fire off a couple of Lighthouse tickets.</p>    </content>
    <published-on type="datetime">2009-12-04T04:38:13Z</published-on>
    <created-on type="datetime">2009-12-04T04:38:13Z</created-on>
    <updated-on type="datetime">2009-12-04T04:38:13Z</updated-on>
  </post>
  <post>
    <id type="integer">91</id>
    <title>Quick bundler notes</title>
    <content>
<p>I have high hopes for <a href="http://github.com/wycats/bundler/tree/master">Yehuda&#8217;s bundler</a> &#8211; config.gems has always been troublesome and I like the merb approach of simply creating a local gem repo just for your app.  With that, a few observations after diving in&#8230;</p>

<p>It doesn&#8217;t like <code>source "http://gems.github.com"</code> for now.  There&#8217;s an <a href="http://github.com/wycats/bundler/issues#issue/18">open issue</a> for it.  </p>

<p>Dropping the suggested require at the top of <code>boot.rb</code> does the trick for Rails, but it doesn&#8217;t make use of other environments, if you have them defined (that&#8217;s the <code>:only =&gt; [:test, :cucumber]</code> bit).  </p>

<p>A quick fix at the top of <code>Rakefile</code>:</p>

<pre>
  ENV['BUNDLER_ENVIRONMENT'] =
    case ARGV[0]
    when /^features/    then "cucumber"
    when /^(spec|test)/ then "test"
    else "default"
    end

  require(File.join(File.dirname(__FILE__), 'config', 'boot'))
</pre>

<p>Then in <code>boot.rb</code>, subsititute the initial require with:</p>

<pre>
  require File.expand_path(
      File.join(
        File.dirname(__FILE__), 
        '..', 'vendor', 'gems', 'environments', 
        ENV[ BUNDLER_ENVIRONMENT'] || "default"
      )
  )
</pre>

<p>At the very least, this loads the test and cucumber environments when running <code>rake spec</code> and <code>rake features</code> respectively.</p>

<p>Finally, if you see these:</p>

<pre>
config.gem: Unpacked gem cache in vendor/gems has no specification \
      file. Run 'rake gems:refresh_specs' to fix this.
config.gem: Unpacked gem cache in vendor/gems not in a versioned \
      directory. Giving up.
</pre>

<p>You can add the following line to <code>config/environment.rb</code> to suppress these warnings:</p>

<pre>Rails::VendorGemSourceIndex.silence_spec_warnings = true</pre>

<p><strong>Update on August 25:</strong> looks like everything I&#8217;ve mentioned above has been resolved as of the 0.5.0.pre release.  Awesome.  I wonder if this is being folded into Rails 3?</p>    </content>
    <published-on type="datetime">2009-08-17T04:58:56Z</published-on>
    <created-on type="datetime">2009-08-17T04:58:56Z</created-on>
    <updated-on type="datetime">2009-08-26T03:59:32Z</updated-on>
  </post>
  <post>
    <id type="integer">90</id>
    <title>A name for your number</title>
    <content>
<p><a href="http://www.google.com/voice">Google Voice</a> is here.  When it comes time to pick your Google number, they provide a search tool that scopes by area or ZIP code as well as strings of letters and numbers.  I quickly tried my name, to no avail, and then began searching for that elusive ten-character phrase.  </p>

<p>To that end, <a href="http://gist.github.com/149419">here&#8217;s a quick script</a> that will let you input T9 numbers and spit out matching words.  Give it something like:</p>

<pre><code>$ ruby t9.rb 347 10
</code></pre>

<p>And it&#8217;ll spit out words that begin with the T9 numbers &#8220;347&#8221;, up to ten characters in length.  Good luck.</p>    </content>
    <published-on type="datetime">2009-07-18T05:21:29Z</published-on>
    <created-on type="datetime">2009-07-18T05:21:29Z</created-on>
    <updated-on type="datetime">2009-07-18T05:21:29Z</updated-on>
  </post>
  <post>
    <id type="integer">89</id>
    <title>Toddler interface</title>
    <content>
<p><em>My iPhone is not a toy.</em>  I&#8217;ve tried conveying as much to my toddler, but she still pines after it and has managed to grab it on a few occasions.  Thankfully, she doesn&#8217;t fly off in a tear, but rather, plops rights down, fully absorbed.</p>

<p>It&#8217;s fun to watch her with the phone &#8211; tapping around, pressing the buttons, calling people &#8211; and now, she really seems to <em>get</em> the interface.  She scrolls lists, browses photos, and taps buttons expecting results.  Any doubts I have about the interface are vanquished by watching her use the phone.  It just comes to her so <em>naturally</em>.</p>

<p>Of course, now she touches other displays and gets frustrated by the lack of interaction.  Maybe soon all screens will be touchscreens, but engineers and interface designers better hurry up, lest they fail to meet the expectations of a different generation.</p>    </content>
    <published-on type="datetime">2009-06-15T04:35:05Z</published-on>
    <created-on type="datetime">2009-06-15T04:35:05Z</created-on>
    <updated-on type="datetime">2009-06-15T04:35:05Z</updated-on>
  </post>
  <post>
    <id type="integer">88</id>
    <title>Emacs</title>
    <content>
<p>There&#8217;s nothing like late night improvements to your editor.  Tonight&#8217;s finds:</p>

<ul>
<li><a href="http://github.com/topfunky/emacs-starter-kit/tree/master">Grosenbach&#8217;s emacs starter-kit, Carbon Emacs-inclined</a></li>
<li><a href="http://www.vimeo.com/1013263">ido-mode and more</a></li>
<li><a href="http://tromey.com/elpa/">ELPA</a></li>
</ul>

<p>And for those so interested, my <code>.emacs</code> file can be found on <a href="http://github.com/daveyeu/suitcase/blob/master/emacs">github</a>.</p>    </content>
    <published-on type="datetime">2009-03-04T07:48:38Z</published-on>
    <created-on type="datetime">2009-03-04T07:48:38Z</created-on>
    <updated-on type="datetime">2009-03-04T07:48:38Z</updated-on>
  </post>
</posts>
