dynamoid 3.3.0 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +104 -1
  3. data/README.md +146 -52
  4. data/lib/dynamoid.rb +1 -0
  5. data/lib/dynamoid/adapter.rb +20 -7
  6. data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +70 -37
  7. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/batch_get_item.rb +3 -0
  8. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +20 -12
  9. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +5 -4
  10. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/backoff.rb +2 -2
  11. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb +2 -3
  12. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/start_key.rb +2 -2
  13. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +4 -2
  14. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +4 -2
  15. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/table.rb +1 -0
  16. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/until_past_table_status.rb +2 -1
  17. data/lib/dynamoid/application_time_zone.rb +1 -0
  18. data/lib/dynamoid/associations.rb +182 -19
  19. data/lib/dynamoid/associations/association.rb +10 -2
  20. data/lib/dynamoid/associations/belongs_to.rb +2 -1
  21. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +2 -1
  22. data/lib/dynamoid/associations/has_many.rb +2 -1
  23. data/lib/dynamoid/associations/has_one.rb +2 -1
  24. data/lib/dynamoid/associations/many_association.rb +68 -23
  25. data/lib/dynamoid/associations/single_association.rb +31 -4
  26. data/lib/dynamoid/components.rb +2 -0
  27. data/lib/dynamoid/config.rb +15 -3
  28. data/lib/dynamoid/config/backoff_strategies/constant_backoff.rb +1 -0
  29. data/lib/dynamoid/config/backoff_strategies/exponential_backoff.rb +1 -0
  30. data/lib/dynamoid/config/options.rb +1 -0
  31. data/lib/dynamoid/criteria.rb +9 -1
  32. data/lib/dynamoid/criteria/chain.rb +421 -46
  33. data/lib/dynamoid/criteria/ignored_conditions_detector.rb +3 -3
  34. data/lib/dynamoid/criteria/key_fields_detector.rb +31 -10
  35. data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +3 -2
  36. data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +1 -1
  37. data/lib/dynamoid/dirty.rb +119 -64
  38. data/lib/dynamoid/document.rb +133 -46
  39. data/lib/dynamoid/dumping.rb +9 -0
  40. data/lib/dynamoid/dynamodb_time_zone.rb +1 -0
  41. data/lib/dynamoid/errors.rb +2 -0
  42. data/lib/dynamoid/fields.rb +251 -39
  43. data/lib/dynamoid/fields/declare.rb +86 -0
  44. data/lib/dynamoid/finders.rb +69 -32
  45. data/lib/dynamoid/identity_map.rb +6 -0
  46. data/lib/dynamoid/indexes.rb +86 -17
  47. data/lib/dynamoid/loadable.rb +2 -2
  48. data/lib/dynamoid/log/formatter.rb +26 -0
  49. data/lib/dynamoid/middleware/identity_map.rb +1 -0
  50. data/lib/dynamoid/persistence.rb +502 -104
  51. data/lib/dynamoid/persistence/import.rb +2 -1
  52. data/lib/dynamoid/persistence/save.rb +1 -0
  53. data/lib/dynamoid/persistence/update_fields.rb +5 -2
  54. data/lib/dynamoid/persistence/update_validations.rb +18 -0
  55. data/lib/dynamoid/persistence/upsert.rb +5 -3
  56. data/lib/dynamoid/primary_key_type_mapping.rb +1 -0
  57. data/lib/dynamoid/railtie.rb +1 -0
  58. data/lib/dynamoid/tasks.rb +3 -1
  59. data/lib/dynamoid/tasks/database.rb +1 -0
  60. data/lib/dynamoid/type_casting.rb +12 -2
  61. data/lib/dynamoid/undumping.rb +8 -0
  62. data/lib/dynamoid/validations.rb +6 -1
  63. data/lib/dynamoid/version.rb +1 -1
  64. metadata +48 -75
  65. data/.coveralls.yml +0 -1
  66. data/.document +0 -5
  67. data/.gitignore +0 -74
  68. data/.rspec +0 -2
  69. data/.rubocop.yml +0 -71
  70. data/.rubocop_todo.yml +0 -55
  71. data/.travis.yml +0 -44
  72. data/Appraisals +0 -22
  73. data/Gemfile +0 -8
  74. data/Rakefile +0 -46
  75. data/Vagrantfile +0 -29
  76. data/docker-compose.yml +0 -7
  77. data/dynamoid.gemspec +0 -57
  78. data/gemfiles/rails_4_2.gemfile +0 -9
  79. data/gemfiles/rails_5_0.gemfile +0 -8
  80. data/gemfiles/rails_5_1.gemfile +0 -8
  81. data/gemfiles/rails_5_2.gemfile +0 -8
  82. data/gemfiles/rails_6_0.gemfile +0 -8
