Module: Dynomite::Associations::ManyAssociation

Includes:
Association, Enumerable
Included in:
HasAndBelongsToMany, HasMany
Defined in:
lib/dynomite/associations/many_association.rb

Instance Attribute Summary collapse

Attributes included from Association

#loaded, #name, #options, #source

Instance Method Summary collapse

Methods included from Association

#coerce_to_id, #coerce_to_item, #declaration_field_name, #declaration_field_type, #loaded?, #reader_target, #reset, #target

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object

Delegate methods we don’t find directly to the records array.



233
234
235
236
237
238
239
# File 'lib/dynomite/associations/many_association.rb', line 233

def method_missing(method, *args)
  if records.respond_to?(method)
    records.send(method, *args)
  else
    super
  end
end

Instance Attribute Details

#queryObject

Returns the value of attribute query.



6
7
8
# File 'lib/dynomite/associations/many_association.rb', line 6

def query
  @query
end

Instance Method Details

#<<(item) ⇒ Dynomite::Item Also known as: associate

Add an item or array of items to an association.

tag.posts << post
tag.posts << [post1, post2, post3]

This preserves the current records in the association (if any) and adds the item to the target association if it is detected to exist.

It saves both models immediately - the source model and the target one so any not saved changes will be saved as well.

Parameters:

  • item (Dynomite::Item|Array)

    model (or array of models) to add to the association

Returns:



68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/dynomite/associations/many_association.rb', line 68

def <<(item)
  item = coerce_to_item(item)
  # normal relationship
  associate_one_way(item)

  # inverse relationship
  if target_association
    Array(item).each { |obj| obj.send(target_association).associate_one_way(source) }
  end

  item
end

#==(other) ⇒ Boolean

Is this array equal to the association’s records?

Returns:

  • (Boolean)

    true/false



228
229
230
# File 'lib/dynomite/associations/many_association.rb', line 228

def ==(other)
  records == Array(other)
end

#associate_one_way(item) ⇒ Object



82
83
84
85
86
87
# File 'lib/dynomite/associations/many_association.rb', line 82

def associate_one_way(item)
  items = Array(item)
  ids = items.collect { |o| coerce_to_id(o) }
  ids = source_ids.merge(ids)
  source.update_attribute_presence(source_attribute, ids)
end

#create(attributes = {}) ⇒ Dynomite::Item|Array

Create a new instance of the target class, persist it and add directly to the association.

tag.posts.create(title: 'foo')

Several models can be created at once when an array of attributes specified:

tag.posts.create([{ title: 'foo' }, {title: 'bar'} ])

Parameters:

  • attributes (Hash) (defaults to: {})

    attribute values for the new item

Returns:



170
171
172
# File 'lib/dynomite/associations/many_association.rb', line 170

def create(attributes = {})
  self << target_class.create(attributes)
end

#create!(attributes = {}) ⇒ Dynomite::Item|Array

Create a new instance of the target class, persist it and add directly to the association.

tag.posts.create!(title: 'foo')

Several models can be created at once when an array of attributes specified:

tag.posts.create!([{ title: 'foo' }, {title: 'bar'} ])

If the creation fails an exception will be raised.

Parameters:

  • attributes (Hash) (defaults to: {})

    attribute values for the new item

Returns:



154
155
156
# File 'lib/dynomite/associations/many_association.rb', line 154

def create!(attributes = {})
  self << target_class.create!(attributes)
end

#delete_allObject

Deletes all members of the association and removes them from the association.

tag.posts.delete_all


197
198
199
200
201
# File 'lib/dynomite/associations/many_association.rb', line 197

def delete_all
  objs = target
  source.update_attribute_presence(source_attribute, nil)
  objs.each(&:delete)
end

#destroy_allObject

Destroys all members of the association and removes them from the association.

tag.posts.destroy_all


186
187
188
189
190
# File 'lib/dynomite/associations/many_association.rb', line 186

def destroy_all
  objs = target
  source.update_attribute_presence(source_attribute, nil)
  objs.each(&:destroy)
end

#disassociate(*items) ⇒ Dynomite::Item|Array

Removes an item or array of items from the association.

