<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>activerecord/lib/active_record/autosave_association.rb</filename>
    </added>
    <added>
      <filename>activerecord/lib/active_record/nested_attributes.rb</filename>
    </added>
    <added>
      <filename>activerecord/test/cases/autosave_association_test.rb</filename>
    </added>
    <added>
      <filename>activerecord/test/cases/nested_attributes_test.rb</filename>
    </added>
    <added>
      <filename>activerecord/test/models/bird.rb</filename>
    </added>
    <added>
      <filename>activerecord/test/models/ship_part.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,5 +1,17 @@
 *2.3.0 [Edge]*
 
+* Make the form_for and fields_for helpers support the new Active Record nested update options.  #1202 [Eloy Duran]
+
+		&lt;% form_for @person do |person_form| %&gt;
+		  ...
+		  &lt;% person_form.fields_for :projects do |project_fields| %&gt;
+		    &lt;% if project_fields.object.active? %&gt;
+		      Name: &lt;%= project_fields.text_field :name %&gt;
+		    &lt;% end %&gt;
+		  &lt;% end %&gt;
+		&lt;% end %&gt;
+
+
 * Added grouped_options_for_select helper method for wrapping option tags in optgroups. #977 [Jon Crawford]
 
 * Implement HTTP Digest authentication. #1230 [Gregg Kellogg, Pratik Naik] Example :</diff>
      <filename>actionpack/CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -269,10 +269,12 @@ module ActionView
         options[:url] ||= polymorphic_path(object_or_array)
       end
 
-      # Creates a scope around a specific model object like form_for, but doesn't create the form tags themselves. This makes
-      # fields_for suitable for specifying additional model objects in the same form:
+      # Creates a scope around a specific model object like form_for, but
+      # doesn't create the form tags themselves. This makes fields_for suitable
+      # for specifying additional model objects in the same form.
+      #
+      # === Generic Examples
       #
-      # ==== Examples
       #   &lt;% form_for @person, :url =&gt; { :action =&gt; &quot;update&quot; } do |person_form| %&gt;
       #     First name: &lt;%= person_form.text_field :first_name %&gt;
       #     Last name : &lt;%= person_form.text_field :last_name %&gt;
@@ -282,20 +284,166 @@ module ActionView
       #     &lt;% end %&gt;
       #   &lt;% end %&gt;
       #
-      # ...or if you have an object that needs to be represented as a different parameter, like a Client that acts as a Person:
+      # ...or if you have an object that needs to be represented as a different
+      # parameter, like a Client that acts as a Person:
       #
       #   &lt;% fields_for :person, @client do |permission_fields| %&gt;
       #     Admin?: &lt;%= permission_fields.check_box :admin %&gt;
       #   &lt;% end %&gt;
       #
-      # ...or if you don't have an object, just a name of the parameter
+      # ...or if you don't have an object, just a name of the parameter:
       #
       #   &lt;% fields_for :person do |permission_fields| %&gt;
       #     Admin?: &lt;%= permission_fields.check_box :admin %&gt;
       #   &lt;% end %&gt;
       #
