dynamoid 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +18 -0
  3. data/.travis.yml +5 -3
  4. data/CHANGELOG.md +15 -0
  5. data/README.md +113 -63
  6. data/Vagrantfile +2 -2
  7. data/docker-compose.yml +1 -1
  8. data/gemfiles/rails_4_2.gemfile +1 -1
  9. data/gemfiles/rails_5_0.gemfile +1 -1
  10. data/gemfiles/rails_5_1.gemfile +1 -1
  11. data/gemfiles/rails_5_2.gemfile +1 -1
  12. data/lib/dynamoid/adapter.rb +1 -0
  13. data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +26 -395
  14. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +234 -0
  15. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +89 -0
  16. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/backoff.rb +24 -0
  17. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb +57 -0
  18. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/start_key.rb +28 -0
  19. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +123 -0
  20. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +85 -0
  21. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/table.rb +52 -0
  22. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/until_past_table_status.rb +60 -0
  23. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +1 -0
  24. data/lib/dynamoid/associations/has_many.rb +1 -0
  25. data/lib/dynamoid/associations/has_one.rb +1 -0
  26. data/lib/dynamoid/associations/single_association.rb +1 -0
  27. data/lib/dynamoid/criteria.rb +4 -4
  28. data/lib/dynamoid/criteria/chain.rb +86 -79
  29. data/lib/dynamoid/criteria/ignored_conditions_detector.rb +41 -0
  30. data/lib/dynamoid/criteria/key_fields_detector.rb +61 -0
  31. data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +41 -0
  32. data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +40 -0
  33. data/lib/dynamoid/document.rb +18 -13
  34. data/lib/dynamoid/dumping.rb +52 -40
  35. data/lib/dynamoid/fields.rb +4 -3
  36. data/lib/dynamoid/finders.rb +3 -3
  37. data/lib/dynamoid/persistence.rb +5 -6
  38. data/lib/dynamoid/primary_key_type_mapping.rb +1 -1
  39. data/lib/dynamoid/tasks.rb +1 -0
  40. data/lib/dynamoid/tasks/database.rake +2 -2
  41. data/lib/dynamoid/type_casting.rb +37 -19
  42. data/lib/dynamoid/undumping.rb +53 -42
  43. data/lib/dynamoid/validations.rb +2 -0
  44. data/lib/dynamoid/version.rb +1 -1
  45. metadata +17 -5
  46. data/lib/dynamoid/adapter_plugin/query.rb +0 -144
  47. data/lib/dynamoid/adapter_plugin/scan.rb +0 -107
@@ -183,7 +183,7 @@ module Dynamoid
183
183
  def find_all_by_composite_key(hash_key, options = {})
184
184
  ActiveSupport::Deprecation.warn('[Dynamoid] .find_all_composite_key is deprecated! Call .where instead of')
185
185
 
186
- Dynamoid.adapter.query(table_name, options.merge(hash_value: hash_key)).collect do |item|
186
+ Dynamoid.adapter.query(table_name, options.merge(hash_value: hash_key)).flat_map{ |i| i }.collect do |item|
187
187
  from_database(item)
188
188
  end
189
189
  end
@@ -240,7 +240,7 @@ module Dynamoid
240
240
  opts[range_op_mapped] = range_key_value
241
241
  end
242
242
  dynamo_options = opts.merge(options.reject { |key, _| key == :range })
243
- Dynamoid.adapter.query(table_name, dynamo_options).map do |item|
243
+ Dynamoid.adapter.query(table_name, dynamo_options).flat_map{ |i| i }.map do |item|
244
244
  from_database(item)
245
245
  end
246
246
  end
@@ -264,7 +264,7 @@ module Dynamoid
264
264
  attributes = method.to_s.split('_by_').last.split('_and_')
265
265
 
266
266
  chain = Dynamoid::Criteria::Chain.new(self)
267
- chain.query = {}.tap { |h| attributes.each_with_index { |attr, index| h[attr.to_sym] = args[index] } }
267
+ chain = chain.where({}.tap { |h| attributes.each_with_index { |attr, index| h[attr.to_sym] = args[index] } })
268
268
 
