Class: ActiveRecord::Associations::Association

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

Overview

Direct Known Subclasses

CollectionAssociation, SingularAssociation

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(owner, reflection) ⇒ Association

Returns a new instance of Association.



25
26
27
28
29
30
31
32
# File 'lib/active_record/associations/association.rb', line 25

def initialize(owner, reflection)
  reflection.check_validity!

  @owner, @reflection = owner, reflection

  reset
  reset_scope
end

Instance Attribute Details

#ownerObject (readonly)

:nodoc:



21
22
23
# File 'lib/active_record/associations/association.rb', line 21

def owner
  @owner
end

#reflectionObject (readonly)

:nodoc:



21
22
23
# File 'lib/active_record/associations/association.rb', line 21

def reflection
  @reflection
end

#targetObject

:nodoc:



21
22
23
# File 'lib/active_record/associations/association.rb', line 21

def target
  @target
end

Instance Method Details

#association_scopeObject

The scope for this association.

Note that the association_scope is merged into the target_scope only when the scope method is called. This is because at that point the call may be surrounded by scope.scoping { … } or with_scope { … } etc, which affects the scope which actually gets built.



88
89
90
91
92
# File 'lib/active_record/associations/association.rb', line 88

def association_scope
  if klass
    @association_scope ||= AssociationScope.scope(self)
  end
end

#create(attributes = {}, &block) ⇒ Object



189
190
191
# File 'lib/active_record/associations/association.rb', line 189

def create(attributes = {}, &block)
  _create_record(attributes, &block)
end

#create!(attributes = {}, &block) ⇒ Object



193
194
195
# File 'lib/active_record/associations/association.rb', line 193

def create!(attributes = {}, &block)
  _create_record(attributes, true, &block)
end

#extensionsObject



138
139
140
141
142
143
144
145
146
# File 'lib/active_record/associations/association.rb', line 138

def extensions
  extensions = klass.default_extensions | reflection.extensions

  if reflection.scope
    extensions |= reflection.scope_for(klass.unscoped, owner).extensions
  end

  extensions
end

#initialize_attributes(record, except_from_scope_attributes = nil) ⇒ Object

:nodoc:



179
180
181
182
183
184
185
186
187
# File 'lib/active_record/associations/association.rb', line 179

def initialize_attributes(record, except_from_scope_attributes = nil) #:nodoc:
  except_from_scope_attributes ||= {}
  skip_assign = [reflection.foreign_key, reflection.type].compact
  assigned_keys = record.changed_attribute_names_to_save
  assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
  attributes = scope_for_create.except!(*(assigned_keys - skip_assign))
  record.send(:_assign_attributes, attributes) if attributes.any?
  set_inverse_instance(record)
end

#inversed_from(record) ⇒ Object Also known as: inversed_from_queries



120
121
122
123
# File 'lib/active_record/associations/association.rb', line 120

def inversed_from(record)
  self.target = record
  @inversed = !!record
end

#klassObject

Returns the class of the target. belongs_to polymorphic overrides this to look at the polymorphic_type field on the owner.



128
129
130
# File 'lib/active_record/associations/association.rb', line 128

def klass
  reflection.klass
end

#load_targetObject

Loads the target if needed and returns it.

This method is abstract in the sense that it relies on find_target, which is expected to be provided by descendants.

If the target is already loaded it is just returned. Thus, you can call load_target unconditionally to get the target.

ActiveRecord::RecordNotFound is rescued within the method, and it is not reraised. The proxy is reset and nil is the return value.



158
159
160
161
162
163
164
165
# File 'lib/active_record/associations/association.rb', line 158

def load_target
  @target = find_target if (@stale_state && stale_target?) || find_target?

  loaded! unless loaded?
  target
rescue ActiveRecord::RecordNotFound
  reset
end

#loaded!Object

Asserts the target has been loaded setting the loaded flag to true.



56
57
58
59
60
# File 'lib/active_record/associations/association.rb', line 56

def loaded!
  @loaded = true
  @stale_state = stale_state
  @inversed = false
end

#loaded?Boolean

Has the target been already loaded?

Returns:

  • (Boolean)


51
52
53
# File 'lib/active_record/associations/association.rb', line 51

def loaded?
  @loaded
end

#marshal_dumpObject

We can't dump @reflection and @through_reflection since it contains the scope proc



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

def marshal_dump
  ivars = (instance_variables - [:@reflection, :@through_reflection]).map { |name| [name, instance_variable_get(name)] }
  [@reflection.name, ivars]
end

#marshal_load(data) ⇒ Object



173
174
175
176
177
# File 'lib/active_record/associations/association.rb', line 173

def marshal_load(data)
  reflection_name, ivars = data
  ivars.each { |name, val| instance_variable_set(name, val) }
  @reflection = @owner.class._reflect_on_association(reflection_name)
end

#reloadObject

Reloads the target and returns self on success.



43
44
45
46
47
48
# File 'lib/active_record/associations/association.rb', line 43

def reload
  reset
  reset_scope
  load_target
  self unless target.nil?
end

#remove_inverse_instance(record) ⇒ Object

Remove the inverse association, if possible



114
115
116
117
118
# File 'lib/active_record/associations/association.rb', line 114

def remove_inverse_instance(record)
  if inverse = inverse_association_for(record)
    inverse.inversed_from(nil)
  end
end

#resetObject

Resets the loaded flag to false and sets the target to nil.



35
36
37
38
39
40
# File 'lib/active_record/associations/association.rb', line 35

def reset
  @loaded = false
  @target = nil
  @stale_state = nil
  @inversed = false
end

#reset_scopeObject



94
95
96
# File 'lib/active_record/associations/association.rb', line 94

def reset_scope
  @association_scope = nil
end

#scopeObject



78
79
80
# File 'lib/active_record/associations/association.rb', line 78

def scope
  target_scope.merge!(association_scope)
end

#set_inverse_instance(record) ⇒ Object

Set the inverse association, if possible



99
100
101
102
103
104
# File 'lib/active_record/associations/association.rb', line 99

def set_inverse_instance(record)
  if inverse = inverse_association_for(record)
    inverse.inversed_from(owner)
  end
  record
end

#set_inverse_instance_from_queries(record) ⇒ Object



106
107
108
109
110
111
# File 'lib/active_record/associations/association.rb', line 106

def set_inverse_instance_from_queries(record)
  if inverse = inverse_association_for(record)
    inverse.inversed_from_queries(owner)
  end
  record
end

#stale_target?Boolean

The target is stale if the target no longer points to the record(s) that the relevant foreign_key(s) refers to. If stale, the association accessor method on the owner will reload the target. It's up to subclasses to implement the stale_state method if relevant.

Note that if the target has not been loaded, it is not considered stale.

Returns:

  • (Boolean)


68
69
70
# File 'lib/active_record/associations/association.rb', line 68

def stale_target?
  !@inversed && loaded? && @stale_state != stale_state
end

#target_scopeObject

Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the through association's scope)



134
135
136
# File 'lib/active_record/associations/association.rb', line 134

def target_scope
  AssociationRelation.create(klass, self).merge!(klass.all)
end