-      # Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base,
-      # like FormOptionHelper#collection_select and DateHelper#datetime_select.
+      # Note: This also works for the methods in FormOptionHelper and
+      # DateHelper that are designed to work with an object as base, like
+      # FormOptionHelper#collection_select and DateHelper#datetime_select.
+      #
+      # === Nested Attributes Examples
+      #
+      # When the object belonging to the current scope has a nested attribute
+      # writer for a certain attribute, fields_for will yield a new scope
+      # for that attribute. This allows you to create forms that set or change
+      # the attributes of a parent object and its associations in one go.
+      #
+      # Nested attribute writers are normal setter methods named after an
+      # association. The most common way of defining these writers is either
+      # with +accepts_nested_attributes_for+ in a model definition or by
+      # defining a method with the proper name. For example: the attribute
+      # writer for the association &lt;tt&gt;:address&lt;/tt&gt; is called
+      # &lt;tt&gt;address_attributes=&lt;/tt&gt;.
+      #
+      # Whether a one-to-one or one-to-many style form builder will be yielded
+      # depends on whether the normal reader method returns a _single_ object
+      # or an _array_ of objects.
+      #
+      # ==== One-to-one
+      #
+      # Consider a Person class which returns a _single_ Address from the
+      # &lt;tt&gt;address&lt;/tt&gt; reader method and responds to the
+      # &lt;tt&gt;address_attributes=&lt;/tt&gt; writer method:
+      #
+      #   class Person
+      #     def address
+      #       @address
+      #     end
+      #
+      #     def address_attributes=(attributes)
+      #       # Process the attributes hash
+      #     end
+      #   end
+      #
+      # This model can now be used with a nested fields_for, like so:
+      #
+      #   &lt;% form_for @person, :url =&gt; { :action =&gt; &quot;update&quot; } do |person_form| %&gt;
+      #     ...
+      #     &lt;% person_form.fields_for :address do |address_fields| %&gt;
+      #       Street  : &lt;%= address_fields.text_field :street %&gt;
+      #       Zip code: &lt;%= address_fields.text_field :zip_code %&gt;
+      #     &lt;% end %&gt;
+      #   &lt;% end %&gt;
+      #
+      # When address is already an association on a Person you can use
+      # +accepts_nested_attributes_for+ to define the writer method for you:
+      #
+      #   class Person &lt; ActiveRecord::Base
+      #     has_one :address
+      #     accepts_nested_attributes_for :address
+      #   end
+      #
+      # If you want to destroy the associated model through the form, you have
+      # to enable it first using the &lt;tt&gt;:allow_destroy&lt;/tt&gt; option for
+      # +accepts_nested_attributes_for+:
+      #
+      #   class Person &lt; ActiveRecord::Base
+      #     has_one :address
+      #     accepts_nested_attributes_for :address, :allow_destroy =&gt; true
+      #   end
+      #
+      # Now, when you use a form element with the &lt;tt&gt;_delete&lt;/tt&gt; parameter,
+      # with a value that evaluates to +true+, you will destroy the associated
+      # model (eg. 1, '1', true, or 'true'):
+      #
+      #   &lt;% form_for @person, :url =&gt; { :action =&gt; &quot;update&quot; } do |person_form| %&gt;
+      #     ...
+      #     &lt;% person_form.fields_for :address do |address_fields| %&gt;
+      #       ...
+      #       Delete: &lt;%= address_fields.check_box :_delete %&gt;
+      #     &lt;% end %&gt;
+      #   &lt;% end %&gt;
+      #
+      # ==== One-to-many
+      #
+      # Consider a Person class which returns an _array_ of Project instances
+      # from the &lt;tt&gt;projects&lt;/tt&gt; reader method and responds to the
+      # &lt;tt&gt;projects_attributes=&lt;/tt&gt; writer method:
+      #
+      #   class Person
+      #     def projects
+      #       [@project1, @project2]
+      #     end
+      #
+      #     def projects_attributes=(attributes)
+      #       # Process the attributes hash
+      #     end
+      #   end
+      #
+      # This model can now be used with a nested fields_for. The block given to
+      # the nested fields_for call will be repeated for each instance in the
+      # collection:
+      #
+      #   &lt;% form_for @person, :url =&gt; { :action =&gt; &quot;update&quot; } do |person_form| %&gt;
+      #     ...
+      #     &lt;% person_form.fields_for :projects do |project_fields| %&gt;
+      #       &lt;% if project_fields.object.active? %&gt;
+      #         Name: &lt;%= project_fields.text_field :name %&gt;
+      #       &lt;% end %&gt;
+      #     &lt;% end %&gt;
+      #   &lt;% end %&gt;
+      #
+      # It's also possible to specify the instance to be used:
+      #
+      #   &lt;% form_for @person, :url =&gt; { :action =&gt; &quot;update&quot; } do |person_form| %&gt;
+      #     ...
+      #     &lt;% @person.projects.each do |project| %&gt;
+      #       &lt;% if project.active? %&gt;
+      #         &lt;% person_form.fields_for :projects, project do |project_fields| %&gt;
+      #           Name: &lt;%= project_fields.text_field :name %&gt;
+      #         &lt;% end %&gt;
+      #       &lt;% end %&gt;
+      #     &lt;% end %&gt;
+      #   &lt;% end %&gt;
+      #
+      # When projects is already an association on Person you can use
+      # +accepts_nested_attributes_for+ to define the writer method for you:
+      #
+      #   class Person &lt; ActiveRecord::Base
+      #     has_many :projects
+      #     accepts_nested_attributes_for :projects
+      #   end
+      #
+      # If you want to destroy any of the associated models through the
+      # form, you have to enable it first using the &lt;tt&gt;:allow_destroy&lt;/tt&gt;
+      # option for +accepts_nested_attributes_for+:
+      #
+      #   class Person &lt; ActiveRecord::Base
+      #     has_many :projects
+      #     accepts_nested_attributes_for :projects, :allow_destroy =&gt; true
+      #   end
+      #
+      # This will allow you to specify which models to destroy in the
+      # attributes hash by adding a form element for the &lt;tt&gt;_delete&lt;/tt&gt;
+      # parameter with a value that evaluates to +true+
+      # (eg. 1, '1', true, or 'true'):
+      #
+      #   &lt;% form_for @person, :url =&gt; { :action =&gt; &quot;update&quot; } do |person_form| %&gt;
+      #     ...
+      #     &lt;% person_form.fields_for :projects do |project_fields| %&gt;
+      #       Delete: &lt;%= project_fields.check_box :_delete %&gt;
+      #     &lt;% end %&gt;
+      #   &lt;% end %&gt;
       def fields_for(record_or_name_or_array, *args, &amp;block)
         raise ArgumentError, &quot;Missing block&quot; unless block_given?
         options = args.extract_options!