269
269
  if finder =~ /all/
270
270
  return chain.all
@@ -18,8 +18,7 @@ module Dynamoid
18
18
 
19
19
  module ClassMethods
20
20
  def table_name
21
- table_base_name = options[:name] || base_class.name.split('::').last
22
- .downcase.pluralize
21
+ table_base_name = options[:name] || base_class.name.split('::').last.downcase.pluralize
23
22
 
24
23
  @table_name ||= [Dynamoid::Config.namespace.to_s, table_base_name].reject(&:empty?).join('_')
25
24
  end
@@ -151,22 +150,22 @@ module Dynamoid
151
150
  end
152
151
  end
153
152
 
154
- # Updates multiple attibutes at once, saving the object once the updates are complete.
153
+ # Updates multiple attributes at once, saving the object once the updates are complete.
155
154
  #
156
155
  # @param [Hash] attributes a hash of attributes to update
157
156
  #
158
157
  # @since 0.2.0
159
158
  def update_attributes(attributes)
160
- attributes.each { |attribute, value| write_attribute(attribute, value) } unless attributes.nil? || attributes.empty?
159
+ attributes.each { |attribute, value| write_attribute(attribute, value) }
161
160
  save
162
161
  end
163
162
 
164
- # Updates multiple attibutes at once, saving the object once the updates are complete.
163
+ # Updates multiple attributes at once, saving the object once the updates are complete.
165
164
  # Raises a Dynamoid::Errors::DocumentNotValid exception if there is vaidation and it fails.
166
165
  #
167
166
  # @param [Hash] attributes a hash of attributes to update
168
167
  def update_attributes!(attributes)
169
- attributes.each { |attribute, value| write_attribute(attribute, value) } unless attributes.nil? || attributes.empty?
168
+ attributes.each { |attribute, value| write_attribute(attribute, value) }
170
169
  save!
171
170
  end
172
171
 
@@ -3,7 +3,7 @@
3
3
  module Dynamoid
4
4
  class PrimaryKeyTypeMapping
5
5
  def self.dynamodb_type(type, options)
6
- if Class === type
6
+ if type.is_a?(Class)
7
7
  type = type.respond_to?(:dynamoid_field_type) ? type.dynamoid_field_type : :string
8
8
  end
9
9
 
@@ -0,0 +1 @@
1
+ load "dynamoid/tasks/database.rake"
@@ -30,12 +30,12 @@ namespace :dynamoid do
30
30
  end
31
31
 
32
32
  msg = "Connection to DynamoDB #{success ? 'OK' : 'FAILED'}"
33
- msg << if Dynamoid.config.endpoint
33
+ msg += if Dynamoid.config.endpoint
34
34
  " at local endpoint '#{Dynamoid.config.endpoint}'"
35
35
  else
36
36
  ' at remote AWS endpoint'
37
37
  end
38
- msg << ", reason being '#{failure_reason}'" unless success
38
+ msg += ", reason being '#{failure_reason}'" unless success
39
39
  puts msg
40
40
  end
41
41
  end
@@ -29,6 +29,7 @@ module Dynamoid
29
29
  when :number then NumberTypeCaster
30
30
  when :set then SetTypeCaster
31
31
  when :array then ArrayTypeCaster
32
+ when :map then MapTypeCaster
32
33
  when :datetime then DateTimeTypeCaster
33
34
  when :date then DateTypeCaster
34
35
  when :raw then RawTypeCaster
@@ -96,7 +97,7 @@ module Dynamoid
96
97
  nil
97
98
  elsif value.is_a?(Float) && !value.finite?
98
99
  nil
99
- elsif !(value.respond_to?(:to_d))
100
+ elsif !value.respond_to?(:to_d)
100
101
  nil
101
102
  else
102
103
  value.to_d
@@ -122,8 +123,6 @@ module Dynamoid
122
123
  value.dup
123
124
  elsif value.respond_to?(:to_set)
124
125
  value.to_set
125
- else
126
- nil
127
126
  end
