Class: ActiveRecord::Associations::Association
- Inherits:
-
Object
- Object
- ActiveRecord::Associations::Association
- Defined in:
- lib/active_record/associations/association.rb
Overview
Active Record Associations
This is the root class of all associations (‘+ Foo’ signifies an included module Foo):
Association
SingularAssociation
HasOneAssociation + ForeignAssociation
HasOneThroughAssociation + ThroughAssociation
BelongsToAssociation
BelongsToPolymorphicAssociation
CollectionAssociation
HasManyAssociation + ForeignAssociation
HasManyThroughAssociation + ThroughAssociation
Associations in Active Record are middlemen between the object that holds the association, known as the owner
, and the associated result set, known as the target
. Association metadata is available in reflection
, which is an instance of ActiveRecord::Reflection::AssociationReflection
.
For example, given
class Blog < ActiveRecord::Base
has_many :posts
end
blog = Blog.first
The association of blog.posts
has the object blog
as its owner
, the collection of its posts as target
, and the reflection
object represents a :has_many
macro.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#disable_joins ⇒ Object
readonly
Returns the value of attribute disable_joins.
-
#owner ⇒ Object
:nodoc:.
-
#reflection ⇒ Object
readonly
Returns the value of attribute reflection.
Instance Method Summary collapse
-
#async_load_target ⇒ Object
:nodoc:.
-
#collection? ⇒ Boolean
Whether the association represent a single record or a collection of records.
- #create(attributes = nil, &block) ⇒ Object
- #create!(attributes = nil, &block) ⇒ Object
- #extensions ⇒ Object
-
#initialize(owner, reflection) ⇒ Association
constructor
A new instance of Association.
-
#initialize_attributes(record, except_from_scope_attributes = nil) ⇒ Object
:nodoc:.
- #inversed_from(record) ⇒ Object
- #inversed_from_queries(record) ⇒ Object
-
#klass ⇒ Object
Returns the class of the target.
-
#load_target ⇒ Object
Loads the target if needed and returns it.
-
#loaded! ⇒ Object
Asserts the target has been loaded setting the loaded flag to
true
. -
#loaded? ⇒ Boolean
Has the target been already loaded?.
-
#marshal_dump ⇒ Object
We can’t dump @reflection and @through_reflection since it contains the scope proc.
- #marshal_load(data) ⇒ Object
-
#reload(force = false) ⇒ Object
Reloads the target and returns
self
on success. -
#remove_inverse_instance(record) ⇒ Object
Remove the inverse association, if possible.
-
#reset ⇒ Object
Resets the loaded flag to
false
and sets the target tonil
. -
#reset_negative_cache ⇒ Object
:nodoc:.
- #reset_scope ⇒ Object
- #scope ⇒ Object
-
#set_inverse_instance(record) ⇒ Object
Set the inverse association, if possible.
- #set_inverse_instance_from_queries(record) ⇒ Object
- #set_strict_loading(record) ⇒ Object
-
#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.
- #target ⇒ Object
-
#target=(target) ⇒ Object
Sets the target of this association to
\target
, and the loaded flag totrue
.
Constructor Details
#initialize(owner, reflection) ⇒ Association
Returns a new instance of Association.
41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/active_record/associations/association.rb', line 41 def initialize(owner, reflection) reflection.check_validity! @owner, @reflection = owner, reflection @disable_joins = @reflection.[:disable_joins] || false reset reset_scope @skip_strict_loading = nil end |
Instance Attribute Details
#disable_joins ⇒ Object (readonly)
Returns the value of attribute disable_joins.
37 38 39 |
# File 'lib/active_record/associations/association.rb', line 37 def disable_joins @disable_joins end |
#owner ⇒ Object
:nodoc:
36 37 38 |
# File 'lib/active_record/associations/association.rb', line 36 def owner @owner end |
#reflection ⇒ Object (readonly)
Returns the value of attribute reflection.
37 38 39 |
# File 'lib/active_record/associations/association.rb', line 37 def reflection @reflection end |
Instance Method Details
#async_load_target ⇒ Object
:nodoc:
198 199 200 201 202 203 |
# File 'lib/active_record/associations/association.rb', line 198 def async_load_target # :nodoc: @target = find_target(async: true) if (@stale_state && stale_target?) || find_target? loaded! unless loaded? nil end |
#collection? ⇒ Boolean
Whether the association represent a single record or a collection of records.
237 238 239 |
# File 'lib/active_record/associations/association.rb', line 237 def collection? false end |
#create(attributes = nil, &block) ⇒ Object
227 228 229 |
# File 'lib/active_record/associations/association.rb', line 227 def create(attributes = nil, &block) _create_record(attributes, &block) end |
#create!(attributes = nil, &block) ⇒ Object
231 232 233 |
# File 'lib/active_record/associations/association.rb', line 231 def create!(attributes = nil, &block) _create_record(attributes, true, &block) end |
#extensions ⇒ Object
169 170 171 172 173 174 175 176 177 |
# File 'lib/active_record/associations/association.rb', line 169 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:
217 218 219 220 221 222 223 224 225 |
# File 'lib/active_record/associations/association.rb', line 217 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
153 154 155 |
# File 'lib/active_record/associations/association.rb', line 153 def inversed_from(record) self.target = record end |
#inversed_from_queries(record) ⇒ Object
157 158 159 160 161 |
# File 'lib/active_record/associations/association.rb', line 157 def inversed_from_queries(record) if inversable?(record) self.target = record end end |
#klass ⇒ Object
Returns the class of the target. belongs_to polymorphic overrides this to look at the polymorphic_type field on the owner.
165 166 167 |
# File 'lib/active_record/associations/association.rb', line 165 def klass reflection.klass end |
#load_target ⇒ Object
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.
189 190 191 192 193 194 195 196 |
# File 'lib/active_record/associations/association.rb', line 189 def load_target @target = find_target(async: false) 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
.
86 87 88 89 |
# File 'lib/active_record/associations/association.rb', line 86 def loaded! @loaded = true @stale_state = stale_state end |
#loaded? ⇒ Boolean
Has the target been already loaded?
81 82 83 |
# File 'lib/active_record/associations/association.rb', line 81 def loaded? @loaded end |
#marshal_dump ⇒ Object
We can’t dump @reflection and @through_reflection since it contains the scope proc
206 207 208 209 |
# File 'lib/active_record/associations/association.rb', line 206 def marshal_dump ivars = (instance_variables - [:@reflection, :@through_reflection]).map { |name| [name, instance_variable_get(name)] } [@reflection.name, ivars] end |
#marshal_load(data) ⇒ Object
211 212 213 214 215 |
# File 'lib/active_record/associations/association.rb', line 211 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 |
#reload(force = false) ⇒ Object
Reloads the target and returns self
on success. The QueryCache is cleared if force
is true.
72 73 74 75 76 77 78 |
# File 'lib/active_record/associations/association.rb', line 72 def reload(force = false) klass.connection_pool.clear_query_cache if force && klass reset reset_scope load_target self unless target.nil? end |
#remove_inverse_instance(record) ⇒ Object
Remove the inverse association, if possible
147 148 149 150 151 |
# File 'lib/active_record/associations/association.rb', line 147 def remove_inverse_instance(record) if inverse = inverse_association_for(record) inverse.inversed_from(nil) end end |
#reset ⇒ Object
Resets the loaded flag to false
and sets the target to nil
.
61 62 63 64 |
# File 'lib/active_record/associations/association.rb', line 61 def reset @loaded = false @stale_state = nil end |
#reset_negative_cache ⇒ Object
:nodoc:
66 67 68 |
# File 'lib/active_record/associations/association.rb', line 66 def reset_negative_cache # :nodoc: reset if loaded? && target.nil? end |
#reset_scope ⇒ Object
119 120 121 |
# File 'lib/active_record/associations/association.rb', line 119 def reset_scope @association_scope = nil end |
#scope ⇒ Object
107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/active_record/associations/association.rb', line 107 def scope if disable_joins DisableJoinsAssociationScope.create.scope(self) elsif (scope = klass.current_scope) && scope.try(:proxy_association) == self scope.spawn elsif scope = klass.global_current_scope target_scope.merge!(association_scope).merge!(scope) else target_scope.merge!(association_scope) end end |
#set_inverse_instance(record) ⇒ Object
Set the inverse association, if possible
132 133 134 135 136 137 |
# File 'lib/active_record/associations/association.rb', line 132 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
139 140 141 142 143 144 |
# File 'lib/active_record/associations/association.rb', line 139 def set_inverse_instance_from_queries(record) if inverse = inverse_association_for(record) inverse.inversed_from_queries(owner) end record end |
#set_strict_loading(record) ⇒ Object
123 124 125 126 127 128 129 |
# File 'lib/active_record/associations/association.rb', line 123 def set_strict_loading(record) if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many record.strict_loading! else record.strict_loading!(false, mode: owner.strict_loading_mode) end 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.
97 98 99 |
# File 'lib/active_record/associations/association.rb', line 97 def stale_target? loaded? && @stale_state != stale_state end |
#target ⇒ Object
53 54 55 56 57 58 |
# File 'lib/active_record/associations/association.rb', line 53 def target if @target.is_a?(Promise) @target = @target.value end @target end |
#target=(target) ⇒ Object
Sets the target of this association to \target
, and the loaded flag to true
.
102 103 104 105 |
# File 'lib/active_record/associations/association.rb', line 102 def target=(target) @target = target loaded! end |