module ActiveRecord::AutosaveAssociation

def self.included(base)

def self.included(base)
  base.class_eval do
    base.extend(ClassMethods)
    alias_method_chain :reload, :autosave_associations
    ASSOCIATION_TYPES.each do |type|
      base.send("valid_keys_for_#{type}_association") << :autosave
    end
  end
end

def associated_records_to_validate_or_save(association, new_record, autosave)

unless the parent is/was a new record itself.
or saved. If +autosave+ is +false+ only new records will be returned,
Returns the record for an association collection that should be validated
def associated_records_to_validate_or_save(association, new_record, autosave)
  if new_record
    association
  elsif autosave
    association.target.select { |record| record.changed_for_autosave? }
  else
    association.target.select { |record| record.new_record? }
  end
end

def association_valid?(reflection, association)

enabled records if they're marked_for_destruction? or destroyed.
the parent, self, if it wasn't. Skips any :autosave
Returns whether or not the association is valid and applies any errors to
def association_valid?(reflection, association)
  return true if association.destroyed? || association.marked_for_destruction?
  unless valid = association.valid?
    if reflection.options[:autosave]
      association.errors.each_error do |attribute, error|
        attribute = "#{reflection.name}.#{attribute}"
        errors.add(attribute, error.dup) unless errors.on(attribute)
      end
    else
      errors.add(reflection.name)
    end
  end
  valid
end

def before_save_collection_association

association whether or not the parent was a new record before saving.
Is used as a before_save callback to check while saving a collection
def before_save_collection_association
  @new_record_before_save = new_record?
  true
end

def changed_for_autosave?

any of its nested autosave associations are likewise changed)
Returns whether or not this record has been changed in any way (including whether
def changed_for_autosave?
  new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
end

def mark_for_destruction

Only useful if the :autosave option on the parent is enabled for this associated model.

This does _not_ actually destroy the record yet, rather it will be destroyed when parent.save is called.
Marks this record to be destroyed as part of the parents save transaction.
def mark_for_destruction
  @marked_for_destruction = true
end

def marked_for_destruction?

Only useful if the :autosave option on the parent is enabled for this associated model.

Returns whether or not this record will be destroyed as part of the parents save transaction.
def marked_for_destruction?
  @marked_for_destruction
end

def nested_records_changed_for_autosave?

any new ones), and return true if is changed for autosave
go through nested autosave associations that are loaded in memory (without loading
def nested_records_changed_for_autosave?
  self.class.reflect_on_all_autosave_associations.each do |reflection|
    if association = association_instance_get(reflection.name)
      if [:belongs_to, :has_one].include?(reflection.macro)
        return true if association.target && association.target.changed_for_autosave?
      else
        association.target.each {|record| return true if record.changed_for_autosave? }
      end
    end
  end
  false
end

def reload_with_autosave_associations(options = nil)

Reloads the attributes of the object as usual and removes a mark for destruction.
def reload_with_autosave_associations(options = nil)
  @marked_for_destruction = false
  reload_without_autosave_associations(options)
end

def save_belongs_to_association(reflection)

ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
This all happens inside a transaction, _if_ the Transactions module is included into

destruction with mark_for_destruction.
In addition, it will destroy the association if it was marked for

on the association.
Saves the associated record if it's new or :autosave is enabled
def save_belongs_to_association(reflection)
  if (association = association_instance_get(reflection.name)) && !association.destroyed?
    autosave = reflection.options[:autosave]
    if autosave && association.marked_for_destruction?
      association.destroy
    elsif autosave != false
      saved = association.save(!autosave) if association.new_record? || autosave
      if association.updated?
        association_id = association.send(reflection.options[:primary_key] || :id)
        self[reflection.primary_key_name] = association_id
        # TODO: Removing this code doesn't seem to matter…
        if reflection.options[:polymorphic]
          self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s
        end
      end
      saved if autosave
    end
  end
end

def save_collection_association(reflection)

ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
This all happens inside a transaction, _if_ the Transactions module is included into

with mark_for_destruction.
In addition, it destroys all children that were marked for destruction

:autosave is enabled on the association.
Saves any new associated records, or all loaded autosave associations if
def save_collection_association(reflection)
  if association = association_instance_get(reflection.name)
    autosave = reflection.options[:autosave]
    if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
      records.each do |record|
        next if record.destroyed?
        if autosave && record.marked_for_destruction?
          association.destroy(record)
        elsif autosave != false && (@new_record_before_save || record.new_record?)
          if autosave
            saved = association.send(:insert_record, record, false, false)
          else
            association.send(:insert_record, record)
          end
        elsif autosave
          saved = record.save(false)
        end
        raise ActiveRecord::Rollback if saved == false
      end
    end
    # reconstruct the SQL queries now that we know the owner's id
    association.send(:construct_sql) if association.respond_to?(:construct_sql)
  end
end

def save_has_one_association(reflection)

ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
This all happens inside a transaction, _if_ the Transactions module is included into

destruction with mark_for_destruction.
In addition, it will destroy the association if it was marked for

on the association.
Saves the associated record if it's new or :autosave is enabled
def save_has_one_association(reflection)
  if (association = association_instance_get(reflection.name)) && !association.target.nil? && !association.destroyed?
    autosave = reflection.options[:autosave]
    if autosave && association.marked_for_destruction?
      association.destroy
    else
      key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
      if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave)
        association[reflection.primary_key_name] = key
        saved = association.save(!autosave)
        raise ActiveRecord::Rollback if !saved && autosave
        saved
      end
    end
  end
end

def validate_collection_association(reflection)

+reflection+.
:autosave is turned on for the association specified by
Validate the associated records if :validate or
def validate_collection_association(reflection)
  if association = association_instance_get(reflection.name)
    if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
      records.each { |record| association_valid?(reflection, record) }
    end
  end
end

def validate_single_association(reflection)

turned on for the association specified by +reflection+.
Validate the association if :validate or :autosave is
def validate_single_association(reflection)
  if (association = association_instance_get(reflection.name)) && !association.target.nil?
    association_valid?(reflection, association)
  end
end