128
127
  end
129
128
 
@@ -138,20 +137,20 @@ module Dynamoid
138
137
  end
139
138
 
140
139
  def element_type
141
- unless @options[:of].is_a?(Hash)
142
- @options[:of]
143
- else
140
+ if @options[:of].is_a?(Hash)
144
141
  @options[:of].keys.first
142
+ else
143
+ @options[:of]
145
144
  end
146
145
  end
147
146
 
148
147
  def element_options
149
- unless @options[:of].is_a?(Hash)
150
- { type: element_type }
151
- else
148
+ if @options[:of].is_a?(Hash)
152
149
  @options[:of][element_type].dup.tap do |options|
153
150
  options[:type] = element_type
154
151
  end
152
+ else
153
+ { type: element_type }
155
154
  end
156
155
  end
157
156
  end
@@ -174,8 +173,6 @@ module Dynamoid
174
173
  value.dup
175
174
  elsif value.respond_to?(:to_a)
176
175
  value.to_a
177
- else
178
- nil
179
176
  end
180
177
  end
181
178
 
@@ -190,20 +187,36 @@ module Dynamoid
190
187
  end
191
188
 
192
189
  def element_type
193
- unless @options[:of].is_a?(Hash)
194
- @options[:of]
195
- else
190
+ if @options[:of].is_a?(Hash)
196
191
  @options[:of].keys.first
192
+ else
193
+ @options[:of]
197
194
  end
198
195
  end
199
196
 
200
197
  def element_options
201
- unless @options[:of].is_a?(Hash)
202
- { type: element_type }
203
- else
198
+ if @options[:of].is_a?(Hash)
204
199
  @options[:of][element_type].dup.tap do |options|
205
200
  options[:type] = element_type
206
201
  end
202
+ else
203
+ { type: element_type }
204
+ end
205
+ end
206
+ end
207
+
208
+ class MapTypeCaster < Base
209
+ def process(value)
210
+ return nil if value.nil?
211
+
212
+ if value.is_a? Hash
213
+ value
214
+ elsif value.respond_to? :to_hash
215
+ value.to_hash
216
+ elsif value.respond_to? :to_h
217
+ value.to_h
218
+ else
219
+ nil
207
220
  end
208
221
  end
209
222
  end
@@ -213,7 +226,11 @@ module Dynamoid
213
226
  if !value.respond_to?(:to_datetime)
214
227
  nil
215
228
  elsif value.is_a?(String)
216
- dt = DateTime.parse(value) rescue nil
229
+ dt = begin
230
+ DateTime.parse(value)
231
+ rescue StandardError
232
+ nil
233
+ end
217
234
  if dt
218
235
  seconds = string_utc_offset(value) || ApplicationTimeZone.utc_offset
219
236
  offset = seconds_to_offset(seconds)
@@ -243,7 +260,8 @@ module Dynamoid
243
260
  else
244
261
  begin
245
262
  value.to_date
246
- rescue ArgumentError
263
+ rescue StandardError
264
+ nil
247
265
  end
248
266
  end
249
267
  end
@@ -8,7 +8,7 @@ module Dynamoid
8
8
  attributes.symbolize_keys
9
9
  .select { |attribute| attributes_options.key?(attribute) }
10
10
  .each do |attribute, value|
11
- h[attribute] = undump_field(value, attributes_options[attribute])
11
+ h[attribute] = undump_field(value, attributes_options[attribute])
12
12
  end
13
13
  end
14
14
  end
@@ -32,6 +32,7 @@ module Dynamoid
32
32
  when :number then NumberUndumper
33
33
  when :set then SetUndumper
34
34
  when :array then ArrayUndumper
35
+ when :map then MapUndumper
35
36
  when :datetime then DateTimeUndumper
36
37
  when :date then DateUndumper
37
38
  when :raw then RawUndumper
@@ -45,6 +46,35 @@ module Dynamoid
45
46
  end
46
47
  end
47
48
 