@@ -5,6 +5,7 @@ require_relative 'middleware/limit'
5
5
  require_relative 'middleware/start_key'
6
6
 
7
7
  module Dynamoid
8
+ # @private
8
9
  module AdapterPlugin
9
10
  class AwsSdkV3
10
11
  class Scan
@@ -21,8 +22,8 @@ module Dynamoid
21
22
  request = build_request
22
23
 
23
24
  Enumerator.new do |yielder|
24
- api_call = -> (request) do
25
- client.scan(request).tap do |response|
25
+ api_call = lambda do |req|
26
+ client.scan(req).tap do |response|
26
27
  yielder << response
27
28
  end
28
29
  end
@@ -85,6 +86,7 @@ module Dynamoid
85
86
 
86
87
  def attributes_to_get
87
88
  return if options[:project].nil?
89
+
88
90
  options[:project].map(&:to_s)
89
91
  end
90
92
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
+ # @private
4
5
  module AdapterPlugin
5
6
  class AwsSdkV3
6
7
  # Represents a table. Exposes data from the "DescribeTable" API call, and also
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
+ # @private
4
5
  module AdapterPlugin
5
6
  class AwsSdkV3
6
7
  class UntilPastTableStatus
@@ -32,7 +33,7 @@ module Dynamoid
32
33
  # See: http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#describe_table-instance_method
33
34
  rescue Aws::DynamoDB::Errors::ResourceNotFoundException => e
34
35
  case status
35
- when :creating then
36
+ when :creating
36
37
  if counter >= Dynamoid::Config.sync_retry_max_times
37
38
  Dynamoid.logger.warn "Waiting on table metadata for #{table_name} (check #{counter})"
38
39
  retry # start over at first line of begin, does not reset counter
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
+ # @private
4
5
  module ApplicationTimeZone
5
6
  def self.at(value)
6
7
  case Dynamoid::Config.application_timezone
@@ -25,12 +25,55 @@ module Dynamoid
25
25
  end
26
26
 
27
27
  module ClassMethods
28
- # create a has_many association for this document.
28
+ # Declare a +has_many+ association for this document.
29
29
  #
30
- # @param [Symbol] name the name of the association
31
- # @param [Hash] options options to pass to the association constructor
30
+ # class Category
31
+ # include Dynamoid::Document
32
+ #
33
+ # has_many :posts
34
+ # end
35
+ #
36
+ # Association is an enumerable collection and supports following addition
37
+ # operations:
38
+ #
39
+ # * +create+
40
+ # * +create!+
41
+ # * +destroy_all+
42
+ # * +delete_all+
43
+ # * +delete+
44
+ # * +<<+
45
+ # * +where+
46
+ # * +all+
47
+ # * +empty?+
48
+ # * +size+
49
+ #
50
+ # When a name of an associated class doesn't match an association name a
51
+ # class name should be specified explicitly either with +class+ or
52
+ # +class_name+ option:
53
+ #
54
+ # has_many :labels, class: Tag
55
+ # has_many :labels, class_name: 'Tag'
56
+ #
57
+ # When associated class has own +belongs_to+ association to
58
+ # the current class and the name doesn't match a name of the current
59
+ # class this name can be specified with +inverse_of+ option:
60
+ #
61
+ # class Post
62
+ # include Dynamoid::Document
63
+ #
64
+ # belongs_to :item, class_name: 'Tag'
65
+ # end
66
+ #
67
+ # class Tag
68
+ # include Dynamoid::Document
69
+ #
70
+ # has_many :posts, inverse_of: :item
71
+ # end
72
+ #
73
+ # @param name [Symbol] the name of the association
74
+ # @param options [Hash] options to pass to the association constructor
32
75
  # @option options [Class] :class the target class of the has_many association; that is, the belongs_to class
