Initializes relationships hash for extended model class.
When model calls has n, has 1 or belongs_to, relationships are stored in that hash: keys are repository names and values are relationship sets.
@api private
# File lib/dm-core/model/relationship.rb, line 19 19: def self.extended(model) 20: model.instance_variable_set(:@relationships, {}) 21: end
A shorthand, clear syntax for defining many-to-one resource relationships.
* belongs_to :user # many to one user * belongs_to :friend, :model => 'User' # many to one friend * belongs_to :reference, :repository => :pubmed # association for repository other than default
@param name [Symbol]
the name that the association will be referenced by
@param *args [Model, Hash] model and/or options hash
@option *args :model[Model, String] The name of the class to associate with, if omitted
then the association name is assumed to match the class name
@option *args :repository[Symbol] name of child model repository
@return [Association::Relationship] The association created
should not be accessed directly
@api public
# File lib/dm-core/model/relationship.rb, line 156 156: def belongs_to(name, *args) 157: name = name.to_sym 158: model_name = self.name 159: model = extract_model(args) 160: options = extract_options(args) 161: 162: if options.key?(:through) 163: raise "#{model_name}#belongs_to with :through is deprecated, use 'has 1, :#{name}, #{options.inspect}' in #{model_name} instead (#{caller.first})" 164: elsif options.key?(:model) && model 165: raise ArgumentError, 'should not specify options[:model] if passing the model in the third argument' 166: end 167: 168: assert_valid_options(options) 169: 170: model ||= options.delete(:model) 171: 172: repository_name = repository.name 173: 174: # TODO: change to source_repository_name and target_respository_name 175: options[:child_repository_name] = repository_name 176: options[:parent_repository_name] = options.delete(:repository) 177: 178: relationship = Associations::ManyToOne::Relationship.new(name, self, model, options) 179: 180: relationships(repository_name) << relationship 181: 182: descendants.each do |descendant| 183: descendant.relationships(repository_name) << relationship 184: end 185: 186: create_relationship_reader(relationship) 187: create_relationship_writer(relationship) 188: 189: relationship 190: end
A shorthand, clear syntax for defining one-to-one, one-to-many and many-to-many resource relationships.
* has 1, :friend # one friend * has n, :friends # many friends * has 1..3, :friends # many friends (at least 1, at most 3) * has 3, :friends # many friends (exactly 3) * has 1, :friend, 'User' # one friend with the class User * has 3, :friends, :through => :friendships # many friends through the friendships relationship
@param cardinality [Integer, Range, Infinity]
cardinality that defines the association type and constraints
@param name [Symbol]
the name that the association will be referenced by
@param *args [Model, Hash] model and/or options hash
@option *args :through[Symbol] A association that this join should go through to form
a many-to-many association
@option *args :model[Model, String] The name of the class to associate with, if omitted
then the association name is assumed to match the class name
@option *args :repository[Symbol] name of child model repository
@return [Association::Relationship] the relationship that was
created to reflect either a one-to-one, one-to-many or many-to-many relationship
@raise [ArgumentError] if the cardinality was not understood. Should be a
Integer, Range or Infinity(n)
@api public
# File lib/dm-core/model/relationship.rb, line 96 96: def has(cardinality, name, *args) 97: name = name.to_sym 98: model = extract_model(args) 99: options = extract_options(args) 100: 101: min, max = extract_min_max(cardinality) 102: options.update(:min => min, :max => max) 103: 104: assert_valid_options(options) 105: 106: if options.key?(:model) && model 107: raise ArgumentError, 'should not specify options[:model] if passing the model in the third argument' 108: end 109: 110: model ||= options.delete(:model) 111: 112: repository_name = repository.name 113: 114: # TODO: change to :target_respository_name and :source_repository_name 115: options[:child_repository_name] = options.delete(:repository) 116: options[:parent_repository_name] = repository_name 117: 118: klass = if max > 1 119: options.key?(:through) ? Associations::ManyToMany::Relationship : Associations::OneToMany::Relationship 120: else 121: Associations::OneToOne::Relationship 122: end 123: 124: relationship = klass.new(name, model, self, options) 125: 126: relationships(repository_name) << relationship 127: 128: descendants.each do |descendant| 129: descendant.relationships(repository_name) << relationship 130: end 131: 132: create_relationship_reader(relationship) 133: create_relationship_writer(relationship) 134: 135: relationship 136: end
When DataMapper model is inherited, relationships of parent are duplicated and copied to subclass model
@api private
# File lib/dm-core/model/relationship.rb, line 27 27: def inherited(model) 28: model.instance_variable_set(:@relationships, {}) 29: 30: @relationships.each do |repository_name, relationships| 31: model_relationships = model.relationships(repository_name) 32: relationships.each { |relationship| model_relationships << relationship } 33: end 34: 35: super 36: end
Used to express unlimited cardinality of association, see has
@api public
# File lib/dm-core/model/relationship.rb, line 63 63: def n 64: Infinity 65: end
Returns copy of relationships set in given repository.
@param [Symbol] repository_name
Name of the repository for which relationships set is returned
@return [RelationshipSet] relationships set for given repository
@api semipublic
# File lib/dm-core/model/relationship.rb, line 45 45: def relationships(repository_name = default_repository_name) 46: # TODO: create RelationshipSet#copy that will copy the relationships, but assign the 47: # new Relationship objects to a supplied repository and model. dup does not really 48: # do what is needed 49: 50: default_repository_name = self.default_repository_name 51: 52: @relationships[repository_name] ||= if repository_name == default_repository_name 53: RelationshipSet.new 54: else 55: relationships(default_repository_name).dup 56: end 57: end
Validates options of association method like belongs_to or has: verifies types of cardinality bounds, repository, association class, keys and possible values of :through option.
@api private
# File lib/dm-core/model/relationship.rb, line 250 250: def assert_valid_options(options) 251: # TODO: update to match Query#assert_valid_options 252: # - perform options normalization elsewhere 253: 254: if options.key?(:min) && options.key?(:max) 255: min = options[:min] 256: max = options[:max] 257: 258: min = min.to_int unless min == Infinity 259: max = max.to_int unless max == Infinity 260: 261: if min == Infinity && max == Infinity 262: raise ArgumentError, 'Cardinality may not be n..n. The cardinality specifies the min/max number of results from the association' 263: elsif min > max 264: raise ArgumentError, "Cardinality min (#{min}) cannot be larger than the max (#{max})" 265: elsif min < 0 266: raise ArgumentError, "Cardinality min much be greater than or equal to 0, but was #{min}" 267: elsif max < 1 268: raise ArgumentError, "Cardinality max much be greater than or equal to 1, but was #{max}" 269: end 270: end 271: 272: if options.key?(:repository) 273: options[:repository] = options[:repository].to_sym 274: end 275: 276: if options.key?(:class_name) 277: raise "+options[:class_name]+ is deprecated, use :model instead (#{caller[1]})" 278: elsif options.key?(:remote_name) 279: raise "+options[:remote_name]+ is deprecated, use :via instead (#{caller[1]})" 280: end 281: 282: if options.key?(:through) 283: assert_kind_of 'options[:through]', options[:through], Symbol, Module 284: end 285: 286: [ :via, :inverse ].each do |key| 287: if options.key?(key) 288: assert_kind_of "options[#{key.inspect}]", options[key], Symbol, Associations::Relationship 289: end 290: end 291: 292: # TODO: deprecate :child_key and :parent_key in favor of :source_key and 293: # :target_key (will mean something different for each relationship) 294: 295: [ :child_key, :parent_key ].each do |key| 296: if options.key?(key) 297: options[key] = Array(options[key]) 298: end 299: end 300: 301: if options.key?(:limit) 302: raise ArgumentError, '+options[:limit]+ should not be specified on a relationship' 303: end 304: end
Dynamically defines reader method
@api private
# File lib/dm-core/model/relationship.rb, line 323 323: def create_relationship_reader(relationship) 324: name = relationship.name 325: reader_name = name.to_s 326: 327: return if method_defined?(reader_name) 328: 329: reader_visibility = relationship.reader_visibility 330: 331: relationship_module.module_eval #{reader_visibility} def #{reader_name}(query = nil) # TODO: when no query is passed in, return the results from # the ivar directly. This will require that the ivar # actually hold the resource/collection, and in the case # of 1:1, the underlying collection is hidden in a # private ivar, and the resource is in a known ivar persistence_state.get(relationships[#{name.inspect}], query) end, __FILE__, __LINE__ + 1 332: end
Dynamically defines writer method
@api private
# File lib/dm-core/model/relationship.rb, line 348 348: def create_relationship_writer(relationship) 349: name = relationship.name 350: writer_name = "#{name}=" 351: 352: return if method_defined?(writer_name) 353: 354: writer_visibility = relationship.writer_visibility 355: 356: relationship_module.module_eval #{writer_visibility} def #{writer_name}(target) relationship = relationships[#{name.inspect}] self.persistence_state = persistence_state.set(relationship, target) persistence_state.get(relationship) end, __FILE__, __LINE__ + 1 357: end
A support method for converting Integer, Range or Infinity values into two values representing the minimum and maximum cardinality of the association
@return [Array] A pair of integers, min and max
@api private
# File lib/dm-core/model/relationship.rb, line 235 235: def extract_min_max(cardinality) 236: case cardinality 237: when Integer then [ cardinality, cardinality ] 238: when Range then [ cardinality.first, cardinality.last ] 239: when Infinity then [ 0, Infinity ] 240: else 241: assert_kind_of 'options', options, Integer, Range, Infinity.class 242: end 243: end
Extract the model from an Array of arguments
@param [Array(Model, String, Hash)]
The arguments passed to an relationship declaration
@return [Model, #]
target model for the association
@api private
# File lib/dm-core/model/relationship.rb, line 203 203: def extract_model(args) 204: model = args.first 205: 206: if model.kind_of?(Model) 207: model 208: elsif model.respond_to?(:to_str) 209: model.to_str 210: else 211: nil 212: end 213: end
Extract the model from an Array of arguments
@param [Array(Model, String, Hash)]
The arguments passed to an relationship declaration
@return [Hash]
options for the association
@api private
# File lib/dm-core/model/relationship.rb, line 224 224: def extract_options(args) 225: options = args.last 226: options.respond_to?(:to_hash) ? options.to_hash.dup : {} 227: end
@api public
# File lib/dm-core/model/relationship.rb, line 367 367: def method_missing(method, *args, &block) 368: if relationship = relationships(repository_name)[method] 369: return Query::Path.new([ relationship ]) 370: end 371: 372: super 373: end
Defines the anonymous module that is used to add relationships. Using a single module here prevents having a very large number of anonymous modules, where each property has their own module. @api private
# File lib/dm-core/model/relationship.rb, line 310 310: def relationship_module 311: @relationship_module ||= begin 312: mod = Module.new 313: class_eval do 314: include mod 315: end 316: mod 317: end 318: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.