Class: ActiveRecord::Associations::Association
- Defined in:
- activerecord/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
selfon success. -
#remove_inverse_instance(record) ⇒ Object
Remove the inverse association, if possible.
-
#reset ⇒ Object
Resets the loaded flag to
falseand 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 'activerecord/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 'activerecord/lib/active_record/associations/association.rb', line 37 def disable_joins @disable_joins end |
#owner ⇒ Object
:nodoc:
36 37 38 |
# File 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/lib/active_record/associations/association.rb', line 237 def collection? false end |
#create(attributes = nil, &block) ⇒ Object
227 228 229 |
# File 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/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 'activerecord/lib/active_record/associations/association.rb', line 102 def target=(target) @target = target loaded! end |