33
- # @option options [Symbol] :class_name the name of the target class of the association; that is, the name of the belongs_to class
76
+ # @option options [String] :class_name the name of the target class of the association; that is, the name of the belongs_to class
34
77
  # @option options [Symbol] :inverse_of the name of the association on the target class; that is, if the class has a belongs_to association, the name of that association
35
78
  #
36
79
  # @since 0.2.0
@@ -38,12 +81,47 @@ module Dynamoid
38
81
  association(:has_many, name, options)
39
82
  end
40
83
 
41
- # create a has_one association for this document.
84
+ # Declare a +has_one+ association for this document.
85
+ #
86
+ # class Image
87
+ # include Dynamoid::Document
42
88
  #
43
- # @param [Symbol] name the name of the association
44
- # @param [Hash] options options to pass to the association constructor
89
+ # has_one :post
90
+ # end
91
+ #
92
+ # Association supports following operations:
93
+ #
94
+ # * +create+
95
+ # * +create!+
96
+ # * +delete+
97
+ #
98
+ # When a name of an associated class doesn't match an association name a
99
+ # class name should be specified explicitly either with +class+ or
100
+ # +class_name+ option:
101
+ #
102
+ # has_one :item, class: Post
103
+ # has_one :item, class_name: 'Post'
104
+ #
105
+ # When associated class has own +belong_to+ association to the current
106
+ # class and the name doesn't match a name of the current class this name
107
+ # can be specified with +inverse_of+ option:
108
+ #
109
+ # class Post
110
+ # include Dynamoid::Document
111
+ #
112
+ # belongs_to :logo, class_name: 'Image'
113
+ # end
114
+ #
115
+ # class Image
116
+ # include Dynamoid::Document
117
+ #
118
+ # has_one :post, inverse_of: :logo
119
+ # end
120
+ #
121
+ # @param name [Symbol] the name of the association
122
+ # @param options [Hash] options to pass to the association constructor
45
123
  # @option options [Class] :class the target class of the has_one association; that is, the belongs_to class
46
- # @option options [Symbol] :class_name the name of the target class of the association; that is, the name of the belongs_to class
124
+ # @option options [String] :class_name the name of the target class of the association; that is, the name of the belongs_to class
47
125
  # @option options [Symbol] :inverse_of the name of the association on the target class; that is, if the class has a belongs_to association, the name of that association
48
126
  #
49
127
  # @since 0.2.0
@@ -51,25 +129,110 @@ module Dynamoid
51
129
  association(:has_one, name, options)
52
130
  end
53
131
 
54
- # create a belongs_to association for this document.
132
+ # Declare a +belongs_to+ association for this document.
133
+ #
134
+ # class Post
135
+ # include Dynamoid::Document
55
136
  #
