Class: ActiveRecord::Associations::Preloader::Association

Inherits:
Object
  • Object
show all
Defined in:
activerecord/lib/active_record/associations/preloader/association.rb

Overview

:nodoc:

Direct Known Subclasses

ThroughAssociation

Defined Under Namespace

Classes: LoaderQuery, LoaderRecords

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(klass, owners, reflection, preload_scope, reflection_scope, associate_by_default) ⇒ Association

Returns a new instance of Association.



92
93
94
95
96
97
98
99
100
101
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 92

def initialize(klass, owners, reflection, preload_scope, reflection_scope, associate_by_default)
  @klass         = klass
  @owners        = owners.uniq(&:__id__)
  @reflection    = reflection
  @preload_scope = preload_scope
  @reflection_scope = reflection_scope
  @associate     = associate_by_default || !preload_scope || preload_scope.empty_scope?
  @model         = owners.first && owners.first.class
  @run = false
end

Instance Attribute Details

#klassObject (readonly)

Returns the value of attribute klass



90
91
92
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 90

def klass
  @klass
end

Instance Method Details

#associate_records_from_unscoped(unscoped_records) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 206

def associate_records_from_unscoped(unscoped_records)
  return if unscoped_records.nil? || unscoped_records.empty?
  return if !reflection_scope.empty_scope?
  return if preload_scope && !preload_scope.empty_scope?
  return if reflection.collection?

  unscoped_records.select { |r| r[association_key_name].present? }.each do |record|
    owners = owners_by_key[derive_key(record, association_key_name)]
    owners&.each_with_index do |owner, i|
      association = owner.association(reflection.name)
      association.target = record

      if i == 0 # Set inverse on first owner
        association.set_inverse_instance(record)
      end
    end
  end
end

#association_key_nameObject

The name of the key on the associated records



149
150
151
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 149

def association_key_name
  reflection.join_primary_key(klass)
end

#future_classesObject



107
108
109
110
111
112
113
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 107

def future_classes
  if run?
    []
  else
    [@klass]
  end
end

#load_records(raw_records = nil) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 185

def load_records(raw_records = nil)
  # owners can be duplicated when a relation has a collection association join
  # #compare_by_identity makes such owners different hash keys
  @records_by_owner = {}.compare_by_identity
  raw_records ||= loader_query.records_for([self])
  @preloaded_records = raw_records.select do |record|
    assignments = false

    owners_by_key[derive_key(record, association_key_name)]&.each do |owner|
      entries = (@records_by_owner[owner] ||= [])

      if reflection.collection? || entries.empty?
        entries << record
        assignments = true
      end
    end

    assignments
  end
end

#loaded?(owner) ⇒ Boolean

Returns:

  • (Boolean)


164
165
166
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 164

def loaded?(owner)
  owner.association(reflection.name).loaded?
end

#loader_queryObject



153
154
155
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 153

def loader_query
  LoaderQuery.new(scope, association_key_name)
end

#owners_by_keyObject



157
158
159
160
161
162
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 157

def owners_by_key
  @owners_by_key ||= owners.each_with_object({}) do |owner, result|
    key = derive_key(owner, owner_key_name)
    (result[key] ||= []) << owner if key
  end
end

#preloaded_recordsObject



142
143
144
145
146
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 142

def preloaded_records
  load_records unless defined?(@preloaded_records)

  @preloaded_records
end

#records_by_ownerObject



136
137
138
139
140
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 136

def records_by_owner
  load_records unless defined?(@records_by_owner)

  @records_by_owner
end

#runObject



123
124
125
126
127
128
129
130
131
132
133
134
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 123

def run
  return self if run?
  @run = true

  records = records_by_owner

  owners.each do |owner|
    associate_records_to_owner(owner, records[owner] || [])
  end if @associate

  self
end

#run?Boolean

Returns:

  • (Boolean)


119
120
121
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 119

def run?
  @run
end

#runnable_loadersObject



115
116
117
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 115

def runnable_loaders
  [self]
end

#scopeObject



172
173
174
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 172

def scope
  @scope ||= build_scope
end

#set_inverse(record) ⇒ Object



176
177
178
179
180
181
182
183
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 176

def set_inverse(record)
  if owners = owners_by_key[derive_key(record, association_key_name)]
    # Processing only the first owner
    # because the record is modified but not an owner
    association = owners.first.association(reflection.name)
    association.set_inverse_instance(record)
  end
end

#table_nameObject



103
104
105
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 103

def table_name
  @klass.table_name
end

#target_for(owner) ⇒ Object



168
169
170
# File 'activerecord/lib/active_record/associations/preloader/association.rb', line 168

def target_for(owner)
  Array.wrap(owner.association(reflection.name).target)
end