Class: DataMapper::Associations::ManyToMany::Relationship

Inherits:
OneToMany::Relationship show all
Extended by:
Chainable
Defined in:
lib/dm-core/associations/many_to_many.rb

Constant Summary

OPTIONS =
superclass::OPTIONS.dup << :through << :via

Instance Attribute Summary

Attributes inherited from Relationship

#child_repository_name, #instance_variable_name, #max, #min, #name, #options, #parent_repository_name, #reader_visibility, #writer_visibility

Instance Method Summary collapse

Methods included from Chainable

chainable, extendable

Methods inherited from OneToMany::Relationship

#collection_for, #default_for, #get, #get_collection, #lazy_load, #set, #set_collection

Methods inherited from Relationship

#==, #child_model, #child_model?, #child_model_name, #eql?, #field, #get, #get!, #hash, #inverse, #loaded?, #parent_key, #parent_model, #parent_model?, #parent_model_name, #query_for, #relative_target_repository_name, #relative_target_repository_name_for, #set, #set!, #valid?

Methods included from Subject

#default?, #default_for

Methods included from DataMapper::Assertions

#assert_kind_of

Instance Method Details

#child_keyDataMapper::PropertySet Also known as: target_key

Returns a set of keys that identify the target model

Returns:



15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/dm-core/associations/many_to_many.rb', line 15

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

#eager_load(source, other_query = nil) ⇒ ManyToMany::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

Parameters:

  • source (Resource, Collection)

    the source to query with

  • other_query (Query, Hash) (defaults to: nil)

    optional query to restrict the collection

Returns:



161
162
163
164
# File 'lib/dm-core/associations/many_to_many.rb', line 161

def eager_load(source, other_query = nil)
  # FIXME: enable SEL for m:m relationships
  source.model.all(query_for(source, other_query))
end

#finalizeself

Initialize the chain for “many to many” relationships

Returns:

  • (self)


132
133
134
135
136
# File 'lib/dm-core/associations/many_to_many.rb', line 132

def finalize
  through
  via
  self
end


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/dm-core/associations/many_to_many.rb', line 110

def links
  return @links if defined?(@links)

  @links = []
  links  = [ through, via ]

  while relationship = links.shift
    if relationship.respond_to?(:links)
      links.unshift(*relationship.links)
    else
      @links << relationship
    end
  end

  @links.freeze
end

#queryObject

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.



144
145
146
147
148
# File 'lib/dm-core/associations/many_to_many.rb', line 144

def query
  # TODO: consider making this a query_for method, so that ManyToMany::Relationship#query only
  # returns the query supplied in the definition
  @many_to_many_query ||= super.merge(:links => links).freeze
end

#source_scope(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.



139
140
141
# File 'lib/dm-core/associations/many_to_many.rb', line 139

def source_scope(source)
  { through.inverse => source }
end

#throughObject

Intermediate association for through model relationships

Example: for :bugs association in

class Software::Engineer

include DataMapper::Resource

has n, :missing_tests
has n, :bugs, :through => :missing_tests

end

through is :missing_tests

TODO: document a case when through option is a model and not an association name



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/dm-core/associations/many_to_many.rb', line 51

def through
  return @through if defined?(@through)

  @through = options[:through]

  if @through.kind_of?(Associations::Relationship)
    return @through
  end

  model           = source_model
  repository_name = source_repository_name
  relationships   = model.relationships(repository_name)
  name            = through_relationship_name

  @through = relationships[name] ||
    DataMapper.repository(repository_name) do
      model.has(min..max, name, through_model, one_to_many_options)
    end

  @through.child_key

  @through
end

#viaObject



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/dm-core/associations/many_to_many.rb', line 76

def via
  return @via if defined?(@via)

  @via = options[:via]

  if @via.kind_of?(Associations::Relationship)
    return @via
  end

  name            = self.name
  through         = self.through
  repository_name = through.relative_target_repository_name
  through_model   = through.target_model
  relationships   = through_model.relationships(repository_name)
  singular_name   = DataMapper::Inflector.singularize(name.to_s).to_sym

  @via = relationships[@via] ||
    relationships[name]      ||
    relationships[singular_name]

  @via ||= if anonymous_through_model?
    DataMapper.repository(repository_name) do
      through_model.belongs_to(singular_name, target_model, many_to_one_options)
    end
  else
    raise UnknownRelationshipError, "No relationships named #{name} or #{singular_name} in #{through_model}"
  end

  @via.child_key

  @via
end