56
- # @param [Symbol] name the name of the association
57
- # @param [Hash] options options to pass to the association constructor
137
+ # belongs_to :categories
138
+ # end
139
+ #
140
+ # Association supports following operations:
141
+ #
142
+ # * +create+
143
+ # * +create!+
144
+ # * +delete+
145
+ #
146
+ # When a name of an associated class doesn't match an association name a
147
+ # class name should be specified explicitly either with +class+ or
148
+ # +class_name+ option:
149
+ #
150
+ # belongs_to :item, class: Post
151
+ # belongs_to :item, class_name: 'Post'
152
+ #
153
+ # When associated class has own +has_many+ or +has_one+ association to
154
+ # the current class and the name doesn't match a name of the current
155
+ # class this name can be specified with +inverse_of+ option:
156
+ #
157
+ # class Category
158
+ # include Dynamoid::Document
159
+ #
160
+ # has_many :items, class_name: 'Post'
161
+ # end
162
+ #
163
+ # class Post
164
+ # include Dynamoid::Document
165
+ #
166
+ # belongs_to :categories, inverse_of: :items
167
+ # end
168
+ #
169
+ # By default a hash key attribute name is +id+. If an associated class
170
+ # uses another name for a hash key attribute it should be specified in
171
+ # the +belongs_to+ association:
172
+ #
173
+ # belongs_to :categories, foreign_key: :uuid
174
+ #
175
+ # @param name [Symbol] the name of the association
176
+ # @param options [Hash] options to pass to the association constructor
58
177
  # @option options [Class] :class the target class of the has_one association; that is, the has_many or has_one class
59
- # @option options [Symbol] :class_name the name of the target class of the association; that is, the name of the has_many or has_one class
178
+ # @option options [String] :class_name the name of the target class of the association; that is, the name of the has_many or has_one class
60
179
  # @option options [Symbol] :inverse_of the name of the association on the target class; that is, if the class has a has_many or has_one association, the name of that association
180
+ # @option options [Symbol] :foreign_key the name of a hash key attribute in the target class
61
181
  #
62
182
  # @since 0.2.0
63
183
  def belongs_to(name, options = {})
64
184
  association(:belongs_to, name, options)
65
185
  end
66
186
 
67
- # create a has_and_belongs_to_many association for this document.
187
+ # Declare a +has_and_belongs_to_many+ association for this document.
188
+ #
189
+ # class Post
190
+ # include Dynamoid::Document
191
+ #
192
+ # has_and_belongs_to_many :tags
193
+ # end
194
+ #
195
+ # Association is an enumerable collection and supports following addition
196
+ # operations:
197
+ #
198
+ # * +create+
199
+ # * +create!+
200
+ # * +destroy_all+
201
+ # * +delete_all+
202
+ # * +delete+
203
+ # * +<<+
204
+ # * +where+
205
+ # * +all+
206
+ # * +empty?+
207
+ # * +size+
208
+ #
209
+ # When a name of an associated class doesn't match an association name a
210
+ # class name should be specified explicitly either with +class+ or
211
+ # +class_name+ option:
212
+ #
213
+ # has_and_belongs_to_many :labels, class: Tag
214
+ # has_and_belongs_to_many :labels, class_name: 'Tag'
215
+ #
216
+ # When associated class has own +has_and_belongs_to_many+ association to
217
+ # the current class and the name doesn't match a name of the current
218
+ # class this name can be specified with +inverse_of+ option:
219
+ #
220
+ # class Tag
221
+ # include Dynamoid::Document
222
+ #
223
+ # has_and_belongs_to_many :items, class_name: 'Post'
224
+ # end
225
+ #
226
+ # class Post
227
+ # include Dynamoid::Document
228
+ #
229
+ # has_and_belongs_to_many :tags, inverse_of: :items
230
+ # end
68
231
  #
69
- # @param [Symbol] name the name of the association
70
- # @param [Hash] options options to pass to the association constructor
232
+ # @param name [Symbol] the name of the association
233
+ # @param options [Hash] options to pass to the association constructor
71
234
  # @option options [Class] :class the target class of the has_and_belongs_to_many association; that is, the belongs_to class
72
- # @option options [Symbol] :class_name the name of the target class of the association; that is, the name of the belongs_to class
235
+ # @option options [String] :class_name the name of the target class of the association; that is, the name of the belongs_to class
73
236
  # @option options [Symbol] :inverse_of the name of the association on the target class; that is, if the class has a belongs_to association, the name of that association
