dynamoid 3.4.1 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/lib/dynamoid/adapter.rb +2 -2
  4. data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +23 -23
  5. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/batch_get_item.rb +2 -0
  6. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +1 -1
  7. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/backoff.rb +1 -2
  8. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb +1 -3
  9. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/start_key.rb +1 -2
  10. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +3 -2
  11. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +3 -2
  12. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/until_past_table_status.rb +1 -1
  13. data/lib/dynamoid/config.rb +2 -3
  14. data/lib/dynamoid/criteria.rb +1 -1
  15. data/lib/dynamoid/criteria/chain.rb +37 -12
  16. data/lib/dynamoid/criteria/ignored_conditions_detector.rb +2 -3
  17. data/lib/dynamoid/criteria/key_fields_detector.rb +7 -8
  18. data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +2 -2
  19. data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +0 -1
  20. data/lib/dynamoid/dirty.rb +50 -50
  21. data/lib/dynamoid/document.rb +2 -1
  22. data/lib/dynamoid/fields.rb +14 -1
  23. data/lib/dynamoid/finders.rb +4 -4
  24. data/lib/dynamoid/indexes.rb +2 -2
  25. data/lib/dynamoid/persistence.rb +32 -6
  26. data/lib/dynamoid/persistence/upsert.rb +0 -1
  27. data/lib/dynamoid/tasks.rb +3 -1
  28. data/lib/dynamoid/type_casting.rb +0 -2
  29. data/lib/dynamoid/version.rb +1 -1
  30. metadata +40 -56
  31. data/.coveralls.yml +0 -1
  32. data/.document +0 -5
  33. data/.gitignore +0 -74
  34. data/.rspec +0 -2
  35. data/.rubocop.yml +0 -71
  36. data/.rubocop_todo.yml +0 -55
  37. data/.travis.yml +0 -44
  38. data/Appraisals +0 -22
  39. data/Gemfile +0 -8
  40. data/Rakefile +0 -46
  41. data/Vagrantfile +0 -29
  42. data/docker-compose.yml +0 -7
  43. data/dynamoid.gemspec +0 -57
  44. data/gemfiles/rails_4_2.gemfile +0 -9
  45. data/gemfiles/rails_5_0.gemfile +0 -8
  46. data/gemfiles/rails_5_1.gemfile +0 -8
  47. data/gemfiles/rails_5_2.gemfile +0 -8
  48. data/gemfiles/rails_6_0.gemfile +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 25fefa51d69224bd2536fe3afa6f3ee672ff4ad09d22fda85456b1d83a63b040
4
- data.tar.gz: cb2d6c4bc4d50ace05ffbdd9644081d2141e9e64653a8c421c222aa0eef36de5
3
+ metadata.gz: 16a6980d441040611894ad405ce797b6ef655e9e2730b010d72ff3e65771129f
4
+ data.tar.gz: 1ca556953abc411f0b7e2fd5e5c74518a7da4381b86449b2f49e81d9f76ee0a8
5
5
  SHA512:
6
- metadata.gz: 769b5fb40049770a8b8aec38312e791f4d7702b308e3b15b2754f1a838f5d953b663081855df70fe3c3e80b202f3285a51b2e8738441ab9aa0a784d38385f447
7
- data.tar.gz: 4231f149557483770ed04d81efef46d55f6a1f56475f9426d8dad1a5f26b1e5661ed1f9e0f43f502d073bb2370dbb5d0073668c6757b23230da90e9a5a623e3b
6
+ metadata.gz: 71856592a8a381b27517682c6ce7bb48047de11dc2fbb17c175af629d3ec7e2b63895e9b24c943b57a93e47db0b1de390454b5a89d71899a59c9208a79e0595e
7
+ data.tar.gz: 8cd759df57475c7566ef0ceae781d21eb443cb1dcd0f6da0f3eaa778fe6a3abeac8865a1198a2dbd2f435eec2cb5317b47ce5e5bd5b9dadbe7537a412053492b
@@ -11,6 +11,30 @@
11
11
  ---
12
12
 
13
13
 
