Class: ActiveRecord::Associations::Builder::BelongsTo

Inherits:
SingularAssociation show all
Defined in:
lib/active_record/associations/builder/belongs_to.rb

Overview

:nodoc:

Constant Summary

Constants inherited from Association

Association::VALID_OPTIONS

Class Method Summary collapse

Methods inherited from SingularAssociation

define_constructors

Methods inherited from Association

build, build_scope, check_dependent_options, create_reflection, define_extensions, define_readers, define_writers, validate_options, wrap_scope

Class Method Details

.add_counter_cache_callbacks(model, reflection) ⇒ Object

[View source]

71
72
73
74
75
76
77
78
79
80
# File 'lib/active_record/associations/builder/belongs_to.rb', line 71

def self.add_counter_cache_callbacks(model, reflection)
  cache_column = reflection.counter_cache_column

  model.after_update lambda { |record|
    record.belongs_to_counter_cache_after_update(reflection)
  }

  klass = reflection.class_name.safe_constantize
  klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
end

.add_counter_cache_methods(mixin) ⇒ Object

[View source]

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/active_record/associations/builder/belongs_to.rb', line 29

def self.add_counter_cache_methods(mixin)
  return if mixin.method_defined? :belongs_to_counter_cache_after_update

  mixin.class_eval do
    def belongs_to_counter_cache_after_update(reflection)
      foreign_key  = reflection.foreign_key
      cache_column = reflection.counter_cache_column

      if (@_after_replace_counter_called ||= false)
        @_after_replace_counter_called = false
      elsif association(reflection.name).target_changed?
        if reflection.polymorphic?
          model     = attribute_in_database(reflection.foreign_type).try(:constantize)
          model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
        else
          model     = reflection.klass
          model_was = reflection.klass
        end

        foreign_key_was = attribute_before_last_save foreign_key
        foreign_key     = attribute_in_database foreign_key

        if foreign_key && model.respond_to?(:increment_counter)
          foreign_key = counter_cache_target(reflection, model, foreign_key)
          model.increment_counter(cache_column, foreign_key)
        end

        if foreign_key_was && model_was.respond_to?(:decrement_counter)
          foreign_key_was = counter_cache_target(reflection, model_was, foreign_key_was)
          model_was.decrement_counter(cache_column, foreign_key_was)
        end
      end
    end

    private
      def counter_cache_target(reflection, model, foreign_key)
        primary_key = reflection.association_primary_key(model)
        model.unscoped.where!(primary_key => foreign_key)
      end
  end
end

.add_default_callbacks(model, reflection) ⇒ Object

[View source]

135
136
137
138
139
# File 'lib/active_record/associations/builder/belongs_to.rb', line 135

def self.add_default_callbacks(model, reflection)
  model.before_validation lambda { |o|
    o.association(reflection.name).default(&reflection.options[:default])
  }
end

.add_destroy_callbacks(model, reflection) ⇒ Object

[View source]

141
142
143
# File 'lib/active_record/associations/builder/belongs_to.rb', line 141

def self.add_destroy_callbacks(model, reflection)
  model.after_destroy lambda { |o| o.association(reflection.name).handle_dependency }
end

.add_touch_callbacks(model, reflection) ⇒ Object

[View source]

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/active_record/associations/builder/belongs_to.rb', line 117

def self.add_touch_callbacks(model, reflection)
  foreign_key = reflection.foreign_key
  n           = reflection.name
  touch       = reflection.options[:touch]

  callback = lambda { |changes_method| lambda { |record|
    BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
  }}

  unless reflection.counter_cache_column
    model.after_create callback.(:saved_changes), if: :saved_changes?
    model.after_destroy callback.(:changes_to_save)
  end

  model.after_update callback.(:saved_changes), if: :saved_changes?
  model.after_touch callback.(:changes_to_save)
end

.define_accessors(mixin, reflection) ⇒ Object

[View source]

24
25
26
27
# File 'lib/active_record/associations/builder/belongs_to.rb', line 24

def self.define_accessors(mixin, reflection)
  super
  add_counter_cache_methods mixin
end

.define_callbacks(model, reflection) ⇒ Object

[View source]

17
18
19
20
21
22
# File 'lib/active_record/associations/builder/belongs_to.rb', line 17

def self.define_callbacks(model, reflection)
  super
  add_counter_cache_callbacks(model, reflection) if reflection.options[:counter_cache]
  add_touch_callbacks(model, reflection)         if reflection.options[:touch]
  add_default_callbacks(model, reflection)       if reflection.options[:default]
end

.define_validations(model, reflection) ⇒ Object

[View source]

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/active_record/associations/builder/belongs_to.rb', line 145

def self.define_validations(model, reflection)
  if reflection.options.key?(:required)
    reflection.options[:optional] = !reflection.options.delete(:required)
  end

  if reflection.options[:optional].nil?
    required = model.belongs_to_required_by_default
  else
    required = !reflection.options[:optional]
  end

  super

  if required
    model.validates_presence_of reflection.name, message: :required
  end
end

.macroObject

[View source]

5
6
7
# File 'lib/active_record/associations/builder/belongs_to.rb', line 5

def self.macro
  :belongs_to
end

.touch_record(o, changes, foreign_key, name, touch, touch_method) ⇒ Object

:nodoc:

[View source]

82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/active_record/associations/builder/belongs_to.rb', line 82

def self.touch_record(o, changes, foreign_key, name, touch, touch_method) # :nodoc:
  old_foreign_id = changes[foreign_key] && changes[foreign_key].first

  if old_foreign_id
    association = o.association(name)
    reflection = association.reflection
    if reflection.polymorphic?
      foreign_type = reflection.foreign_type
      klass = changes[foreign_type] && changes[foreign_type].first || o.public_send(foreign_type)
      klass = klass.constantize
    else
      klass = association.klass
    end
    primary_key = reflection.association_primary_key(klass)
    old_record = klass.find_by(primary_key => old_foreign_id)

    if old_record
      if touch != true
        old_record.send(touch_method, touch)
      else
        old_record.send(touch_method)
      end
    end
  end

  record = o.send name
  if record && record.persisted?
    if touch != true
      record.send(touch_method, touch)
    else
      record.send(touch_method)
    end
  end
end

.valid_dependent_optionsObject

[View source]

13
14
15
# File 'lib/active_record/associations/builder/belongs_to.rb', line 13

def self.valid_dependent_options
  [:destroy, :delete]
end

.valid_options(options) ⇒ Object

[View source]

9
10
11
# File 'lib/active_record/associations/builder/belongs_to.rb', line 9

def self.valid_options(options)
  super + [:polymorphic, :touch, :counter_cache, :optional, :default]
end