tag.posts.disassociate(post)
tag.posts.disassociate(post1, post2, post3)
tag.posts.disassociate([post1, post2, post3])

This removes their records from the association field on the source, and attempts to remove the source from the target association if it is detected to exist.

It saves both models immediately - the source model and the target one so any not saved changes will be saved as well.

Parameters:

  • item (Dynomite::Item|Array)

    model (or array of models) to remove from the association

Returns:



104
105
106
107
108
109
110
111
112
113
114
# File 'lib/dynomite/associations/many_association.rb', line 104

def disassociate(*items)
  items.flatten!
  items.map! { |item| coerce_to_item(item) }
  # normal relationship
  items.each { |item| disassociate_one_way(item) }

  # inverse relationship
  if target_association
    items.each { |obj| obj.send(target_association).disassociate_one_way(source) }
  end
end

#disassociate_allObject



121
122
123
124
125
126
# File 'lib/dynomite/associations/many_association.rb', line 121

def disassociate_all
  # target is all items. IE: user.posts
  target.each do |item|
    disassociate(item)
  end
end

#disassociate_one_way(item) ⇒ Object



116
117
118
119
# File 'lib/dynomite/associations/many_association.rb', line 116

def disassociate_one_way(item)
  ids = source_ids - Array(coerce_to_id(item))
  source.update_attribute_presence(source_attribute, ids)
end

#each(&block) ⇒ Dynomite::Item

Create a new instance of the target class and add it directly to the association. If the create fails an exception will be raised.

Returns:



177
178
179
# File 'lib/dynomite/associations/many_association.rb', line 177

def each(&block)
  records.each(&block)
end

#find_targetObject

Returns the has many association. IE: user.posts.

Returns:

  • the has many association. IE: user.posts



19
20
21
22
23
24
25
26
27
28
29
# File 'lib/dynomite/associations/many_association.rb', line 19

def find_target
  return [] if source_ids.empty?

  # IE: user.posts - target class is Post
  if target_class.partition_key_field == "id" && target_class.sort_key_field.nil?
    # Quick find lookup
    Array(target_class.find(source_ids.to_a, raise_error: false))
  else
    relation.to_a
  end
end

#include?(item) ⇒ Boolean

Delegate include? to the records.

Returns:

  • (Boolean)


51
52
53
# File 'lib/dynomite/associations/many_association.rb', line 51

def include?(item)
  records.include?(item)
end

#initialize(*args) ⇒ Object



8
9
10
11
# File 'lib/dynomite/associations/many_association.rb', line 8

def initialize(*args)
  @query = {}
  super
end

#recordsObject Also known as: all



37
38
39
40
41
42
43
# File 'lib/dynomite/associations/many_association.rb', line 37

def records
  if query.empty?
    target
  else
    results_with_query(target)
  end
end

#relationObject



31
32
33
34
35
# File 'lib/dynomite/associations/many_association.rb', line 31

def relation
  return [] if source_ids.empty? # check again in case user calls relation directly. IE: user.posts.relation
  # Slow scan lookup because of the in operator
  target_class.where("id.in": source_ids.to_a)
end

#setter(item) ⇒ Dynomite::Item|Array

Replace an association with item or array of items. This removes all of the existing associated records and replaces them with the passed item(s), and associates the target association if it is detected to exist.

Parameters:

  • item (Dynomite::Item)

    the item (or array of items) to add to the association

Returns:



134
135
136
137
138
# File 'lib/dynomite/associations/many_association.rb', line 134

def setter(item)
  target.each { |i| disassociate(i) }
  self << item
  item
end

#where(args) ⇒ Dynomite::Association

Naive association filtering.

tag.posts.where(title: 'foo')

It loads lazily all the associated models and checks provided conditions. That’s why only equality conditions can be specified.

Parameters:

  • args (Hash)

    A hash of attributes; each must match every returned item’s attribute exactly.

Returns:

  • (Dynomite::Association)

    the association this method was called on (for chaining purposes)



218
219
220
221
222
223
# File 'lib/dynomite/associations/many_association.rb', line 218

def where(args)
  filtered = clone
  filtered.query = query.clone
  args.each { |k, v| filtered.query[k] = v }
  filtered
end