<?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>SmartLogic Solutions Blog &#187; ActionPack</title>
	<atom:link href="http://blog.smartlogicsolutions.com/category/actionpack/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.smartlogicsolutions.com</link>
	<description>News and updates from the people at SmartLogic Solutions</description>
	<lastBuildDate>Tue, 30 Nov 2010 21:39:14 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Rails 2.3 Nested Object Forms: I&#8217;m not Crazy about Them</title>
		<link>http://blog.smartlogicsolutions.com/2009/02/24/rails-23-nested-object-forms-im-not-crazy-about-them/</link>
		<comments>http://blog.smartlogicsolutions.com/2009/02/24/rails-23-nested-object-forms-im-not-crazy-about-them/#comments</comments>
		<pubDate>Tue, 24 Feb 2009 15:00:51 +0000</pubDate>
		<dc:creator>John Trupiano</dc:creator>
				<category><![CDATA[ActionPack]]></category>
		<category><![CDATA[ActiveRecord]]></category>
		<category><![CDATA[John Trupiano]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[accepts_nested_attributes_for]]></category>
		<category><![CDATA[nested object forms]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[rails2.3]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://blog.smartlogicsolutions.com/?p=529</guid>
		<description><![CDATA[I&#8217;m a couple of weeks late, but I just finished reviewing Rails 2.3 Nested Object Forms. While a very nice and &#8220;magical&#8221; feature, I&#8217;ve got to admit that I&#8217;m really not that crazy about how it works. Let me be the first to admit that there&#8217;s no one right way to do things. In fact, [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m a couple of weeks late, but I just finished reviewing <a href="http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes">Rails 2.3 Nested Object Forms</a>.  While a very nice and &#8220;magical&#8221; feature, I&#8217;ve got to admit that I&#8217;m really not that crazy about how it works.</p>
<p>Let me be the first to admit that there&#8217;s no one right way to do things.  In fact, I&#8217;m writing this post particularly because I have a few objections to how this functionality is ultimately exposed, and I&#8217;d like to hear arguments from those who disagree.  In other words, let me acknowledge the possibility that I am the misguided one.</p>
<p><span id="more-529"></span></p>
<h3>Review of Nested Object Forms</h3>
<p>As a quick review, we can now prepare our models for nested object mass assignment:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> Person <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span>
&nbsp;
  validates_presence_of <span style="color:#ff3333; font-weight:bold;">:name</span>
&nbsp;
  has_many <span style="color:#ff3333; font-weight:bold;">:children</span>
  accepts_nested_attributes_for <span style="color:#ff3333; font-weight:bold;">:children</span>
    <span style="color:#008000; font-style:italic;"># can also be used on has_one etc.. associations</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">class</span> Child <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span>
  belongs_to <span style="color:#ff3333; font-weight:bold;">:person</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>We specify which nested relationships can be passed in via mass assignment using the <code>accepts_nested_attributes_for()</code> function.  Continuing the example, we can now execute the following to actually save a person and two children at the same time:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">person = Person.<span style="color:#9900CC;">create</span>!<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:name</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'George'</span>, <span style="color:#ff3333; font-weight:bold;">:children_attributes</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#123;</span><span style="color:#996600;">&quot;new_1&quot;</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#123;</span>:name <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'Amy'</span><span style="color:#006600; font-weight:bold;">&#125;</span>, <span style="color:#996600;">&quot;new_2&quot;</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#123;</span>:name <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'Stephanie'</span><span style="color:#006600; font-weight:bold;">&#125;</span><span style="color:#006600; font-weight:bold;">&#125;</span><span style="color:#006600; font-weight:bold;">&#41;</span></pre></div></div>

<p>For full disclosure, my console session from start to finish:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">john-mbp:playground john$ rails <span style="color: #660033;">-v</span>
Rails 2.3.0
john-mbp:playground john$ rails anewapp
...
...
john-mbp:playground john$ <span style="color: #7a0874; font-weight: bold;">cd</span> anewapp<span style="color: #000000; font-weight: bold;">/</span>
john-mbp:anewapp john$ ruby script<span style="color: #000000; font-weight: bold;">/</span>generate model person name:string
...
john-mbp:anewapp john$ ruby script<span style="color: #000000; font-weight: bold;">/</span>generate model child name:string person_id:integer
...
&nbsp;
<span style="color: #666666; font-style: italic;"># edit app/models/person.rb as above</span>
<span style="color: #666666; font-style: italic;"># edit app/models.child.rb as above</span>
&nbsp;
john-mbp:anewapp john$ rake db:migrate
<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000; font-weight: bold;">in</span> <span style="color: #000000; font-weight: bold;">/</span>Users<span style="color: #000000; font-weight: bold;">/</span>john<span style="color: #000000; font-weight: bold;">/</span>playground<span style="color: #000000; font-weight: bold;">/</span>anewapp<span style="color: #7a0874; font-weight: bold;">&#41;</span>
==  CreatePeople: migrating ===================================================
<span style="color: #660033;">--</span> create_table<span style="color: #7a0874; font-weight: bold;">&#40;</span>:people<span style="color: #7a0874; font-weight: bold;">&#41;</span>
   -<span style="color: #000000; font-weight: bold;">&gt;</span> 0.0044s
==  CreatePeople: migrated <span style="color: #7a0874; font-weight: bold;">&#40;</span>0.0046s<span style="color: #7a0874; font-weight: bold;">&#41;</span> ==========================================
&nbsp;
==  CreateChildren: migrating =================================================
<span style="color: #660033;">--</span> create_table<span style="color: #7a0874; font-weight: bold;">&#40;</span>:children<span style="color: #7a0874; font-weight: bold;">&#41;</span>
   -<span style="color: #000000; font-weight: bold;">&gt;</span> 0.0041s
==  CreateChildren: migrated <span style="color: #7a0874; font-weight: bold;">&#40;</span>0.0045s<span style="color: #7a0874; font-weight: bold;">&#41;</span> ========================================
&nbsp;
john-mbp:anewapp john$ ruby script<span style="color: #000000; font-weight: bold;">/</span>console 
Loading development environment <span style="color: #7a0874; font-weight: bold;">&#40;</span>Rails 2.3.0<span style="color: #7a0874; font-weight: bold;">&#41;</span>
<span style="color: #000000; font-weight: bold;">&gt;&gt;</span> person = Person.create<span style="color: #000000; font-weight: bold;">!</span><span style="color: #7a0874; font-weight: bold;">&#40;</span>:name =<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #ff0000;">'George'</span>, :children_attributes =<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #7a0874; font-weight: bold;">&#123;</span><span style="color: #ff0000;">&quot;new_1&quot;</span> =<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #7a0874; font-weight: bold;">&#123;</span>:name =<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #ff0000;">'Amy'</span><span style="color: #7a0874; font-weight: bold;">&#125;</span>, <span style="color: #ff0000;">&quot;new_2&quot;</span> =<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #7a0874; font-weight: bold;">&#123;</span>:name =<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #ff0000;">'Stephanie'</span><span style="color: #7a0874; font-weight: bold;">&#125;</span><span style="color: #7a0874; font-weight: bold;">&#125;</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>
=<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #666666; font-style: italic;">#&lt;Person id: 1, name: &quot;George&quot;, created_at: &quot;2009-02-24 00:49:21&quot;, updated_at: &quot;2009-02-24 00:49:21&quot;&gt;</span>
<span style="color: #000000; font-weight: bold;">&gt;&gt;</span> person.children
=<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #666666; font-style: italic;">#&lt;Child id: 1, name: &quot;Amy&quot;, person_id: 1, created_at: &quot;2009-02-24 00:49:21&quot;, updated_at: &quot;2009-02-24 00:49:21&quot;&gt;, #&lt;Child id: 2, name: &quot;Stephanie&quot;, person_id: 1, created_at: &quot;2009-02-24 00:49:21&quot;, updated_at: &quot;2009-02-24 00:49:21&quot;&gt;]</span>
<span style="color: #000000; font-weight: bold;">&gt;&gt;</span> person.children.size
=<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #000000;">2</span></pre></div></div>

<p>Pretty cool, huh?  I agree.  I think this is fantastic, and with the integration with fields_for, it will really ease a lot of the pain that previously existed with multi-model forms.</p>
<p>This said, I&#8217;ve got a few major bones to pick with how it&#8217;s implemented.</p>
<h3>So What&#8217;s Wrong?</h3>
<p>The primary issue is the fact that this functionality is statically defined (in that you can&#8217;t turn it on/off in specific scenarios).  Why is this an issue?  Well, this is a potentially dangerous functionality to expose everywhere.  Let&#8217;s say that you wanted to take advantage of this new feature to allow administrators to create new accounts that came pre-loaded with a complementary $X credit to their accounts.  So you built a form for admins to fill out that included a <code>fields_for :credits</code> to achieve this single-form implementation of an account with credits.  Now, in order to avail of this, I need to turn on the nested mass assignment.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> Person <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span>
  has_many <span style="color:#ff3333; font-weight:bold;">:credits</span>
  accepts_nested_attributes_for <span style="color:#ff3333; font-weight:bold;">:credits</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">class</span> Credit <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span>
  belongs_to <span style="color:#ff3333; font-weight:bold;">:person</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Wait&#8230;.I&#8217;ve just turned this feature on globally&#8230;..I can&#8217;t just make it available at the controller level?  But isn&#8217;t the controller where I define authorization, who can edit/update/create what?  Precisely.  </p>
<p>So&#8230;.unless I want to go old school and rewrite this all by hand, anytime I want to render a person form (e.g. on an &#8220;Edit My Profile&#8221; link for authenticated users), I need to recall that I&#8217;ve exposed myself to a possible POST data hack (anyone who understands this feature in rails will know how to handcraft the POST data).  This is a fairly ridiculous thing to expect on a project of any nontrivial size.</p>
<p>However, there&#8217;s another way around this issue.  Rather than rewriting all of the multi-model form stuff by hand, I could simply ward off the extra parameters at the controller level, e.g.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> ProfileController <span style="color:#006600; font-weight:bold;">&lt;</span> ApplicationController
  before_filter <span style="color:#ff3333; font-weight:bold;">:remove_nested_params</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> update
    <span style="color:#0066ff; font-weight:bold;">@user</span> = User.<span style="color:#9900CC;">find</span><span style="color:#006600; font-weight:bold;">&#40;</span>current_user.<span style="color:#9900CC;">id</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#0066ff; font-weight:bold;">@user</span>.<span style="color:#9900CC;">update_attributes</span>!<span style="color:#006600; font-weight:bold;">&#40;</span>params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:user</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    redirect_to profile_path
  <span style="color:#9966CC; font-weight:bold;">rescue</span>
    render <span style="color:#ff3333; font-weight:bold;">:action</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'edit'</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  protected
    <span style="color:#9966CC; font-weight:bold;">def</span> remove_nested_params
      params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:user</span><span style="color:#006600; font-weight:bold;">&#93;</span>.<span style="color:#9900CC;">delete</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:credits</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>This works fine in isolation or for a one-off, but again, this is a piece of information that is going to be lost as the project trudges along.  Your team will forget this, and someone is going to forget to &#8220;close the hole&#8221; when adding that new &#8220;quick edit profile&#8221; form that only shows their email address and display name.</p>
<h3>Counter Suggestions</h3>
<p>I would advocate any solution where this behavior was pushed out to controllers.  That said, I don&#8217;t have an airtight procedure planned out.  I will, however, leave you with a few suggestions.</p>
<p>We could implement it around_filter style where the feature is enabled/disabled in an around_filter.  The primary benefit of this approach would be that the majority of the code could probably stay put in ActiveRecord.  We&#8217;d just need to expose hooks to turn it on/off.  Then we could define a controller-level meta-function (e.g. filter_parameter_logging) that could set which actions to apply the behavior on, or which models can be nested.  The syntax could look like:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> ProfileController <span style="color:#006600; font-weight:bold;">&lt;</span> ApplicationController
  allow_nested_assignment_for User, <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:children</span><span style="color:#006600; font-weight:bold;">&#93;</span>, <span style="color:#ff3333; font-weight:bold;">:only</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:update</span><span style="color:#006600; font-weight:bold;">&#93;</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>I&#8217;ll be perfectly honest in that I don&#8217;t love this suggestion.  It incorrectly scopes this authorization tweak to the whole action and any before/after filters that execute in between it (still leaving holes, albeit smaller) as opposed to scoping it directly to the single save! action that we really want to special-case.  If we perhaps exposed a separate save_with_nested() function, then we could isolate the scope down to the specific call.  Needless to say, I doubt this is a course of action rails would take, and it&#8217;s not one I would advocate.</p>
<p>Another alternative would be to keep the functionality as is, but then allow you to override or extend it at the controller level (a la my initial suggestion).  This would allow developers to avail of the benefits in the less-risky areas, and then conditionally avail of them in more risky areas by simply turning it on for specific controller/action combos.  In this case, we&#8217;d define a basic harmless nested assignment baseline in the model, and the more risky nested assignment in those controllers where we wanted to isolate it, e.g.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> Person <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span>
  has_many <span style="color:#ff3333; font-weight:bold;">:children</span>
  has_many <span style="color:#ff3333; font-weight:bold;">:credits</span>
  accepts_nested_attributes_for <span style="color:#ff3333; font-weight:bold;">:children</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">class</span> <span style="color:#6666ff; font-weight:bold;">Admin::PeopleController</span> <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span>
  allow_nested_assignment_for User, <span style="color:#ff3333; font-weight:bold;">:credits</span>, <span style="color:#ff3333; font-weight:bold;">:only</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:create</span>, <span style="color:#ff3333; font-weight:bold;">:update</span><span style="color:#006600; font-weight:bold;">&#93;</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>The primary benefit of an approach like this is that it aligns properly to a developer&#8217;s train of thought.  When you go to add a trivial feature, you don&#8217;t think &#8220;I need to close that gap my unrelated nontrivial feature added for me.&#8221;  However, when you go to add a nontrival feature (like allowing an admin to auto-create credits in their new account form), you do think &#8220;what do I need to differently?&#8221;  In the former case, the developer thinks his new feature is trivial, and doesn&#8217;t expect to have do anything exceptional for it.  In the latter case, the developer thinks his new feature is nontrivial, and will think about it from a &#8220;what extra work does that mean for me?&#8221; standpoint.</p>
<h3>Closing Thoughts</h3>
<p>One of the biggest things that worries me is that no one is talking about this.  The whole community <a href="http://rails.uservoice.com/pages/rails3">requested this feature</a>, and at this point it has received quite a bit of fanfare.  But there are some clear ways to accidentally screw yourself that just aren&#8217;t being mentioned as caveats.</p>
<p>To be entirely blunt, I just feel like this is plugin material at this point.  Though I commend rails for opening up its feature set to a community vote, I fear that we may have misstepped with this feature in particular.  I&#8217;m very curious to hear what others think.  (Be gentle with the flame)</p>
<div id="crp_related"><h3>Related Posts:</h3><ul><li><a href="http://blog.smartlogicsolutions.com/2008/09/10/using-activerecords-to_xml-to-produce-custom-xml-including-deep-level-associations/" rel="bookmark" class="crp_title">Using ActiveRecord&#8217;s to_xml to produce custom xml including deep level associations</a></li><li><a href="http://blog.smartlogicsolutions.com/2009/04/25/reintroducing-sanitize_email-work-with-production-email-without-fear/" rel="bookmark" class="crp_title">Reintroducing sanitize_email | Work with Production Email without Fear</a></li><li><a href="http://blog.smartlogicsolutions.com/2008/06/23/dont-abuse-the-session/" rel="bookmark" class="crp_title">Don&#8217;t Abuse the Session</a></li><li><a href="http://blog.smartlogicsolutions.com/2008/11/19/timecop-freeze-time-in-ruby-for-better-testing/" rel="bookmark" class="crp_title">Timecop: Freeze Time in Ruby for Better Testing</a></li><li><a href="http://blog.smartlogicsolutions.com/2008/06/05/mapresources-and-custom-nested-routes/" rel="bookmark" class="crp_title">map.resources and custom nested routes</a></li><li>Powered by <a href="http://ajaydsouza.com/wordpress/plugins/contextual-related-posts/">Contextual Related Posts</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://blog.smartlogicsolutions.com/2009/02/24/rails-23-nested-object-forms-im-not-crazy-about-them/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
	</channel>
</rss>

