diff options
Diffstat (limited to 'activerecord/lib/active_record/associations/has_one_association.rb')
-rw-r--r-- | activerecord/lib/active_record/associations/has_one_association.rb | 155 |
1 files changed, 48 insertions, 107 deletions
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index d581939f04..e13f97125f 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -1,135 +1,76 @@ module ActiveRecord # = Active Record Belongs To Has One Association module Associations - class HasOneAssociation < AssociationProxy #:nodoc: - def create(attrs = {}, replace_existing = true) - new_record(replace_existing) do |reflection| - attrs = merge_with_conditions(attrs) - reflection.create_association(attrs) - end - end + class HasOneAssociation < SingularAssociation #:nodoc: + def replace(record, save = true) + raise_on_type_mismatch(record) if record + load_target - def create!(attrs = {}, replace_existing = true) - new_record(replace_existing) do |reflection| - attrs = merge_with_conditions(attrs) - reflection.create_association!(attrs) - end - end + reflection.klass.transaction do + if target && target != record + remove_target!(options[:dependent]) + end + + if record + set_inverse_instance(record) + set_owner_attributes(record) - def build(attrs = {}, replace_existing = true) - new_record(replace_existing) do |reflection| - attrs = merge_with_conditions(attrs) - reflection.build_association(attrs) + if owner.persisted? && save && !record.save + nullify_owner_attributes(record) + set_owner_attributes(target) + raise RecordNotSaved, "Failed to save the new associated #{reflection.name}." + end + end end - end - def replace(obj, dont_save = false) - load_target + self.target = record + end - unless @target.nil? || @target == obj - if dependent? && !dont_save - case @reflection.options[:dependent] + def delete(method = options[:dependent]) + if load_target + case method when :delete - @target.delete if @target.persisted? - @owner.clear_association_cache + target.delete when :destroy - @target.destroy if @target.persisted? - @owner.clear_association_cache + target.destroy when :nullify - @target[@reflection.primary_key_name] = nil - @target.save if @owner.persisted? && @target.persisted? - end - else - @target[@reflection.primary_key_name] = nil - @target.save if @owner.persisted? && @target.persisted? + target.update_attribute(reflection.foreign_key, nil) end end - - if obj.nil? - @target = nil - else - raise_on_type_mismatch(obj) - set_belongs_to_association_for(obj) - @target = (AssociationProxy === obj ? obj.target : obj) - end - - set_inverse_instance(obj, @owner) - @loaded = true - - unless !@owner.persisted? || obj.nil? || dont_save - return (obj.save ? self : false) - else - return (obj.nil? ? nil : self) - end end - protected - def owner_quoted_id(reflection = @reflection) - if reflection.options[:primary_key] - @owner.class.quote_value(@owner.send(reflection.options[:primary_key])) - else - @owner.quoted_id - end - end + def association_scope + super.order(options[:order]) + end private - def find_target - options = @reflection.options.dup.slice(:select, :order, :include, :readonly) - the_target = with_scope(:find => @scope[:find]) do - @reflection.klass.find(:first, options) - end - set_inverse_instance(the_target, @owner) - the_target - end - - def construct_find_scope - if @reflection.options[:as] - sql = - "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " + - "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}" - else - sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}" - end - sql << " AND (#{conditions})" if conditions - { :conditions => sql } - end + alias creation_attributes construct_owner_attributes - def construct_create_scope - create_scoping = {} - set_belongs_to_association_for(create_scoping) - create_scoping + # The reason that the save param for replace is false, if for create (not just build), + # is because the setting of the foreign keys is actually handled by the scoping when + # the record is instantiated, and so they are set straight away and do not need to be + # updated within replace. + def set_new_record(record) + replace(record, false) end - def new_record(replace_existing) - # Make sure we load the target first, if we plan on replacing the existing - # instance. Otherwise, if the target has not previously been loaded - # elsewhere, the instance we create will get orphaned. - load_target if replace_existing - record = @reflection.klass.send(:with_scope, :create => @scope[:create]) do - yield @reflection - end - - if replace_existing - replace(record, true) + def remove_target!(method) + if [:delete, :destroy].include?(method) + target.send(method) else - record[@reflection.primary_key_name] = @owner.id if @owner.persisted? - self.target = record - set_inverse_instance(record, @owner) - end + nullify_owner_attributes(target) - record - end - - def we_can_set_the_inverse_on_this?(record) - inverse = @reflection.inverse_of - return !inverse.nil? + if target.persisted? && owner.persisted? && !target.save + set_owner_attributes(target) + raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " + + "The record failed to save when after its foreign key was set to nil." + end + end end - def merge_with_conditions(attrs={}) - attrs ||= {} - attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash) - attrs + def nullify_owner_attributes(record) + record[reflection.foreign_key] = nil end end end |