dynamoid 3.4.1 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/lib/dynamoid/adapter.rb +2 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +23 -23
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/batch_get_item.rb +2 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +1 -1
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/backoff.rb +1 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb +1 -3
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/start_key.rb +1 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +3 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +3 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/until_past_table_status.rb +1 -1
- data/lib/dynamoid/config.rb +2 -3
- data/lib/dynamoid/criteria.rb +1 -1
- data/lib/dynamoid/criteria/chain.rb +37 -12
- data/lib/dynamoid/criteria/ignored_conditions_detector.rb +2 -3
- data/lib/dynamoid/criteria/key_fields_detector.rb +7 -8
- data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +2 -2
- data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +0 -1
- data/lib/dynamoid/dirty.rb +50 -50
- data/lib/dynamoid/document.rb +2 -1
- data/lib/dynamoid/fields.rb +14 -1
- data/lib/dynamoid/finders.rb +4 -4
- data/lib/dynamoid/indexes.rb +2 -2
- data/lib/dynamoid/persistence.rb +32 -6
- data/lib/dynamoid/persistence/upsert.rb +0 -1
- data/lib/dynamoid/tasks.rb +3 -1
- data/lib/dynamoid/type_casting.rb +0 -2
- data/lib/dynamoid/version.rb +1 -1
- metadata +40 -56
- data/.coveralls.yml +0 -1
- data/.document +0 -5
- data/.gitignore +0 -74
- data/.rspec +0 -2
- data/.rubocop.yml +0 -71
- data/.rubocop_todo.yml +0 -55
- data/.travis.yml +0 -44
- data/Appraisals +0 -22
- data/Gemfile +0 -8
- data/Rakefile +0 -46
- data/Vagrantfile +0 -29
- data/docker-compose.yml +0 -7
- data/dynamoid.gemspec +0 -57
- data/gemfiles/rails_4_2.gemfile +0 -9
- data/gemfiles/rails_5_0.gemfile +0 -8
- data/gemfiles/rails_5_1.gemfile +0 -8
- data/gemfiles/rails_5_2.gemfile +0 -8
- data/gemfiles/rails_6_0.gemfile +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16a6980d441040611894ad405ce797b6ef655e9e2730b010d72ff3e65771129f
|
4
|
+
data.tar.gz: 1ca556953abc411f0b7e2fd5e5c74518a7da4381b86449b2f49e81d9f76ee0a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71856592a8a381b27517682c6ce7bb48047de11dc2fbb17c175af629d3ec7e2b63895e9b24c943b57a93e47db0b1de390454b5a89d71899a59c9208a79e0595e
|
7
|
+
data.tar.gz: 8cd759df57475c7566ef0ceae781d21eb443cb1dcd0f6da0f3eaa778fe6a3abeac8865a1198a2dbd2f435eec2cb5317b47ce5e5bd5b9dadbe7537a412053492b
|
data/CHANGELOG.md
CHANGED
@@ -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
|
data/lib/dynamoid/adapter.rb
CHANGED
@@ -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
|
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 [
|
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 }.
|
529
|
-
|
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
|
@@ -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
|
@@ -29,8 +29,8 @@ module Dynamoid
|
|
29
29
|
request = build_request
|
30
30
|
|
31
31
|
Enumerator.new do |yielder|
|
32
|
-
api_call =
|
33
|
-
client.query(
|
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 =
|
25
|
-
client.scan(
|
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
|
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
|
data/lib/dynamoid/config.rb
CHANGED
@@ -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}"
|
17
|
+
"dynamoid_#{app_name}_#{Rails.env}"
|
18
18
|
else
|
19
|
-
'dynamoid'
|
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
|
data/lib/dynamoid/criteria.rb
CHANGED
@@ -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 { |
|
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
|
-
|
212
|
+
raw_pages_via_query
|
186
213
|
else
|
187
214
|
issue_scan_warning if Dynamoid::Config.warn_on_scan && query.present?
|
188
|
-
|
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
|
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
|
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
|
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
|
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 { |
|
28
|
-
.flat_map { |
|
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 |
|
75
|
-
@query.contain?(
|
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 |
|
94
|
-
@query.contain_with_eq_operator?(
|
95
|
-
@query.contain?(
|
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 |
|
117
|
-
@query.contain_with_eq_operator?(
|
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
|
-
|
23
|
-
" field #{
|
22
|
+
'where conditions contain nonexistent' \
|
23
|
+
" field #{'name'.pluralize(count)} #{fields_list}"
|
24
24
|
end
|
25
25
|
|
26
26
|
private
|