74
237
  #
75
238
  # @since 0.2.0
@@ -81,9 +244,9 @@ module Dynamoid
81
244
 
82
245
  # create getters and setters for an association.
83
246
  #
84
- # @param [Symbol] symbol the type (:has_one, :has_many, :has_and_belongs_to_many, :belongs_to) of the association
85
- # @param [Symbol] name the name of the association
86
- # @param [Hash] options options to pass to the association constructor; see above for all valid options
247
+ # @param type [Symbol] the type (:has_one, :has_many, :has_and_belongs_to_many, :belongs_to) of the association
248
+ # @param name [Symbol] the name of the association
249
+ # @param options [Hash] options to pass to the association constructor; see above for all valid options
87
250
  #
88
251
  # @since 0.2.0
89
252
  def association(type, name, options = {})
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Dynamoid #:nodoc:
3
+ module Dynamoid
4
4
  # The base association module which all associations include. Every association has two very important components: the source and
5
5
  # the target. The source is the object which is calling the association information. It always has the target_ids inside of an attribute on itself.
6
6
  # The target is the object which is referencing by this association.
7
+ # @private
7
8
  module Associations
9
+ # @private
8
10
  module Association
9
11
  attr_accessor :name, :options, :source, :loaded
10
12
 
@@ -56,6 +58,12 @@ module Dynamoid #:nodoc:
56
58
  :set
57
59
  end
58
60
 
61
+ def disassociate_source
62
+ Array(target).each do |target_entry|
63
+ target_entry.send(target_association).disassociate(source.hash_key) if target_entry && target_association
64
+ end
65
+ end
66
+
59
67
  private
60
68
 
61
69
  # The target class name, either inferred through the association's name or specified in options.
@@ -116,7 +124,7 @@ module Dynamoid #:nodoc:
116
124
 
117
125
  # Create a new instance of the target class without trying to add it to the association. This creates a base, that caller can update before setting or adding it.
118
126
  #
119
- # @param [Hash] attribute hash for the new object
127
+ # @param attributes [Hash] attribute values for the new object
120
128
  #
121
129
  # @return [Dynamoid::Document] the newly-created object
122
130
  #
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Dynamoid #:nodoc:
3
+ module Dynamoid
4
4
  # The belongs_to association. For belongs_to, we reference only a single target instead of multiple records; that target is the
5
5
  # object to which the association object is associated.
6
6
  module Associations
7
+ # @private
7
8
  class BelongsTo
8
9
  include SingleAssociation
9
10
 
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Dynamoid #:nodoc:
3
+ module Dynamoid
4
4
  # The has and belongs to many association.
5
5
  module Associations
6
+ # @private
6
7
  class HasAndBelongsToMany
7
8
  include ManyAssociation
8
9
 
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Dynamoid #:nodoc:
3
+ module Dynamoid
4
4
  # The has_many association.
5
5
  module Associations
6
+ # @private
6
7
  class HasMany
7
8
  include ManyAssociation
8
9
 
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Dynamoid #:nodoc:
3
+ module Dynamoid
4
4
  # The HasOne association.
5
5
  module Associations
6
+ # @private
6
7
  class HasOne
7
8
  include Association
8
9
  include SingleAssociation
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Dynamoid #:nodoc:
3
+ module Dynamoid
4
4
  module Associations
5
5
  module ManyAssociation
6
6
  include Association
@@ -13,6 +13,8 @@ module Dynamoid #:nodoc:
13
13
  end
14
14
 
15
15
  include Enumerable
16
+
17
+ # @private
16
18
  # Delegate methods to the records the association represents.
17
19
  delegate :first, :last, :empty?, :size, :class, to: :records
18
20
 
@@ -20,11 +22,15 @@ module Dynamoid #:nodoc:
20
22
  #
21
23
  # @return the association records; depending on which association this is, either a single instance or an array