49
+ module UndumpHashHelper
50
+ extend self
51
+
52
+ def undump_hash(hash)
53
+ {}.tap do |h|
54
+ hash.each { |key, value| h[key.to_sym] = undump_hash_value(value) }
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def undump_hash_value(val)
61
+ case val
62
+ when BigDecimal
63
+ if Dynamoid::Config.convert_big_decimal
64
+ val.to_f
65
+ else
66
+ val
67
+ end
68
+ when Hash
69
+ undump_hash(val)
70
+ when Array
71
+ val.map { |v| undump_hash_value(v) }
72
+ else
73
+ val
74
+ end
75
+ end
76
+ end
77
+
48
78
  class Base
49
79
  def initialize(options)
50
80
  @options = options
@@ -68,7 +98,7 @@ module Dynamoid
68
98
  end
69
99
 
70
100
  class SetUndumper < Base
71
- ALLOWED_TYPES = [:string, :integer, :number, :date, :datetime, :serialized]
101
+ ALLOWED_TYPES = %i[string integer number date datetime serialized].freeze
72
102
 
73
103
  def process(set)
74
104
  if @options.key?(:of)
@@ -94,26 +124,26 @@ module Dynamoid
94
124
  end
95
125
 
96
126
  def element_type
97
- unless @options[:of].is_a?(Hash)
98
- @options[:of]
99
- else
127
+ if @options[:of].is_a?(Hash)
100
128
  @options[:of].keys.first
129
+ else
130
+ @options[:of]
101
131
  end
102
132
  end
103
133
 
104
134
  def element_options
105
- unless @options[:of].is_a?(Hash)
106
- { type: element_type }
107
- else
135
+ if @options[:of].is_a?(Hash)
108
136
  @options[:of][element_type].dup.tap do |options|
109
137
  options[:type] = element_type
110
138
  end
139
+ else
140
+ { type: element_type }
111
141
  end
112
142
  end
113
143
  end
114
144
 
115
145
  class ArrayUndumper < Base
116
- ALLOWED_TYPES = [:string, :integer, :number, :date, :datetime, :serialized]
146
+ ALLOWED_TYPES = %i[string integer number date datetime serialized].freeze
117
147
 
118
148
  def process(array)
119
149
  if @options.key?(:of)
@@ -139,24 +169,30 @@ module Dynamoid
139
169
  end
140
170
 
141
171
  def element_type
142
- unless @options[:of].is_a?(Hash)
143
- @options[:of]
144
- else
172
+ if @options[:of].is_a?(Hash)
145
173
  @options[:of].keys.first
174
+ else
175
+ @options[:of]
146
176
  end
147
177
  end
148
178
 
149
179
  def element_options
150
- unless @options[:of].is_a?(Hash)
151
- { type: element_type }
152
- else
180
+ if @options[:of].is_a?(Hash)
153
181
  @options[:of][element_type].dup.tap do |options|
154
182
  options[:type] = element_type
155
183
  end
184
+ else
185
+ { type: element_type }
156
186
  end
157
187
  end
158
188
  end
159
189
 
190
+ class MapUndumper < Base
191
+ def process(value)
192
+ UndumpHashHelper.undump_hash(value)
193
+ end
194
+ end
195
+
160
196
  class DateTimeUndumper < Base
161
197
  def process(value)
162
198
  return value if value.is_a?(Date) || value.is_a?(DateTime) || value.is_a?(Time)
@@ -190,36 +226,11 @@ module Dynamoid
190
226
  class RawUndumper < Base
191
227
  def process(value)
192
228
  if value.is_a?(Hash)
193
- undump_hash(value)
229
+ UndumpHashHelper.undump_hash(value)
194
230
  else
195
231
  value
196
232
  end
197
233
  end
198
-
199
- private
200
-
201
- def undump_hash(hash)
202
- {}.tap do |h|
203
- hash.each { |key, value| h[key.to_sym] = undump_hash_value(value) }
204
- end
205
- end
206
-
207
- def undump_hash_value(val)
208
- case val
209
- when BigDecimal
210
- if Dynamoid::Config.convert_big_decimal
211
- val.to_f
212
- else
213
- val
214
- end
215
- when Hash
216
- undump_hash(val)
217
- when Array
218
- val.map { |v| undump_hash_value(v) }
219
- else
220
- val
221
- end
222
- end
223
234
  end
