dynamoid 3.4.1 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
-