@@ -760,7 +908,11 @@ module ActionView
 
         case record_or_name_or_array
         when String, Symbol
-          name = &quot;#{object_name}#{index}[#{record_or_name_or_array}]&quot;
+          if nested_attributes_association?(record_or_name_or_array)
+            return fields_for_with_nested_attributes(record_or_name_or_array, args, block)
+          else
+            name = &quot;#{object_name}#{index}[#{record_or_name_or_array}]&quot;
+          end
         when Array
           object = record_or_name_or_array.last
           name = &quot;#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]&quot;
@@ -802,6 +954,32 @@ module ActionView
         def objectify_options(options)
           @default_options.merge(options.merge(:object =&gt; @object))
         end
+
+        def nested_attributes_association?(association_name)
+          @object.respond_to?(&quot;#{association_name}_attributes=&quot;)
+        end
+
+        def fields_for_with_nested_attributes(association_name, args, block)
+          name = &quot;#{object_name}[#{association_name}_attributes]&quot;
+          association = @object.send(association_name)
+
+          if association.is_a?(Array)
+            children = args.first.respond_to?(:new_record?) ? [args.first] : association
+
+            children.map do |child|
+              child_name = &quot;#{name}[#{ child.new_record? ? new_child_id : child.id }]&quot;
+              @template.fields_for(child_name, child, *args, &amp;block)
+            end.join
+          else
+            @template.fields_for(name, association, *args, &amp;block)
+          end
+        end
+
+        def new_child_id
+          value = (@child_counter ||= 1)
+          @child_counter += 1
+          &quot;new_#{value}&quot;
+        end
     end
   end
 
@@ -809,4 +987,4 @@ module ActionView
     cattr_accessor :default_form_builder
     self.default_form_builder = ::ActionView::Helpers::FormBuilder
   end
-end
+end
\ No newline at end of file</diff>
      <filename>actionpack/lib/action_view/helpers/form_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -15,21 +15,31 @@ silence_warnings do
     def new_record?
       @new_record
     end
+
+    attr_accessor :author
+    def author_attributes=(attributes); end
+
+    attr_accessor :comments
+    def comments_attributes=(attributes); end
   end
 
   class Comment
     attr_reader :id
     attr_reader :post_id
+    def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end
     def save; @id = 1; @post_id = 1 end
     def new_record?; @id.nil? end
     def to_param; @id; end
     def name
-      @id.nil? ? 'new comment' : &quot;comment ##{@id}&quot;
+      @id.nil? ? &quot;new #{self.class.name.downcase}&quot; : &quot;#{self.class.name.downcase} ##{@id}&quot;
     end
   end
-end
 
-class Comment::Nested &lt; Comment; end
+  class Author &lt; Comment
+    attr_accessor :post
+    def post_attributes=(attributes); end
+  end
+end
 
 class FormHelperTest &lt; ActionView::TestCase
   tests ActionView::Helpers::FormHelper
@@ -479,7 +489,7 @@ class FormHelperTest &lt; ActionView::TestCase
     assert_dom_equal expected, output_buffer
   end
 
-  def test_nested_fields_for_with_index
+  def test_form_for_with_index_and_nested_fields_for
     form_for(:post, @post, :index =&gt; 1) do |f|
       f.fields_for(:comment, @post) do |c|
         concat c.text_field(:title)
@@ -558,6 +568,127 @@ class FormHelperTest &lt; ActionView::TestCase
     assert_dom_equal expected, output_buffer
   end
 
+  def test_nested_fields_for_with_a_new_record_on_a_nested_attributes_one_to_one_association
+    @post.author = Author.new
+
+    form_for(:post, @post) do |f|
+      concat f.text_field(:title)
+      f.fields_for(:author) do |af|
+        concat af.text_field(:name)
+      end
+    end
+
+    expected = '&lt;form action=&quot;http://www.example.com&quot; method=&quot;post&quot;&gt;' +
+               '&lt;input name=&quot;post[title]&quot; size=&quot;30&quot; type=&quot;text&quot; id=&quot;post_title&quot; value=&quot;Hello World&quot; /&gt;' +
+               '&lt;input id=&quot;post_author_attributes_name&quot; name=&quot;post[author_attributes][name]&quot; size=&quot;30&quot; type=&quot;text&quot; value=&quot;new author&quot; /&gt;' +
+               '&lt;/form&gt;'
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for_with_an_existing_record_on_a_nested_attributes_one_to_one_association
+    @post.author = Author.new(321)
+
+    form_for(:post, @post) do |f|
+      concat f.text_field(:title)
+      f.fields_for(:author) do |af|
+        concat af.text_field(:name)
+      end
+    end
+
+    expected = '&lt;form action=&quot;http://www.example.com&quot; method=&quot;post&quot;&gt;' +
+               '&lt;input name=&quot;post[title]&quot; size=&quot;30&quot; type=&quot;text&quot; id=&quot;post_title&quot; value=&quot;Hello World&quot; /&gt;' +
+               '&lt;input id=&quot;post_author_attributes_name&quot; name=&quot;post[author_attributes][name]&quot; size=&quot;30&quot; type=&quot;text&quot; value=&quot;author #321&quot; /&gt;' +
+               '&lt;/form&gt;'
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for_with_existing_records_on_a_nested_attributes_collection_association
+    @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+
+    form_for(:post, @post) do |f|
+      concat f.text_field(:title)
+      @post.comments.each do |comment|
+        f.fields_for(:comments, comment) do |cf|
+          concat cf.text_field(:name)
+        end
+      end
+    end
+
+    expected = '&lt;form action=&quot;http://www.example.com&quot; method=&quot;post&quot;&gt;' +
+               '&lt;input name=&quot;post[title]&quot; size=&quot;30&quot; type=&quot;text&quot; id=&quot;post_title&quot; value=&quot;Hello World&quot; /&gt;' +
+               '&lt;input id=&quot;post_comments_attributes_1_name&quot; name=&quot;post[comments_attributes][1][name]&quot; size=&quot;30&quot; type=&quot;text&quot; value=&quot;comment #1&quot; /&gt;' +
+               '&lt;input id=&quot;post_comments_attributes_2_name&quot; name=&quot;post[comments_attributes][2][name]&quot; size=&quot;30&quot; type=&quot;text&quot; value=&quot;comment #2&quot; /&gt;' +
+               '&lt;/form&gt;'
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for_with_new_records_on_a_nested_attributes_collection_association
+    @post.comments = [Comment.new, Comment.new]
+
+    form_for(:post, @post) do |f|
+      concat f.text_field(:title)
+      @post.comments.each do |comment|
+        f.fields_for(:comments, comment) do |cf|
+          concat cf.text_field(:name)
+        end
+      end
+    end
+
+    expected = '&lt;form action=&quot;http://www.example.com&quot; method=&quot;post&quot;&gt;' +
+               '&lt;input name=&quot;post[title]&quot; size=&quot;30&quot; type=&quot;text&quot; id=&quot;post_title&quot; value=&quot;Hello World&quot; /&gt;' +
+               '&lt;input id=&quot;post_comments_attributes_new_1_name&quot; name=&quot;post[comments_attributes][new_1][name]&quot; size=&quot;30&quot; type=&quot;text&quot; value=&quot;new comment&quot; /&gt;' +
+               '&lt;input id=&quot;post_comments_attributes_new_2_name&quot; name=&quot;post[comments_attributes][new_2][name]&quot; size=&quot;30&quot; type=&quot;text&quot; value=&quot;new comment&quot; /&gt;' +
+               '&lt;/form&gt;'
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for_with_existing_and_new_records_on_a_nested_attributes_collection_association
+    @post.comments = [Comment.new(321), Comment.new]
+
+    form_for(:post, @post) do |f|
+      concat f.text_field(:title)
+      @post.comments.each do |comment|
+        f.fields_for(:comments, comment) do |cf|
+          concat cf.text_field(:name)
+        end
+      end
+    end
+
+    expected = '&lt;form action=&quot;http://www.example.com&quot; method=&quot;post&quot;&gt;' +
+               '&lt;input name=&quot;post[title]&quot; size=&quot;30&quot; type=&quot;text&quot; id=&quot;post_title&quot; value=&quot;Hello World&quot; /&gt;' +
+               '&lt;input id=&quot;post_comments_attributes_321_name&quot; name=&quot;post[comments_attributes][321][name]&quot; size=&quot;30&quot; type=&quot;text&quot; value=&quot;comment #321&quot; /&gt;' +
+               '&lt;input id=&quot;post_comments_attributes_new_1_name&quot; name=&quot;post[comments_attributes][new_1][name]&quot; size=&quot;30&quot; type=&quot;text&quot; value=&quot;new comment&quot; /&gt;' +
+               '&lt;/form&gt;'
+
+    assert_dom_equal expected, output_buffer
+  end
+
+  def test_nested_fields_for_on_a_nested_attributes_collection_association_yields_only_builder
+    @post.comments = [Comment.new(321), Comment.new]
+    yielded_comments = []
+
+    form_for(:post, @post) do |f|
+      concat f.text_field(:title)
+      f.fields_for(:comments) do |cf|
+        concat cf.text_field(:name)
+        yielded_comments &lt;&lt; cf.object
+      end
+    end
+
+    expected = '&lt;form action=&quot;http://www.example.com&quot; method=&quot;post&quot;&gt;' +
+               '&lt;input name=&quot;post[title]&quot; size=&quot;30&quot; type=&quot;text&quot; id=&quot;post_title&quot; value=&quot;Hello World&quot; /&gt;' +
+               '&lt;input id=&quot;post_comments_attributes_321_name&quot; name=&quot;post[comments_attributes][321][name]&quot; size=&quot;30&quot; type=&quot;text&quot; value=&quot;comment #321&quot; /&gt;' +
+               '&lt;input id=&quot;post_comments_attributes_new_1_name&quot; name=&quot;post[comments_attributes][new_1][name]&quot; size=&quot;30&quot; type=&quot;text&quot; value=&quot;new comment&quot; /&gt;' +
+               '&lt;/form&gt;'
+
+    assert_dom_equal expected, output_buffer
+    assert_equal yielded_comments, @post.comments
+  end
+
   def test_fields_for
     fields_for(:post, @post) do |f|
       concat f.text_field(:title)
@@ -974,4 +1105,4 @@ class FormHelperTest &lt; ActionView::TestCase
     def protect_against_forgery?
       false
     end
-end
+end
\ No newline at end of file</diff>
      <filename>actionpack/test/template/form_helper_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,14 @@
 *2.3.0/3.0*
 
+* Add Support for updating deeply nested models from a single form. #1202 [Eloy Duran]
+
+	class Book &lt; ActiveRecord::Base
+	  has_one :author
+	  has_many :pages
+
+	  accepts_nested_attributes_for :author, :pages
+	end
+
 * Make after_save callbacks fire only if the record was successfully saved.  #1735 [Michael Lovitt]
 
   Previously the callbacks would fire if a before_save cancelled saving.</diff>
      <filename>activerecord/CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -46,6 +46,7 @@ module ActiveRecord
   autoload :AssociationPreload, 'active_record/association_preload'
   autoload :Associations, 'active_record/associations'
   autoload :AttributeMethods, 'active_record/attribute_methods'
+  autoload :AutosaveAssociation, 'active_record/autosave_association'
   autoload :Base, 'active_record/base'
   autoload :Calculations, 'active_record/calculations'
   autoload :Callbacks, 'active_record/callbacks'
@@ -55,6 +56,7 @@ module ActiveRecord
   autoload :Migration, 'active_record/migration'
   autoload :Migrator, 'active_record/migration'
   autoload :NamedScope, 'active_record/named_scope'
+  autoload :NestedAttributes, 'active_record/nested_attributes'
   autoload :Observing, 'active_record/observer'
   autoload :QueryCache, 'active_record/query_cache'
   autoload :Reflection, 'active_record/reflection'</diff>
      <filename>activerecord/lib/active_record.rb</filename>
    </modified>
    <modified>
      <diff>@@ -88,6 +88,18 @@ module ActiveRecord
       end unless self.new_record?
     end
 
+    private
+      # Gets the specified association instance if it responds to :loaded?, nil otherwise.
+      def association_instance_get(name)
+        association = instance_variable_get(&quot;@#{name}&quot;)
+        association if association.respond_to?(:loaded?)
+      end
+
+      # Set the specified association instance.
+      def association_instance_set(name, association)
+        instance_variable_set(&quot;@#{name}&quot;, association)
+      end
+
     # Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like
     # &quot;Project has one Project Manager&quot; or &quot;Project belongs to a Portfolio&quot;. Each macro adds a number of methods to the class which are
     # specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own &lt;tt&gt;attr*&lt;/tt&gt;
@@ -256,6 +268,10 @@ module ActiveRecord
     # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be
     # aware of, mostly involving the saving of associated objects.
     #
+    # Unless you enable the :autosave option on a &lt;tt&gt;has_one&lt;/tt&gt;, &lt;tt&gt;belongs_to&lt;/tt&gt;,
+    # &lt;tt&gt;has_many&lt;/tt&gt;, or &lt;tt&gt;has_and_belongs_to_many&lt;/tt&gt; association,
+    # in which case the members are always saved.
+    #
     # === One-to-one associations
     #
     # * Assigning an object to a +has_one+ association automatically saves that object and the object being replaced (if there is one), in
@@ -752,6 +768,9 @@ module ActiveRecord
       #   If true, all the associated objects are readonly through the association.
       # [:validate]
       #   If false, don't validate the associated objects when saving the parent object. true by default.
+      # [:autosave]
+      #   If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default.
+      #
       # Option examples:
       #   has_many :comments, :order =&gt; &quot;posted_on&quot;
       #   has_many :comments, :include =&gt; :author
@@ -865,6 +884,8 @@ module ActiveRecord
       #   If true, the associated object is readonly through the association.
       # [:validate]
       #   If false, don't validate the associated object when saving the parent object. +false+ by default.
+      # [:autosave]
+      #   If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
       #
       # Option examples:
       #   has_one :credit_card, :dependent =&gt; :destroy  # destroys the associated credit card
@@ -882,13 +903,10 @@ module ActiveRecord
         else
           reflection = create_has_one_reflection(association_id, options)
 
-          ivar = &quot;@#{reflection.name}&quot;
-
           method_name = &quot;has_one_after_save_for_#{reflection.name}&quot;.to_sym
           define_method(method_name) do
-            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
-
-            if !association.nil? &amp;&amp; (new_record? || association.new_record? || association[reflection.primary_key_name] != id)
+            association = association_instance_get(reflection.name)
+            if association &amp;&amp; (new_record? || association.new_record? || association[reflection.primary_key_name] != id)
               association[reflection.primary_key_name] = id
               association.save(true)
             end
@@ -979,6 +997,8 @@ module ActiveRecord
       #   If true, the associated object is readonly through the association.
       # [:validate]
       #   If false, don't validate the associated objects when saving the parent object. +false+ by default.
+      # [:autosave]
+      #   If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
       #
       # Option examples:
       #   belongs_to :firm, :foreign_key =&gt; &quot;client_of&quot;
@@ -991,15 +1011,12 @@ module ActiveRecord
       def belongs_to(association_id, options = {})
         reflection = create_belongs_to_reflection(association_id, options)
 
-        ivar = &quot;@#{reflection.name}&quot;
-
         if reflection.options[:polymorphic]
           association_accessor_methods(reflection, BelongsToPolymorphicAssociation)
 
           method_name = &quot;polymorphic_belongs_to_before_save_for_#{reflection.name}&quot;.to_sym
           define_method(method_name) do
-            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
-
+            association = association_instance_get(reflection.name)
             if association &amp;&amp; association.target
               if association.new_record?
                 association.save(true)
@@ -1019,9 +1036,7 @@ module ActiveRecord
 
           method_name = &quot;belongs_to_before_save_for_#{reflection.name}&quot;.to_sym
           define_method(method_name) do
-            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
-
-            if !association.nil?
+            if association = association_instance_get(reflection.name)
               if association.new_record?
                 association.save(true)
               end
@@ -1196,6 +1211,8 @@ module ActiveRecord
       #   If true, all the associated objects are readonly through the association.
       # [:validate]
       #   If false, don't validate the associated objects when saving the parent object. +true+ by default.
+      # [:autosave]
+      #   If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default.
       #
       # Option examples:
       #   has_and_belongs_to_many :projects
@@ -1243,33 +1260,30 @@ module ActiveRecord
         end
 
         def association_accessor_methods(reflection, association_proxy_class)
-          ivar = &quot;@#{reflection.name}&quot;
-
           define_method(reflection.name) do |*params|
             force_reload = params.first unless params.empty?
-
-            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+            association = association_instance_get(reflection.name)
 
             if association.nil? || force_reload
               association = association_proxy_class.new(self, reflection)
               retval = association.reload
               if retval.nil? and association_proxy_class == BelongsToAssociation
-                instance_variable_set(ivar, nil)
+                association_instance_set(reflection.name, nil)
                 return nil
               end
-              instance_variable_set(ivar, association)
+              association_instance_set(reflection.name, association)
             end
 
             association.target.nil? ? nil : association
           end
 
           define_method(&quot;loaded_#{reflection.name}?&quot;) do
-            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+            association = association_instance_get(reflection.name)
             association &amp;&amp; association.loaded?
           end
 
           define_method(&quot;#{reflection.name}=&quot;) do |new_value|
-            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+            association = association_instance_get(reflection.name)
 
             if association.nil? || association.target != new_value
               association = association_proxy_class.new(self, reflection)
@@ -1280,7 +1294,7 @@ module ActiveRecord
               self.send(reflection.name, new_value)
             else
               association.replace(new_value)
-              instance_variable_set(ivar, new_value.nil? ? nil : association)
+              association_instance_set(reflection.name, new_value.nil? ? nil : association)
             end
           end
 
@@ -1288,20 +1302,18 @@ module ActiveRecord
             return if target.nil? and association_proxy_class == BelongsToAssociation
             association = association_proxy_class.new(self, reflection)
             association.target = target
-            instance_variable_set(ivar, association)
+            association_instance_set(reflection.name, association)
           end
         end
 
         def collection_reader_method(reflection, association_proxy_class)
           define_method(reflection.name) do |*params|
-            ivar = &quot;@#{reflection.name}&quot;
-
             force_reload = params.first unless params.empty?
-            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+            association = association_instance_get(reflection.name)
 
-            unless association.respond_to?(:loaded?)
+            unless association
               association = association_proxy_class.new(self, reflection)
-              instance_variable_set(ivar, association)
+              association_instance_set(reflection.name, association)
             end
 
             association.reload if force_reload
@@ -1339,8 +1351,7 @@ module ActiveRecord
         def add_single_associated_validation_callbacks(association_name)
           method_name = &quot;validate_associated_records_for_#{association_name}&quot;.to_sym
           define_method(method_name) do
-            association = instance_variable_get(&quot;@#{association_name}&quot;)
-            if !association.nil?
+            if association = association_instance_get(association_name)
               errors.add association_name unless association.target.nil? || association.valid?
             end
           end
@@ -1350,12 +1361,10 @@ module ActiveRecord
 
         def add_multiple_associated_validation_callbacks(association_name)
           method_name = &quot;validate_associated_records_for_#{association_name}&quot;.to_sym
-          ivar = &quot;@#{association_name}&quot;
-
           define_method(method_name) do
-            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+            association = association_instance_get(association_name)
 
-            if association.respond_to?(:loaded?)
+            if association
               if new_record?
                 association
               elsif association.loaded?
@@ -1372,8 +1381,6 @@ module ActiveRecord
         end
 
         def add_multiple_associated_save_callbacks(association_name)
-          ivar = &quot;@#{association_name}&quot;
-
           method_name = &quot;before_save_associated_records_for_#{association_name}&quot;.to_sym
           define_method(method_name) do
             @new_record_before_save = new_record?
@@ -1383,13 +1390,13 @@ module ActiveRecord
 
           method_name = &quot;after_create_or_update_associated_records_for_#{association_name}&quot;.to_sym
           define_method(method_name) do
-            association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+            association = association_instance_get(association_name)
 
             records_to_save = if @new_record_before_save
               association
-            elsif association.respond_to?(:loaded?) &amp;&amp; association.loaded?
+            elsif association &amp;&amp; association.loaded?
               association.select { |record| record.new_record? }
-            elsif association.respond_to?(:loaded?) &amp;&amp; !association.loaded?
+            elsif association &amp;&amp; !association.loaded?
               association.target.select { |record| record.new_record? }
             else
               []
@@ -1407,15 +1414,13 @@ module ActiveRecord
 
         def association_constructor_method(constructor, reflection, association_proxy_class)
           define_method(&quot;#{constructor}_#{reflection.name}&quot;) do |*params|
-            ivar = &quot;@#{reflection.name}&quot;
-
             attributees      = params.first unless params.empty?
             replace_existing = params[1].nil? ? true : params[1]
-            association      = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+            association      = association_instance_get(reflection.name)
 
-            if association.nil?
+            unless association
               association = association_proxy_class.new(self, reflection)
-              instance_variable_set(ivar, association)
+              association_instance_set(reflection.name, association)
             end
 
             if association_proxy_class == HasOneAssociation</diff>
      <filename>activerecord/lib/active_record/associations.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3136,6 +3136,11 @@ module ActiveRecord #:nodoc:
     include Dirty
     include Callbacks, Observing, Timestamp
     include Associations, AssociationPreload, NamedScope
+
+    # AutosaveAssociation needs to be included before Transactions, because we want
+    # #save_with_autosave_associations to be wrapped inside a transaction.
+    include AutosaveAssociation, NestedAttributes
+
     include Aggregations, Transactions, Reflection, Calculations, Serialization
   end
 end</diff>
      <filename>activerecord/lib/active_record/base.rb</filename>
    </modified>
    <modified>
      <diff>@@ -65,6 +65,11 @@ module ActiveRecord
       def reflect_on_association(association)
         reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
       end
+
+      # Returns an array of AssociationReflection objects for all associations which have &lt;tt&gt;:autosave&lt;/tt&gt; enabled.
+      def reflect_on_all_autosave_associations
+        reflections.values.select { |reflection| reflection.options[:autosave] }
+      end
     end
 
 </diff>
      <filename>activerecord/lib/active_record/reflection.rb</filename>
    </modified>
    <modified>
      <diff>@@ -27,6 +27,7 @@ module ActiveRecord
       $queries_executed = []
       yield
     ensure
+      %w{ BEGIN COMMIT }.each { |x| $queries_executed.delete(x) }
       assert_equal num, $queries_executed.size, &quot;#{$queries_executed.size} instead of #{num} queries were executed.#{$queries_executed.size == 0 ? '' : &quot;\nQueries:\n#{$queries_executed.join(&quot;\n&quot;)}&quot;}&quot;
     end
 </diff>
      <filename>activerecord/lib/active_record/test_case.rb</filename>
    </modified>
    <modified>
      <diff>@@ -166,7 +166,7 @@ class DirtyTest &lt; ActiveRecord::TestCase
 
   def test_association_assignment_changes_foreign_key
     pirate = Pirate.create!(:catchphrase =&gt; 'jarl')
-    pirate.parrot = Parrot.create!
+    pirate.parrot = Parrot.create!(:name =&gt; 'Lorre')
     assert pirate.changed?
     assert_equal %w(parrot_id), pirate.changed
   end</diff>
      <filename>activerecord/test/cases/dirty_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -91,6 +91,15 @@ class ReflectionTest &lt; ActiveRecord::TestCase
     assert_equal Money, Customer.reflect_on_aggregation(:balance).klass
   end
 
+  def test_reflect_on_all_autosave_associations
+    expected = Pirate.reflect_on_all_associations.select { |r| r.options[:autosave] }
+    received = Pirate.reflect_on_all_autosave_associations
+
+    assert !received.empty?
+    assert_not_equal Pirate.reflect_on_all_associations.length, received.length
+    assert_equal expected, received
+  end
+
   def test_has_many_reflection
     reflection_for_clients = ActiveRecord::Reflection::AssociationReflection.new(:has_many, :clients, { :order =&gt; &quot;id&quot;, :dependent =&gt; :destroy }, Firm)
 </diff>
      <filename>activerecord/test/cases/reflection_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,6 +4,8 @@ class Parrot &lt; ActiveRecord::Base
   has_and_belongs_to_many :treasures
   has_many :loots, :as =&gt; :looter
   alias_attribute :title, :name
+
+  validates_presence_of :name
 end
 
 class LiveParrot &lt; Parrot</diff>
      <filename>activerecord/test/models/parrot.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,5 +5,12 @@ class Pirate &lt; ActiveRecord::Base
 
   has_many :treasure_estimates, :through =&gt; :treasures, :source =&gt; :price_estimates
 
+  # These both have :autosave enabled because accepts_nested_attributes_for is used on them.
+  has_one :ship
+  has_many :birds
+
+  accepts_nested_attributes_for :parrots, :birds, :allow_destroy =&gt; true, :reject_if =&gt; proc { |attributes| attributes.empty? }
+  accepts_nested_attributes_for :ship, :allow_destroy =&gt; true
+
   validates_presence_of :catchphrase
 end</diff>
      <filename>activerecord/test/models/pirate.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,10 @@
 class Ship &lt; ActiveRecord::Base
   self.record_timestamps = false
+
+  belongs_to :pirate
+  has_many :parts, :class_name =&gt; 'ShipPart', :autosave =&gt; true
+
+  accepts_nested_attributes_for :pirate, :allow_destroy =&gt; true
+
+  validates_presence_of :name
 end
\ No newline at end of file</diff>
      <filename>activerecord/test/models/ship.rb</filename>
    </modified>
    <modified>
      <diff>@@ -55,6 +55,11 @@ ActiveRecord::Schema.define do
     t.binary :data
   end
 
+  create_table :birds, :force =&gt; true do |t|
+    t.string :name
+    t.integer :pirate_id
+  end
+
   create_table :books, :force =&gt; true do |t|
     t.column :name, :string
   end
@@ -356,12 +361,18 @@ ActiveRecord::Schema.define do
 
   create_table :ships, :force =&gt; true do |t|
     t.string :name
+    t.integer :pirate_id
     t.datetime :created_at
     t.datetime :created_on
     t.datetime :updated_at
     t.datetime :updated_on
   end
 
+  create_table :ship_parts, :force =&gt; true do |t|
+    t.string :name
+    t.integer :ship_id
+  end
+
   create_table :sponsors, :force =&gt; true do |t|
     t.integer :club_id
     t.integer :sponsorable_id</diff>
      <filename>activerecord/test/schema/schema.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>a02d752ae408b34e048b7b040e28c36074a9119f</id>
    </parent>
  </parents>
  <author>
    <name>Eloy Duran</name>
    <login>alloy</login>
    <email>eloy.de.enige@gmail.com</email>
  </author>
  <url>http://github.com/rails/rails/commit/ec8f04584479aff895b0b511a7ba1e9d33f84067</url>
  <id>ec8f04584479aff895b0b511a7ba1e9d33f84067</id>
  <committed-date>2009-01-31T17:44:30-08:00</committed-date>
  <authored-date>2009-01-31T17:44:30-08:00</authored-date>
  <message>Add support for nested object forms to ActiveRecord and the helpers in ActionPack

Signed-Off-By: Michael Koziarski &lt;michael@koziarski.com&gt;

[#1202 state:committed]</message>
  <tree>86d6580345865cd1ce63b2055c0eca091ba8ee52</tree>
  <committer>
    <name>Michael Koziarski</name>
    <login>NZKoz</login>
    <email>michael@koziarski.com</email>
  </committer>
</commit>
