has_one_association.rb 3.6 KB
Newer Older
1
module ActiveRecord
2
  # = Active Record Belongs To Has One Association
3
  module Associations
4
    class HasOneAssociation < SingularAssociation #:nodoc:
5
      include ForeignAssociation
6 7 8

      def handle_dependency
        case options[:dependent]
9
        when :restrict_with_exception
10 11 12 13 14
          raise ActiveRecord::DeleteRestrictionError.new(reflection.name) if load_target

        when :restrict_with_error
          if load_target
            record = klass.human_attribute_name(reflection.name).downcase
15 16 17 18 19 20 21 22
            message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.one', record: record, raise: true) rescue nil
            if message
              ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
                The error key `:'restrict_dependent_destroy.one'` has been deprecated and will be removed in Rails 5.1.
                Please use `:'restrict_dependent_destroy.has_one'` instead.
              MESSAGE
            end
            owner.errors.add(:base, message || :'restrict_dependent_destroy.has_one', record: record)
23
            throw(:abort)
24 25 26 27 28 29 30
          end

        else
          delete
        end
      end

31
      def replace(record, save = true)
32
        raise_on_type_mismatch!(record) if record
33
        load_target
34

35
        return self.target if !(target || record)
36 37 38

        assigning_another_record = target != record
        if assigning_another_record || record.changed?
39
          save &&= owner.persisted?
40

41
          transaction_if(save) do
42
            remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
J
Jo Liss 已提交
43

44 45 46
            if record
              set_owner_attributes(record)
              set_inverse_instance(record)
J
Jo Liss 已提交
47

48
              if save && !record.save
49 50 51 52
                nullify_owner_attributes(record)
                set_owner_attributes(target) if target
                raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
              end
53 54
            end
          end
55 56
        end

57
        self.target = record
58
      end
59

60 61 62 63 64 65 66 67
      def delete(method = options[:dependent])
        if load_target
          case method
            when :delete
              target.delete
            when :destroy
              target.destroy
            when :nullify
68
              target.update_columns(reflection.foreign_key => nil) if target.persisted?
69
          end
70
        end
71 72
      end

73 74
      private

75
        # The reason that the save param for replace is false, if for create (not just build),
76 77 78 79
        # 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)
J
Jon Leighton 已提交
80
          replace(record, false)
J
Jamis Buck 已提交
81
        end
82

83
        def remove_target!(method)
84 85 86 87 88 89 90
          case method
            when :delete
              target.delete
            when :destroy
              target.destroy
            else
              nullify_owner_attributes(target)
91

92 93 94
              if target.persisted? && owner.persisted? && !target.save
                set_owner_attributes(target)
                raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
J
Jo Liss 已提交
95
                                      "The record failed to save after its foreign key was set to nil."
96
              end
97 98
          end
        end
99 100

        def nullify_owner_attributes(record)
101
          record[reflection.foreign_key] = nil
102
        end
103 104 105 106 107 108 109 110

        def transaction_if(value)
          if value
            reflection.klass.transaction { yield }
          else
            yield
          end
        end
111 112 113
    end
  end
end