22
24
  #
25
+ # @private
23
26
  # @since 0.2.0
24
27
  def find_target
25
- Array(target_class.find(source_ids.to_a))
28
+ return [] if source_ids.empty?
29
+
30
+ Array(target_class.find(source_ids.to_a, raise_error: false))
26
31
  end
27
32
 
33
+ # @private
28
34
  def records
29
35
  if query.empty?
30
36
  target
@@ -43,13 +49,20 @@ module Dynamoid #:nodoc:
43
49
  records.include?(object)
44
50
  end
45
51
 
46
- # Deletes an object or array of objects from the association. This removes their records from the association field on the source,
47
- # and attempts to remove the source from the target association if it is detected to exist.
52
+ # Delete an object or array of objects from the association.
48
53
  #
49
- # @param [Dynamoid::Document] object the object (or array of objects) to remove from the association
54
+ # tag.posts.delete(post)
55
+ # tag.posts.delete([post1, post2, post3])
50
56
  #
51
- # @return [Dynamoid::Document] the deleted object
57
+ # This removes their records from the association field on the source,
58
+ # and attempts to remove the source from the target association if it is
59
+ # detected to exist.
52
60
  #
61
+ # It saves both models immediately - the source model and the target one
62
+ # so any not saved changes will be saved as well.
63
+ #
64
+ # @param object [Dynamoid::Document|Array] model (or array of models) to remove from the association
65
+ # @return [Dynamoid::Document|Array] the deleted model
53
66
  # @since 0.2.0
54
67
  def delete(object)
55
68
  disassociate(Array(object).collect(&:hash_key))
@@ -59,13 +72,19 @@ module Dynamoid #:nodoc:
59
72
  object
60
73
  end
61
74
 
62
- # Add an object or array of objects to an association. This preserves the current records in the association (if any)
63
- # and adds the object to the target association if it is detected to exist.
75
+ # Add an object or array of objects to an association.
64
76
  #
65
- # @param [Dynamoid::Document] object the object (or array of objects) to add to the association
77
+ # tag.posts << post
78
+ # tag.posts << [post1, post2, post3]
66
79
  #
67
- # @return [Dynamoid::Document] the added object
80
+ # This preserves the current records in the association (if any) and adds
81
+ # the object to the target association if it is detected to exist.
68
82
  #
83
+ # It saves both models immediately - the source model and the target one
84
+ # so any not saved changes will be saved as well.
85
+ #
86
+ # @param object [Dynamoid::Document|Array] model (or array of models) to add to the association
87
+ # @return [Dynamoid::Document] the added model
69
88
  # @since 0.2.0
70
89
  def <<(object)
71
90
  associate(Array(object).collect(&:hash_key))
@@ -82,8 +101,9 @@ module Dynamoid #:nodoc:
82
101
  #
83
102
  # @param [Dynamoid::Document] object the object (or array of objects) to add to the association
84
103
  #
85
- # @return [Dynamoid::Document] the added object
104
+ # @return [Dynamoid::Document|Array] the added object
86
105
  #
106
+ # @private
87
107
  # @since 0.2.0
88
108
  def setter(object)
89
109
  target.each { |o| delete(o) }
@@ -91,23 +111,37 @@ module Dynamoid #:nodoc:
91
111
  object
92
112
  end
93
113
 
94
- # Create a new instance of the target class and add it directly to the association. If the create fails an exception will be raised.
114
+ # Create a new instance of the target class, persist it and add directly
115
+ # to the association.
95
116
  #
96
- # @param [Hash] attribute hash for the new object
117
+ # tag.posts.create!(title: 'foo')
97
118
  #
98
- # @return [Dynamoid::Document] the newly-created object
119
+ # Several models can be created at once when an array of attributes
120
+ # specified:
121
+ #
122
+ # tag.posts.create!([{ title: 'foo' }, {title: 'bar'} ])
99
123
  #