224
235
 
225
236
  class SerializedUndumper < Base
@@ -233,7 +244,7 @@ module Dynamoid
233
244
  end
234
245
 
235
246
  class BooleanUndumper < Base
236
- STRING_VALUES = ['t', 'f']
247
+ STRING_VALUES = %w[t f].freeze
237
248
 
238
249
  def process(value)
239
250
  store_as_boolean = if @options[:store_as_native_boolean].nil?
@@ -14,6 +14,7 @@ module Dynamoid
14
14
  def save(options = {})
15
15
  options.reverse_merge!(validate: true)
16
16
  return false if options[:validate] && !valid?
17
+
17
18
  super
18
19
  end
19
20
 
@@ -30,6 +31,7 @@ module Dynamoid
30
31
  # @since 0.2.0
31
32
  def save!
32
33
  raise Dynamoid::Errors::DocumentNotValid, self unless valid?
34
+
33
35
  save(validate: false)
34
36
  self
35
37
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
- VERSION = '3.1.0'
4
+ VERSION = '3.2.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Symonds
@@ -21,7 +21,7 @@ authors:
21
21
  autorequire:
22
22
  bindir: exe
23
23
  cert_chain: []
24
- date: 2018-11-19 00:00:00.000000000 Z
24
+ date: 2019-05-15 00:00:00.000000000 Z
25
25
  dependencies:
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: activemodel
@@ -240,8 +240,15 @@ files:
240
240
  - lib/dynamoid.rb
241
241
  - lib/dynamoid/adapter.rb
242
242
  - lib/dynamoid/adapter_plugin/aws_sdk_v3.rb
243
- - lib/dynamoid/adapter_plugin/query.rb
244
- - lib/dynamoid/adapter_plugin/scan.rb
243
+ - lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb
244
+ - lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb
245
+ - lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/backoff.rb
246
+ - lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb
247
+ - lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/start_key.rb
248
+ - lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb
249
+ - lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb
250
+ - lib/dynamoid/adapter_plugin/aws_sdk_v3/table.rb
251
+ - lib/dynamoid/adapter_plugin/aws_sdk_v3/until_past_table_status.rb
245
252
  - lib/dynamoid/application_time_zone.rb
246
253
  - lib/dynamoid/associations.rb
247
254
  - lib/dynamoid/associations/association.rb
@@ -258,6 +265,10 @@ files:
258
265
  - lib/dynamoid/config/options.rb
259
266
  - lib/dynamoid/criteria.rb
260
267
  - lib/dynamoid/criteria/chain.rb
268
+ - lib/dynamoid/criteria/ignored_conditions_detector.rb
269
+ - lib/dynamoid/criteria/key_fields_detector.rb
270
+ - lib/dynamoid/criteria/nonexistent_fields_detector.rb
271
+ - lib/dynamoid/criteria/overwritten_conditions_detector.rb
261
272
  - lib/dynamoid/dirty.rb
262
273
  - lib/dynamoid/document.rb
263
274
  - lib/dynamoid/dumping.rb
@@ -271,6 +282,7 @@ files:
271
282
  - lib/dynamoid/persistence.rb
272
283
  - lib/dynamoid/primary_key_type_mapping.rb
273
284
  - lib/dynamoid/railtie.rb
285
+ - lib/dynamoid/tasks.rb
274
286
  - lib/dynamoid/tasks/database.rake
275
287
  - lib/dynamoid/tasks/database.rb
276
288
  - lib/dynamoid/type_casting.rb
@@ -297,7 +309,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
297
309
  version: '0'
298
310
  requirements: []
299
311
  rubyforge_project:
300
- rubygems_version: 2.6.14
312
+ rubygems_version: 2.7.6
301
313
  signing_key:
302
314
  specification_version: 4
303
315
  summary: Dynamoid is an ORM for Amazon's DynamoDB