14
+
15
+ # 3.5 / 2020-04-04
16
+
17
+
18
+ ## Features
19
+ * Feature: [#405](https://github.com/Dynamoid/dynamoid/pull/405) Added `update!` class method (@UrsaDK)
20
+ * Feature: [#408](https://github.com/Dynamoid/dynamoid/pull/408) Added `ActiveSupport` load hook on `Dynamoid` load (@aaronmallen)
21
+ * Feature: [#422](https://github.com/Dynamoid/dynamoid/pull/422) Added `.pluck` method
22
+
23
+ Fixes:
24
+ * Fix: [#410](https://github.com/Dynamoid/dynamoid/pull/410) Fixed creating GSI when table uses on-demand capacity provisioning (@icy-arctic-fox)
25
+ * Fix: [#414](https://github.com/Dynamoid/dynamoid/pull/414) Fixed lazy table creation
26
+ * Fix: [#415](https://github.com/Dynamoid/dynamoid/pull/415) Fixed RubyDoc comment (@walkersumida)
27
+ * Fix: [#420](https://github.com/Dynamoid/dynamoid/pull/420) Fixed `#persisted?` for deleted/destroyed models
28
+
29
+ Improvements:
30
+ * Improvement: [#416](https://github.com/Dynamoid/dynamoid/pull/416) Improved speed of Adapter's `truncate` method. It now uses `#batch_delete_item` method (@TheSmartnik)
31
+ * Improvement: [#421](https://github.com/Dynamoid/dynamoid/pull/421) Added `touch: false` option of the #save method
32
+ * Improvement: [#423](https://github.com/Dynamoid/dynamoid/pull/423) Added warning when generated for a field methods override existing ones
33
+
34
+ ---
35
+
36
+
37
+
14
38
  # 3.4.1
15
39
 
16
40
  ## Fixes
@@ -3,7 +3,7 @@
3
3
  # require only 'concurrent/atom' once this issue is resolved:
4
4
  # https://github.com/ruby-concurrency/concurrent-ruby/pull/377
5
5
  require 'concurrent'
6
- require "dynamoid/adapter_plugin/aws_sdk_v3"
6
+ require 'dynamoid/adapter_plugin/aws_sdk_v3'
7
7
 
8
8
  # encoding: utf-8
9
9
  module Dynamoid
@@ -94,7 +94,7 @@ module Dynamoid
94
94
  #
95
95
  # @param [String] table the name of the table to write the object to
96
96
  # @param [Array] ids to delete, can also be a string of just one id
97
- # @param [Array] range_key of the record to delete, can also be a string of just one range_key
97
+ # @param [Hash] range_key of the record to delete, can also be a string of just one range_key
98
98
  #
99
99
  def delete(table, ids, options = {})
100
100
  range_key = options[:range_key] # array of range keys that matches the ids passed in
@@ -61,6 +61,25 @@ module Dynamoid
61
61
 
62
62
  attr_reader :table_cache
63
63
 
64
+ # Build an array of values for Condition
65
+ # Is used in ScanFilter and QueryFilter
66
+ # https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html
67
+ # @params [String] operator: value of RANGE_MAP or FIELD_MAP hash, e.g. "EQ", "LT" etc
68
+ # @params [Object] value: scalar value or array/set
69
+ def self.attribute_value_list(operator, value)
70
+ # For BETWEEN and IN operators we should keep value as is (it should be already an array)
71
+ # NULL and NOT_NULL require absence of attribute list
72
+ # For all the other operators we wrap the value with array
73
+ # https://docs.aws.amazon.com/en_us/amazondynamodb/latest/developerguide/LegacyConditionalParameters.Conditions.html
74
+ if %w[BETWEEN IN].include?(operator)
75
+ [value].flatten
76
+ elsif %w[NULL NOT_NULL].include?(operator)
77
+ nil
78
+ else
79
+ [value]
80
+ end
81
+ end
82
+
64
83
  # Establish the connection to DynamoDB.
65
84
  #
66
85
  # @return [Aws::DynamoDB::Client] the DynamoDB connection
@@ -525,11 +544,11 @@ module Dynamoid
525
544
  hk = table.hash_key
526
545
  rk = table.range_key
527
546
 
528
- scan(table_name, {}, {}).flat_map{ |i| i }.each do |attributes|
529
- opts = {}
530
- opts[:range_key] = attributes[rk.to_sym] if rk
531
- delete_item(table_name, attributes[hk], opts)
547
+ ids = scan(table_name, {}, {}).flat_map { |i| i }.map do |attributes|
548
+ rk ? [attributes[hk], attributes[rk.to_sym]] : attributes[hk]
532
549
  end
550
+
551
+ batch_delete_item(table_name => ids)
533
552
  end
534
553
 
535
554
  def count(table_name)
@@ -587,25 +606,6 @@ module Dynamoid
587
606
  end
588
607
  end
589
608
 
590
- # Build an array of values for Condition
591
- # Is used in ScanFilter and QueryFilter
592
- # https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html
593
- # @params [String] operator: value of RANGE_MAP or FIELD_MAP hash, e.g. "EQ", "LT" etc
594
- # @params [Object] value: scalar value or array/set
595
- def self.attribute_value_list(operator, value)
596
- # For BETWEEN and IN operators we should keep value as is (it should be already an array)
597
- # NULL and NOT_NULL require absence of attribute list
598
- # For all the other operators we wrap the value with array
599
- # https://docs.aws.amazon.com/en_us/amazondynamodb/latest/developerguide/LegacyConditionalParameters.Conditions.html
600
- if %w[BETWEEN IN].include?(operator)
601
- [value].flatten
602
- elsif %w[NULL NOT_NULL].include?(operator)
603
- nil
604
- else
605
- [value]
606
- end
607
- end
608
-
609
609
  def sanitize_item(attributes)
610
610
  config_value = Dynamoid.config.store_attribute_with_nil_value
611
611
  store_attribute_with_nil_value = config_value.nil? ? false : !!config_value
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dynamoid
2
4
  module AdapterPlugin
3
5
  class AwsSdkV3
@@ -213,7 +213,7 @@ module Dynamoid
213
213
  end
214
214
 
215
215
  # Only global secondary indexes have a separate throughput.
216
- if index.type == :global_secondary
216
+ if index.type == :global_secondary && options[:billing_mode] != :on_demand
217
217
  hash[:provisioned_throughput] = {
218
218
  read_capacity_units: index.read_capacity,
219
219
  write_capacity_units: index.write_capacity
@@ -14,11 +14,10 @@ module Dynamoid
14
14
  response = @next_chain.call(request)
15
15
  @backoff.call if @backoff
16
16
 
17
- return response
17
+ response
18
18
  end
19
19
  end
20
20
  end
21
21
  end
22
22
  end
23
23
  end
24
-
@@ -46,12 +46,10 @@ module Dynamoid
46
46
  @scan_count += response.scanned_count
47
47
  throw :stop_pagination if @scan_limit && @scan_count >= @scan_limit
48
48
 
49
- return response
49
+ response
50
50
  end
51
51
  end
52
-
53
52
  end
54
53
  end
55
54
  end
56
55
  end
57
-
@@ -18,11 +18,10 @@ module Dynamoid
18
18
  throw :stop_pagination
19
19
  end
20
20
 
21
- return response
21
+ response
22
22
  end
23
23
  end
24
24
  end
25
25
  end
26
26
  end
27
27
  end
28
-
@@ -29,8 +29,8 @@ module Dynamoid
29
29
  request = build_request
30
30
 
31
31
  Enumerator.new do |yielder|
32
- api_call = -> (request) do
33
- client.query(request).tap do |response|
32
+ api_call = lambda do |req|
33
+ client.query(req).tap do |response|
34
34
  yielder << response
35
35
  end
36
36
  end
@@ -122,6 +122,7 @@ module Dynamoid
122
122
 
123
123
  def attributes_to_get
124
124
  return if options[:project].nil?
125
+
125
126
  options[:project].map(&:to_s)
126
127
  end
127
128
  end
@@ -21,8 +21,8 @@ module Dynamoid
21
21
  request = build_request
22
22
 
23
23
  Enumerator.new do |yielder|
24
- api_call = -> (request) do
25
- client.scan(request).tap do |response|
24
+ api_call = lambda do |req|
25
+ client.scan(req).tap do |response|
26
26
  yielder << response
27
27
  end
28
28
  end
@@ -85,6 +85,7 @@ module Dynamoid
85
85
 
86
86
  def attributes_to_get
87
87
  return if options[:project].nil?
88
+
88
89
  options[:project].map(&:to_s)
89
90
  end
90
91
  end
@@ -32,7 +32,7 @@ module Dynamoid
32
32
  # See: http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#describe_table-instance_method
33
33
  rescue Aws::DynamoDB::Errors::ResourceNotFoundException => e
34
34
  case status
35
- when :creating then
35
+ when :creating
36
36
  if counter >= Dynamoid::Config.sync_retry_max_times
37
37
  Dynamoid.logger.warn "Waiting on table metadata for #{table_name} (check #{counter})"
38
38
  retry # start over at first line of begin, does not reset counter
@@ -14,9 +14,9 @@ module Dynamoid
14
14
  DEFAULT_NAMESPACE = if defined?(Rails)
15
15
  klass = Rails.application.class
16
16
  app_name = Rails::VERSION::MAJOR >= 6 ? klass.module_parent_name : klass.parent_name
17
- "dynamoid_#{app_name}_#{Rails.env}".freeze
17
+ "dynamoid_#{app_name}_#{Rails.env}"
18
18
  else
19
- 'dynamoid'.freeze
19
+ 'dynamoid'
20
20
  end
21
21
 
22
22
  extend self
@@ -95,6 +95,5 @@ module Dynamoid
95
95
  backoff_strategies[backoff].call
96
96
  end
97
97
  end
98
-
99
98
  end
100
99
  end
@@ -8,7 +8,7 @@ module Dynamoid
8
8
  extend ActiveSupport::Concern
9
9
 
10
10
  module ClassMethods
11
- %i[where all first last each record_limit scan_limit batch start scan_index_forward find_by_pages project].each do |meth|
11
+ %i[where all first last each record_limit scan_limit batch start scan_index_forward find_by_pages project pluck].each do |meth|
12
12
  # Return a criteria chain in response to a method that will begin or end a chain. For more information,
13
13
  # see Dynamoid::Criteria::Chain.
14
14
  #
@@ -102,12 +102,12 @@ module Dynamoid
102
102
  ranges = []
103
103
 
104
104
  if @key_fields_detector.key_present?
105
- Dynamoid.adapter.query(source.table_name, range_query).flat_map{ |i| i }.collect do |hash|
105
+ Dynamoid.adapter.query(source.table_name, range_query).flat_map { |i| i }.collect do |hash|
106
106
  ids << hash[source.hash_key.to_sym]
107
107
  ranges << hash[source.range_key.to_sym] if source.range_key
108
108
  end
109
109
  else
110
- Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).flat_map{ |i| i }.collect do |hash|
110
+ Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).flat_map { |i| i }.collect do |hash|
111
111
  ids << hash[source.hash_key.to_sym]
112
112
  ranges << hash[source.range_key.to_sym] if source.range_key
113
113
  end
@@ -164,6 +164,20 @@ module Dynamoid
164
164
  self
165
165
  end
166
166
 
167
+ def pluck(*args)
168
+ fields = args.map(&:to_sym)
169
+ @project = fields
170
+
171
+ if fields.many?
172
+ items.map do |item|
173
+ fields.map { |key| Undumping.undump_field(item[key], source.attributes[key]) }
174
+ end.to_a
175
+ else
176
+ key = fields.first
177
+ items.map { |item| Undumping.undump_field(item[key], source.attributes[key]) }.to_a
178
+ end
179
+ end
180
+
167
181
  private
168
182
 
169
183
  # The actual records referenced by the association.
@@ -172,7 +186,12 @@ module Dynamoid
172
186
  #
173
187
  # @since 0.2.0
174
188
  def records
175
- pages.lazy.flat_map { |i| i }
189
+ pages.lazy.flat_map { |items, _| items }
190
+ end
191
+
192
+ # Raw items like they are stored before type casting
193
+ def items
194
+ raw_pages.lazy.flat_map { |items, _| items }
176
195
  end
177
196
 
178
197
  # Arrays of records, sized based on the actual pages produced by DynamoDB
@@ -181,11 +200,19 @@ module Dynamoid
181
200
  #
182
201
  # @since 3.1.0
183
202
  def pages
203
+ raw_pages.lazy.map do |items, options|
204
+ models = items.map { |i| source.from_database(i) }
205
+ [models, options]
206
+ end.each
207
+ end
208
+
209
+ # Pages of items before type casting
210
+ def raw_pages
184
211
  if @key_fields_detector.key_present?
185
- pages_via_query
212
+ raw_pages_via_query
186
213
  else
187
214
  issue_scan_warning if Dynamoid::Config.warn_on_scan && query.present?
188
- pages_via_scan
215
+ raw_pages_via_scan
189
216
  end
190
217
  end
191
218
 
@@ -194,13 +221,12 @@ module Dynamoid
194
221
  # @return [Enumerator] an iterator of the found pages. An array of records
195
222
  #
196
223
  # @since 3.1.0
197
- def pages_via_query
224
+ def raw_pages_via_query
198
225
  Enumerator.new do |y|
199
226
  Dynamoid.adapter.query(source.table_name, range_query).each do |items, metadata|
200
- page = items.map { |h| source.from_database(h) }
201
227
  options = metadata.slice(:last_evaluated_key)
202
228
 
203
- y.yield page, options
229
+ y.yield items, options
204
230
  end
205
231
  end
206
232
  end
@@ -210,13 +236,12 @@ module Dynamoid
210
236
  # @return [Enumerator] an iterator of the found pages. An array of records
211
237
  #
212
238
  # @since 3.1.0
213
- def pages_via_scan
239
+ def raw_pages_via_scan
214
240
  Enumerator.new do |y|
215
241
  Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).each do |items, metadata|
216
- page = items.map { |h| source.from_database(h) }
217
242
  options = metadata.slice(:last_evaluated_key)
218
243
 
219
- y.yield page, options
244
+ y.yield items, options
220
245
  end
221
246
  end
222
247
  end
@@ -332,7 +357,7 @@ module Dynamoid
332
357
  opts.merge(query_opts).merge(consistent_opts)
333
358
  end
334
359
 
335
- # TODO casting should be operator aware
360
+ # TODO: casting should be operator aware
336
361
  # e.g. for NULL operator value should be boolean
337
362
  # and isn't related to an attribute own type
338
363
  def type_cast_condition_parameter(key, value)
@@ -24,8 +24,8 @@ module Dynamoid
24
24
  def ignored_keys
25
25
  @conditions.keys
26
26
  .group_by(&method(:key_to_field))
27
- .select { |field, ary| ary.size > 1 }
28
- .flat_map { |field, ary| ary[0 .. -2] }
27
+ .select { |_, ary| ary.size > 1 }
28
+ .flat_map { |_, ary| ary[0..-2] }
29
29
  end
30
30
 
31
31
  def key_to_field(key)
@@ -38,4 +38,3 @@ module Dynamoid
38
38
  end
39
39
  end
40
40
  end
41
-
@@ -3,7 +3,6 @@
3
3
  module Dynamoid #:nodoc:
4
4
  module Criteria
5
5
  class KeyFieldsDetector
6
-
7
6
  class Query
8
7
  def initialize(query_hash)
9
8
  @query_hash = query_hash
@@ -71,8 +70,8 @@ module Dynamoid #:nodoc:
71
70
  def match_local_secondary_index
72
71
  return unless @query.contain_with_eq_operator?(@source.hash_key)
73
72
 
74
- lsi = @source.local_secondary_indexes.values.find do |lsi|
75
- @query.contain?(lsi.range_key)
73
+ lsi = @source.local_secondary_indexes.values.find do |i|
74
+ @query.contain?(i.range_key)
76
75
  end
77
76
 
78
77
  if lsi.present?
@@ -90,9 +89,9 @@ module Dynamoid #:nodoc:
90
89
  # But only do so if projects ALL attributes otherwise we won't
91
90
  # get back full data
92
91
  def match_global_secondary_index_and_sort_key
93
- gsi = @source.global_secondary_indexes.values.find do |gsi|
94
- @query.contain_with_eq_operator?(gsi.hash_key) && gsi.projected_attributes == :all &&
95
- @query.contain?(gsi.range_key)
92
+ gsi = @source.global_secondary_indexes.values.find do |i|
93
+ @query.contain_with_eq_operator?(i.hash_key) && i.projected_attributes == :all &&
94
+ @query.contain?(i.range_key)
96
95
  end
97
96
 
98
97
  if gsi.present?
@@ -113,8 +112,8 @@ module Dynamoid #:nodoc:
113
112
  end
114
113
 
115
114
  def match_global_secondary_index
116
- gsi = @source.global_secondary_indexes.values.find do |gsi|
117
- @query.contain_with_eq_operator?(gsi.hash_key) && gsi.projected_attributes == :all
115
+ gsi = @source.global_secondary_indexes.values.find do |i|
116
+ @query.contain_with_eq_operator?(i.hash_key) && i.projected_attributes == :all
118
117
  end
119
118
 
120
119
  if gsi.present?
@@ -19,8 +19,8 @@ module Dynamoid
19
19
  fields_list = @nonexistent_fields.map { |s| "`#{s}`" }.join(', ')
20
20
  count = @nonexistent_fields.size
21
21
 
22
- "where conditions contain nonexistent" \
23
- " field #{ 'name'.pluralize(count) } #{ fields_list }"
22
+ 'where conditions contain nonexistent' \
23
+ " field #{'name'.pluralize(count)} #{fields_list}"
24
24
  end
25
25
 
26
26
  private
@@ -37,4 +37,3 @@ module Dynamoid
37
37
  end
38
38
  end
39
39
  end
40
-