124
+ # If the creation fails an exception will be raised.
125
+ #
126
+ # @param attributes [Hash] attribute values for the new object
127
+ # @return [Dynamoid::Document|Array] the newly-created object
100
128
  # @since 0.2.0
101
129
  def create!(attributes = {})
102
130
  self << target_class.create!(attributes)
103
131
  end
104
132
 
105
- # Create a new instance of the target class and add it directly to the association.
133
+ # Create a new instance of the target class, persist it and add directly
134
+ # to the association.
106
135
  #
107
- # @param [Hash] attribute hash for the new object
136
+ # tag.posts.create(title: 'foo')
108
137
  #
109
- # @return [Dynamoid::Document] the newly-created object
138
+ # Several models can be created at once when an array of attributes
139
+ # specified:
110
140
  #
141
+ # tag.posts.create([{ title: 'foo' }, {title: 'bar'} ])
142
+ #
143
+ # @param attributes [Hash] attribute values for the new object
144
+ # @return [Dynamoid::Document|Array] the newly-created object
111
145
  # @since 0.2.0
112
146
  def create(attributes = {})
113
147
  self << target_class.create(attributes)
@@ -115,16 +149,18 @@ module Dynamoid #:nodoc:
115
149
 
116
150
  # Create a new instance of the target class and add it directly to the association. If the create fails an exception will be raised.
117
151
  #
118
- # @param [Hash] attribute hash for the new object
119
- #
120
152
  # @return [Dynamoid::Document] the newly-created object
121
153
  #
154
+ # @private
122
155
  # @since 0.2.0
123
156
  def each(&block)
124
157
  records.each(&block)
125
158
  end
126
159
 
127
- # Destroys all members of the association and removes them from the association.
160
+ # Destroys all members of the association and removes them from the
161
+ # association.
162
+ #
163
+ # tag.posts.destroy_all
128
164
  #
129
165
  # @since 0.2.0
130
166
  def destroy_all
@@ -133,7 +169,10 @@ module Dynamoid #:nodoc:
133
169
  objs.each(&:destroy)
134
170
  end
135
171
 
136
- # Deletes all members of the association and removes them from the association.
172
+ # Deletes all members of the association and removes them from the
173
+ # association.
174
+ #
175
+ # tag.posts.delete_all
137
176
  #
138
177
  # @since 0.2.0
139
178
  def delete_all
@@ -144,10 +183,13 @@ module Dynamoid #:nodoc:
144
183
 
145
184
  # Naive association filtering.
146
185
  #
147
- # @param [Hash] A hash of attributes; each must match every returned object's attribute exactly.
186
+ # tag.posts.where(title: 'foo')
148
187
  #
149
- # @return [Dynamoid::Association] the association this method was called on (for chaining purposes)
188
+ # It loads lazily all the associated models and checks provided
189
+ # conditions. That's why only equality conditions can be specified.
150
190
  #
191
+ # @param args [Hash] A hash of attributes; each must match every returned object's attribute exactly.
192
+ # @return [Dynamoid::Association] the association this method was called on (for chaining purposes)
151
193
  # @since 0.2.0
152
194
  def where(args)
153
195
  filtered = clone
@@ -167,6 +209,7 @@ module Dynamoid #:nodoc:
167
209
 
168
210
  # Delegate methods we don't find directly to the records array.
169
211
  #
212
+ # @private
170
213
  # @since 0.2.0
171
214
  def method_missing(method, *args)
172
215
  if records.respond_to?(method)
@@ -176,10 +219,12 @@ module Dynamoid #:nodoc:
176
219
  end
177
220
  end
178
221
 
222
+ # @private
179
223
  def associate(hash_key)
180
224
  source.update_attribute(source_attribute, source_ids.merge(Array(hash_key)))
181
225
  end
182
226
 
227
+ # @private
183
228
  def disassociate(hash_key)
184
229
  source.update_attribute(source_attribute, source_ids - Array(hash_key))
185
230
  end