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 collapse

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

Instance Attribute Summary

Attributes inherited from Relationship

#child_properties, #child_repository_name, #instance_variable_name, #max, #min, #name, #options, #parent_properties, #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:



157
158
159
160
# File 'lib/dm-core/associations/many_to_many.rb', line 157

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)


128
129
130
131
132
# File 'lib/dm-core/associations/many_to_many.rb', line 128

def finalize
  through
  via
  self
end


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/dm-core/associations/many_to_many.rb', line 106

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.



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

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.



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

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
# File 'lib/dm-core/associations/many_to_many.rb', line 51

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

  @through = options[:through]

  return @through if @through.is_a?(Associations::Relationship)

  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



74
75
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
# File 'lib/dm-core/associations/many_to_many.rb', line 74

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

  @via = options[:via]

  return @via if @via.is_a?(Associations::Relationship)

  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