Class: DataMapper::Associations::Relationship
- Inherits:
-
Object
- Object
- DataMapper::Associations::Relationship
- Includes:
- Extlib::Assertions
- Defined in:
- lib/dm-core/associations/relationship.rb
Overview
Base class for relationships. Each type of relationship (1 to 1, 1 to n, n to m) implements a subclass of this class with methods like get and set overridden.
Direct Known Subclasses
ManyToOne::Relationship, OneToMany::Relationship, OneToOne::Relationship
Constant Summary collapse
- OPTIONS =
[ :child_repository_name, :parent_repository_name, :child_key, :parent_key, :min, :max, :inverse ].to_set
Instance Attribute Summary collapse
-
#child_repository_name ⇒ Object
readonly
Repository from where child objects are loaded.
-
#instance_variable_name ⇒ Object
readonly
ivar used to store collection of child options in source.
-
#max ⇒ Object
readonly
Maximum number of child objects for relationship.
-
#min ⇒ Object
readonly
Minimum number of child objects for relationship.
-
#name ⇒ Object
readonly
Relationship name.
-
#options ⇒ Object
readonly
Options used to set up association of this relationship.
-
#parent_repository_name ⇒ Object
readonly
Repository from where parent objects are loaded.
-
#query ⇒ Object
readonly
private
Returns query options for relationship.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
Compares another Relationship for equivalency.
-
#child_key ⇒ PropertySet
(also: #relationship_child_key)
Returns a set of keys that identify the target model.
-
#child_model ⇒ Resource
private
Returns model class used by child side of the relationship.
-
#child_model? ⇒ Boolean
private
TODO: document.
-
#child_model_name ⇒ Object
private
TODO: document.
-
#eager_load(source, query = nil) ⇒ Collection
private
Eager load the collection using the source as a base.
-
#eql?(other) ⇒ Boolean
Compares another Relationship for equality.
-
#get(resource, other_query = nil) ⇒ Object
Loads and returns “other end” of the association.
-
#get!(resource) ⇒ Object
Gets “other end” of the association directly as @ivar on given resource.
-
#inverse ⇒ Object
Get the inverse relationship from the target model.
-
#loaded?(resource) ⇒ Boolean
Checks if “other end” of association is loaded on given resource.
-
#parent_key ⇒ PropertySet
private
Returns a set of keys that identify parent model.
-
#parent_model ⇒ Resource
private
Returns model class used by parent side of the relationship.
-
#parent_model? ⇒ Boolean
private
TODO: document.
-
#parent_model_name ⇒ Object
private
TODO: document.
-
#query_for(source, other_query = nil) ⇒ Object
Creates and returns Query instance that fetches target resource(s) (ex.: articles) for given target resource (ex.: author).
-
#relative_target_repository_name ⇒ Object
private
TODO: document.
-
#relative_target_repository_name_for(source) ⇒ Object
private
TODO: document.
-
#set(resource, association) ⇒ Object
Sets value of the “other end” of association on given resource.
-
#set!(resource, association) ⇒ Object
Sets “other end” of the association directly as @ivar on given resource.
-
#source_scope(source) ⇒ Hash
private
Returns a hash of conditions that scopes query that fetches target object.
-
#valid?(source) ⇒ Boolean
Test the source to see if it is a valid target.
Instance Attribute Details
#child_repository_name ⇒ Object (readonly)
Repository from where child objects are loaded
59 60 61 |
# File 'lib/dm-core/associations/relationship.rb', line 59 def child_repository_name @child_repository_name end |
#instance_variable_name ⇒ Object (readonly)
ivar used to store collection of child options in source
instance variable name for source will be @commits
54 55 56 |
# File 'lib/dm-core/associations/relationship.rb', line 54 def instance_variable_name @instance_variable_name end |
#max ⇒ Object (readonly)
Maximum number of child objects for relationship
maximum is 5
95 96 97 |
# File 'lib/dm-core/associations/relationship.rb', line 95 def max @max end |
#min ⇒ Object (readonly)
Minimum number of child objects for relationship
minimum is 2
79 80 81 |
# File 'lib/dm-core/associations/relationship.rb', line 79 def min @min end |
#name ⇒ Object (readonly)
Relationship name
name is :parent
24 25 26 |
# File 'lib/dm-core/associations/relationship.rb', line 24 def name @name end |
#options ⇒ Object (readonly)
Options used to set up association of this relationship
options is a hash with a single key, :model
39 40 41 |
# File 'lib/dm-core/associations/relationship.rb', line 39 def @options end |
#parent_repository_name ⇒ Object (readonly)
Repository from where parent objects are loaded
64 65 66 |
# File 'lib/dm-core/associations/relationship.rb', line 64 def parent_repository_name @parent_repository_name end |
#query ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns query options for relationship.
For this base class, always returns query options has been initialized with. Overriden in subclasses.
104 105 106 |
# File 'lib/dm-core/associations/relationship.rb', line 104 def query @query end |
Instance Method Details
#==(other) ⇒ Boolean
Compares another Relationship for equivalency
352 353 354 355 356 357 358 359 360 |
# File 'lib/dm-core/associations/relationship.rb', line 352 def ==(other) return true if equal?(other) return false if kind_of_inverse?(other) other.respond_to?(:cmp_repository?, true) && other.respond_to?(:cmp_model?, true) && other.respond_to?(:cmp_key?, true) && other.respond_to?(:query) && cmp?(other, :==) end |
#child_key ⇒ PropertySet Also known as: relationship_child_key
Returns a set of keys that identify the target model
166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/dm-core/associations/relationship.rb', line 166 def child_key return @child_key if defined?(@child_key) repository_name = child_repository_name || parent_repository_name properties = child_model.properties(repository_name) @child_key = if @child_properties child_key = properties.values_at(*@child_properties) properties.class.new(child_key).freeze else properties.key end end |
#child_model ⇒ Resource
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns model class used by child side of the relationship
139 140 141 142 143 |
# File 'lib/dm-core/associations/relationship.rb', line 139 def child_model @child_model ||= (@parent_model || Object).find_const(child_model_name) rescue NameError raise NameError, "Cannot find the child_model #{child_model_name} for #{parent_model_name} in #{name}" end |
#child_model? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
TODO: document
147 148 149 150 151 152 |
# File 'lib/dm-core/associations/relationship.rb', line 147 def child_model? child_model true rescue NameError false end |
#child_model_name ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
TODO: document
156 157 158 |
# File 'lib/dm-core/associations/relationship.rb', line 156 def child_model_name @child_model ? child_model.name : @child_model_name end |
#eager_load(source, query = nil) ⇒ Collection
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Eager load the collection using the source as a base
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/dm-core/associations/relationship.rb', line 278 def eager_load(source, query = nil) target_maps = Hash.new { |h,k| h[k] = [] } collection_query = query_for(source, query) # TODO: create an object that wraps this logic, and when the first # kicker is fired, then it'll load up the collection, and then # populate all the other methods collection = source.model.all(collection_query).each do |target| target_maps[target_key.get(target)] << target end Array(source).each do |source| key = target_key.typecast(source_key.get(source)) eager_load_targets(source, target_maps[key], query) end collection end |
#eql?(other) ⇒ Boolean
Compares another Relationship for equality
338 339 340 341 |
# File 'lib/dm-core/associations/relationship.rb', line 338 def eql?(other) return true if equal?(other) instance_of?(other.class) && cmp?(other, :eql?) end |
#get(resource, other_query = nil) ⇒ Object
Loads and returns “other end” of the association. Must be implemented in subclasses.
237 238 239 |
# File 'lib/dm-core/associations/relationship.rb', line 237 def get(resource, other_query = nil) raise NotImplementedError, "#{self.class}#get not implemented" end |
#get!(resource) ⇒ Object
Gets “other end” of the association directly as @ivar on given resource. Subclasses usually use implementation of this class.
246 247 248 |
# File 'lib/dm-core/associations/relationship.rb', line 246 def get!(resource) resource.instance_variable_get(instance_variable_name) end |
#inverse ⇒ Object
Get the inverse relationship from the target model
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
# File 'lib/dm-core/associations/relationship.rb', line 365 def inverse return @inverse if defined?(@inverse) if kind_of_inverse?([:inverse]) return @inverse = [:inverse] end relationships = target_model.relationships(relative_target_repository_name).values @inverse = relationships.detect { |relationship| inverse?(relationship) } || invert @inverse.child_key @inverse end |
#loaded?(resource) ⇒ Boolean
Checks if “other end” of association is loaded on given resource.
303 304 305 306 307 |
# File 'lib/dm-core/associations/relationship.rb', line 303 def loaded?(resource) assert_kind_of 'resource', resource, source_model resource.instance_variable_defined?(instance_variable_name) end |
#parent_key ⇒ PropertySet
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a set of keys that identify parent model
219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/dm-core/associations/relationship.rb', line 219 def parent_key return @parent_key if defined?(@parent_key) repository_name = parent_repository_name || child_repository_name properties = parent_model.properties(repository_name) @parent_key = if @parent_properties parent_key = properties.values_at(*@parent_properties) properties.class.new(parent_key).freeze else properties.key end end |
#parent_model ⇒ Resource
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns model class used by parent side of the relationship
192 193 194 195 196 |
# File 'lib/dm-core/associations/relationship.rb', line 192 def parent_model @parent_model ||= (@child_model || Object).find_const(parent_model_name) rescue NameError raise NameError, "Cannot find the parent_model #{parent_model_name} for #{child_model_name} in #{name}" end |
#parent_model? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
TODO: document
200 201 202 203 204 205 |
# File 'lib/dm-core/associations/relationship.rb', line 200 def parent_model? parent_model true rescue NameError false end |
#parent_model_name ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
TODO: document
209 210 211 |
# File 'lib/dm-core/associations/relationship.rb', line 209 def parent_model_name @parent_model ? parent_model.name : @parent_model_name end |
#query_for(source, other_query = nil) ⇒ Object
Creates and returns Query instance that fetches target resource(s) (ex.: articles) for given target resource (ex.: author)
121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/dm-core/associations/relationship.rb', line 121 def query_for(source, other_query = nil) repository_name = relative_target_repository_name_for(source) DataMapper.repository(repository_name).scope do query = target_model.query.dup query.update(self.query) query.update(source_scope(source)) query.update(other_query) if other_query query.update(:fields => query.fields | target_key) end end |
#relative_target_repository_name ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
TODO: document
384 385 386 |
# File 'lib/dm-core/associations/relationship.rb', line 384 def relative_target_repository_name target_repository_name || source_repository_name end |
#relative_target_repository_name_for(source) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
TODO: document
390 391 392 393 394 395 396 |
# File 'lib/dm-core/associations/relationship.rb', line 390 def relative_target_repository_name_for(source) target_repository_name || if source.respond_to?(:repository) source.repository.name else source_repository_name end end |
#set(resource, association) ⇒ Object
Sets value of the “other end” of association on given resource. Must be implemented in subclasses.
254 255 256 |
# File 'lib/dm-core/associations/relationship.rb', line 254 def set(resource, association) raise NotImplementedError, "#{self.class}#set not implemented" end |
#set!(resource, association) ⇒ Object
Sets “other end” of the association directly as @ivar on given resource. Subclasses usually use implementation of this class.
263 264 265 |
# File 'lib/dm-core/associations/relationship.rb', line 263 def set!(resource, association) resource.instance_variable_set(instance_variable_name, association) end |
#source_scope(source) ⇒ Hash
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a hash of conditions that scopes query that fetches target object
113 114 115 |
# File 'lib/dm-core/associations/relationship.rb', line 113 def source_scope(source) { inverse => source } end |
#valid?(source) ⇒ Boolean
Test the source to see if it is a valid target
318 319 320 321 322 323 324 325 326 327 |
# File 'lib/dm-core/associations/relationship.rb', line 318 def valid?(source) return true if source.nil? case source when Array, Collection then valid_collection?(source) when Resource then valid_resource?(source) else raise ArgumentError, "+source+ should be an Array or Resource, but was a #{source.class.name}" end end |