dynamoid 3.12.1 → 3.13.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -1
- data/README.md +3 -3
- data/SECURITY.md +6 -6
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/batch_get_item.rb +3 -1
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +1 -1
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +4 -1
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +4 -1
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/table.rb +13 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +16 -9
- data/lib/dynamoid/fields/declare.rb +1 -1
- data/lib/dynamoid/fields.rb +32 -2
- data/lib/dynamoid/finders.rb +6 -2
- data/lib/dynamoid/persistence.rb +127 -8
- data/lib/dynamoid/transaction_write/base.rb +12 -0
- data/lib/dynamoid/transaction_write/destroy.rb +3 -3
- data/lib/dynamoid/transaction_write/item_updater.rb +6 -1
- data/lib/dynamoid/transaction_write/save.rb +15 -7
- data/lib/dynamoid/transaction_write/update_fields.rb +9 -1
- data/lib/dynamoid/transaction_write/upsert.rb +11 -4
- data/lib/dynamoid/validations.rb +8 -0
- data/lib/dynamoid/version.rb +1 -1
- metadata +6 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b115b0eb8a9244d3482f3b748edf42272f6d9ffa94db20c5c07c7d08a6a3cf6a
|
|
4
|
+
data.tar.gz: 60821b56861e74149f3751f0c58bbc58e6805a9b4db0a4b863527aad99748099
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1eef7ad0569e01d06b8f52a273acff4621a23419bc7f83830dfb37a44149149ec41b010cfa96c4e81d51043b4c7154c7996c72d7e50b599b4ea969ff6bb7575f
|
|
7
|
+
data.tar.gz: ee7561403e8f0bfd2c0e29e2212c0cec903e264c83adc9d238d565054bc98f111d85b3e70e64e838d6c35a434d8b7ef3025eba347ccfee53d249a3834ce53dc0
|
data/CHANGELOG.md
CHANGED
|
@@ -11,7 +11,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
11
11
|
### Changed
|
|
12
12
|
### Removed
|
|
13
13
|
|
|
14
|
-
## 3.
|
|
14
|
+
## 3.13.0
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
* [#944](https://github.com/Dynamoid/dynamoid/pull/944) Fix `#delete` and `#destroy` methods and set `#destroyed?` properly when operations fail
|
|
18
|
+
* [#987](https://github.com/Dynamoid/dynamoid/pull/987) Fix checking that a primary key is given in transactional methods `#save` and `#destroy`
|
|
19
|
+
### Added
|
|
20
|
+
* [#941](https://github.com/Dynamoid/dynamoid/pull/941) Support table ARN and add option `:arn` for the `table` method to specify a table belonged to specific AWS account
|
|
21
|
+
* [#943](https://github.com/Dynamoid/dynamoid/pull/943) Implement `delete` class method
|
|
22
|
+
* [#945](https://github.com/Dynamoid/dynamoid/pull/945) Implement `#update_attribute!` method
|
|
23
|
+
* [#947](https://github.com/Dynamoid/dynamoid/pull/947) Allow skipping default model fields generation and add option `:skip_generating_fields` for the `table` method to specify field names
|
|
24
|
+
* [#988](https://github.com/Dynamoid/dynamoid/pull/988) Add Ruby 4.0 and Rails 8.1 in CI
|
|
25
|
+
### Changed
|
|
26
|
+
### Removed
|
|
27
|
+
|
|
28
|
+
## 3.12.0 / 2025-08-23
|
|
15
29
|
|
|
16
30
|
### Fixed
|
|
17
31
|
* [#849](https://github.com/Dynamoid/dynamoid/pull/849) Fixed saving a field of custom type when it implements both `.dynamoid_dump()` and `#dynamoid_dump` method and use the former one.
|
data/README.md
CHANGED
|
@@ -132,8 +132,8 @@ end
|
|
|
132
132
|
Dynamoid supports Ruby >= 2.3 and Rails >= 4.2.
|
|
133
133
|
|
|
134
134
|
Its compatibility is tested against following Ruby versions: 2.3, 2.4,
|
|
135
|
-
2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3,
|
|
136
|
-
5.2, 6.0, 6.1, 7.0, 7.1, 7.2, and 8.
|
|
135
|
+
2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, and 4.0, JRuby 9.4.x and against Rails versions: 4.2, 5.0, 5.1,
|
|
136
|
+
5.2, 6.0, 6.1, 7.0, 7.1, 7.2, 8.0, and 8.1.
|
|
137
137
|
|
|
138
138
|
## Setup
|
|
139
139
|
|
|
@@ -1131,7 +1131,7 @@ The following actions are supported:
|
|
|
1131
1131
|
|
|
1132
1132
|
* `#create`/`#create!` - add a new model if it does not already exist
|
|
1133
1133
|
* `#save`/`#save!` - create or update model
|
|
1134
|
-
* `#update_attributes`/`#update_attributes!` - modifies one or more attributes from an
|
|
1134
|
+
* `#update_attributes`/`#update_attributes!` - modifies one or more attributes from an existing
|
|
1135
1135
|
model
|
|
1136
1136
|
* `#delete` - remove an model without callbacks nor validations
|
|
1137
1137
|
* `#destroy`/`#destroy!` - remove an model
|
data/SECURITY.md
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
| Version | Supported |
|
|
6
6
|
|---------|-----------|
|
|
7
|
-
| 3.
|
|
8
|
-
|
|
|
7
|
+
| 3.12.x | ✅ |
|
|
8
|
+
| < 3.12 | ❌ |
|
|
9
9
|
| 2.x | ❌ |
|
|
10
10
|
| 1.x | ❌ |
|
|
11
11
|
| 0.x | ❌ |
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Security contact information
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
To report a security vulnerability, please use the
|
|
16
|
+
[Tidelift security contact](https://tidelift.com/security).
|
|
17
|
+
Tidelift will coordinate the fix and disclosure.
|
|
@@ -63,9 +63,11 @@ module Dynamoid
|
|
|
63
63
|
ids.map { |hk, rk| { table.hash_key => hk, table.range_key => rk } }
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
table_name = table.local? ? table.name : table.arn
|
|
67
|
+
|
|
66
68
|
{
|
|
67
69
|
request_items: {
|
|
68
|
-
|
|
70
|
+
table_name => {
|
|
69
71
|
keys: keys,
|
|
70
72
|
consistent_read: options[:consistent_read]
|
|
71
73
|
}
|
|
@@ -58,7 +58,7 @@ module Dynamoid
|
|
|
58
58
|
# delete explicitly attributes if assigned nil value and configured
|
|
59
59
|
# to not store nil values
|
|
60
60
|
values_to_update = values_sanitized.reject { |_, v| v.nil? }
|
|
61
|
-
values_to_delete = values_sanitized.select { |_, v| v.nil? }
|
|
61
|
+
values_to_delete = values_sanitized.select { |_, v| v.nil? } # rubocop:disable Style/PartitionInsteadOfDoubleSelect
|
|
62
62
|
|
|
63
63
|
@updates.merge!(values_to_update)
|
|
64
64
|
@deletions.merge!(values_to_delete)
|
|
@@ -64,6 +64,9 @@ module Dynamoid
|
|
|
64
64
|
name_placeholders = {}
|
|
65
65
|
value_placeholders = {}
|
|
66
66
|
|
|
67
|
+
# use table arn if it's configured for a model
|
|
68
|
+
table_name = table.local? ? table.name : table.arn
|
|
69
|
+
|
|
67
70
|
# Deal with various limits and batching
|
|
68
71
|
batch_size = options[:batch_size]
|
|
69
72
|
limit = [record_limit, scan_limit, batch_size].compact.min
|
|
@@ -93,7 +96,7 @@ module Dynamoid
|
|
|
93
96
|
:exclusive_start_key
|
|
94
97
|
).compact
|
|
95
98
|
|
|
96
|
-
request[:table_name] =
|
|
99
|
+
request[:table_name] = table_name
|
|
97
100
|
request[:limit] = limit if limit
|
|
98
101
|
request[:key_condition_expression] = key_condition_expression if key_condition_expression.present?
|
|
99
102
|
request[:filter_expression] = filter_expression if filter_expression.present?
|
|
@@ -57,6 +57,9 @@ module Dynamoid
|
|
|
57
57
|
name_placeholders = {}
|
|
58
58
|
value_placeholders = {}
|
|
59
59
|
|
|
60
|
+
# use table arn if it's configured for a model
|
|
61
|
+
table_name = table.local? ? table.name : table.arn
|
|
62
|
+
|
|
60
63
|
# Deal with various limits and batching
|
|
61
64
|
batch_size = options[:batch_size]
|
|
62
65
|
limit = [record_limit, scan_limit, batch_size].compact.min
|
|
@@ -79,7 +82,7 @@ module Dynamoid
|
|
|
79
82
|
:index_name
|
|
80
83
|
).compact
|
|
81
84
|
|
|
82
|
-
request[:table_name] =
|
|
85
|
+
request[:table_name] = table_name
|
|
83
86
|
request[:limit] = limit if limit
|
|
84
87
|
request[:filter_expression] = filter_expression if filter_expression.present?
|
|
85
88
|
request[:expression_attribute_values] = value_placeholders if value_placeholders.present?
|
|
@@ -14,6 +14,7 @@ module Dynamoid
|
|
|
14
14
|
#
|
|
15
15
|
def initialize(schema)
|
|
16
16
|
@schema = schema[:table]
|
|
17
|
+
@local = false
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def range_key
|
|
@@ -47,6 +48,18 @@ module Dynamoid
|
|
|
47
48
|
def name
|
|
48
49
|
schema[:table_name]
|
|
49
50
|
end
|
|
51
|
+
|
|
52
|
+
def arn
|
|
53
|
+
schema[:table_arn]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def local!
|
|
57
|
+
@local = true
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def local?
|
|
61
|
+
@local
|
|
62
|
+
end
|
|
50
63
|
end
|
|
51
64
|
end
|
|
52
65
|
end
|
|
@@ -321,6 +321,22 @@ module Dynamoid
|
|
|
321
321
|
false
|
|
322
322
|
end
|
|
323
323
|
|
|
324
|
+
#
|
|
325
|
+
# New, semi-arbitrary API to get data on the table
|
|
326
|
+
#
|
|
327
|
+
def describe_table(table_name, reload: false)
|
|
328
|
+
(!reload && table_cache[table_name]) || begin
|
|
329
|
+
response = client.describe_table(table_name: table_name)
|
|
330
|
+
table = Table.new(response.data)
|
|
331
|
+
|
|
332
|
+
if table.name == table_name.to_s
|
|
333
|
+
table.local!
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
table_cache[table_name] = table
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
324
340
|
def update_time_to_live(table_name, attribute)
|
|
325
341
|
request = {
|
|
326
342
|
table_name: table_name,
|
|
@@ -654,15 +670,6 @@ module Dynamoid
|
|
|
654
670
|
expected
|
|
655
671
|
end
|
|
656
672
|
|
|
657
|
-
#
|
|
658
|
-
# New, semi-arbitrary API to get data on the table
|
|
659
|
-
#
|
|
660
|
-
def describe_table(table_name, reload: false)
|
|
661
|
-
(!reload && table_cache[table_name]) || begin
|
|
662
|
-
table_cache[table_name] = Table.new(client.describe_table(table_name: table_name).data)
|
|
663
|
-
end
|
|
664
|
-
end
|
|
665
|
-
|
|
666
673
|
#
|
|
667
674
|
# Converts a hash returned by get_item, scan, etc. into a key-value hash
|
|
668
675
|
#
|
|
@@ -77,7 +77,7 @@ module Dynamoid
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def warn_if_method_exists(method)
|
|
80
|
-
if @source.
|
|
80
|
+
if @source.method_defined?(method.to_sym)
|
|
81
81
|
Dynamoid.logger.warn("Method #{method} generated for the field #{@name} overrides already existing method")
|
|
82
82
|
end
|
|
83
83
|
end
|
data/lib/dynamoid/fields.rb
CHANGED
|
@@ -134,6 +134,11 @@ module Dynamoid
|
|
|
134
134
|
# @param name [Symbol] name of the field
|
|
135
135
|
# @param type [Symbol] type of the field (optional)
|
|
136
136
|
# @param options [Hash] any additional options for the field type (optional)
|
|
137
|
+
# @option options [Symbol] :of <description>
|
|
138
|
+
# @option options [Symbol] :store_as_string <description>
|
|
139
|
+
# @option options [Symbol] :store_as_native_boolean <description>
|
|
140
|
+
# @option options [Symbol] :default <description>
|
|
141
|
+
# @option options [Symbol] :alias <description>
|
|
137
142
|
#
|
|
138
143
|
# @since 0.2.0
|
|
139
144
|
def field(name, type = :string, options = {})
|
|
@@ -208,9 +213,11 @@ module Dynamoid
|
|
|
208
213
|
#
|
|
209
214
|
# @param options [Hash] options to override default table settings
|
|
210
215
|
# @option options [Symbol] :name name of a table
|
|
216
|
+
# @option options [Symbol] :arn table ARN; it allows referring tables in another AWS accounts; has higher priority than the +name+ option
|
|
211
217
|
# @option options [Symbol] :key name of a hash key attribute
|
|
212
218
|
# @option options [Symbol] :key_type type of a hash key attribute
|
|
213
219
|
# @option options [Symbol] :inheritance_field name of an attribute used for STI
|
|
220
|
+
# @option options [Array<Symbol>] :skip_generating_fields don't generate implicitly methods with given names, e.g. +:id+, +:created_at+, +:updated_at+
|
|
214
221
|
# @option options [Symbol] :capacity_mode table billing mode - either +provisioned+ or +on_demand+
|
|
215
222
|
# @option options [Integer] :write_capacity table write capacity units
|
|
216
223
|
# @option options [Integer] :read_capacity table read capacity units
|
|
@@ -220,11 +227,18 @@ module Dynamoid
|
|
|
220
227
|
# @since 0.4.0
|
|
221
228
|
def table(options)
|
|
222
229
|
self.options = options
|
|
230
|
+
|
|
231
|
+
id_already_declared = true
|
|
232
|
+
timestamps_already_declared = Dynamoid::Config.timestamps
|
|
233
|
+
|
|
223
234
|
# a default 'id' column is created when Dynamoid::Document is included
|
|
224
235
|
unless attributes.key? hash_key
|
|
225
236
|
remove_field :id
|
|
237
|
+
id_already_declared = false
|
|
238
|
+
|
|
226
239
|
key_type = options[:key_type] || :string
|
|
227
240
|
field(hash_key, key_type)
|
|
241
|
+
id_already_declared = hash_key == :id
|
|
228
242
|
end
|
|
229
243
|
|
|
230
244
|
# The created_at/updated_at fields are declared in the `included` callback first.
|
|
@@ -233,14 +247,30 @@ module Dynamoid
|
|
|
233
247
|
# So we need to make decision again and declare the fields or rollback thier declaration.
|
|
234
248
|
#
|
|
235
249
|
# Do not replace with `#timestamps_enabled?`.
|
|
236
|
-
if options[:timestamps] && !
|
|
250
|
+
if options[:timestamps] && !timestamps_already_declared
|
|
237
251
|
# The fields weren't declared in `included` callback because they are disabled globaly
|
|
238
252
|
field :created_at, :datetime
|
|
239
253
|
field :updated_at, :datetime
|
|
240
|
-
|
|
254
|
+
|
|
255
|
+
timestamps_already_declared = true
|
|
256
|
+
elsif options[:timestamps] == false && timestamps_already_declared
|
|
241
257
|
# The fields were declared in `included` callback but they are disabled for a table
|
|
242
258
|
remove_field :created_at
|
|
243
259
|
remove_field :updated_at
|
|
260
|
+
|
|
261
|
+
timestamps_already_declared = false
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# handle :skip_generating_fields option
|
|
265
|
+
skip_generating_fields = options[:skip_generating_fields] || []
|
|
266
|
+
if id_already_declared && skip_generating_fields.include?(:id)
|
|
267
|
+
remove_field :id
|
|
268
|
+
end
|
|
269
|
+
if timestamps_already_declared && skip_generating_fields.include?(:created_at)
|
|
270
|
+
remove_field :created_at
|
|
271
|
+
end
|
|
272
|
+
if timestamps_already_declared && skip_generating_fields.include?(:updated_at)
|
|
273
|
+
remove_field :updated_at
|
|
244
274
|
end
|
|
245
275
|
end
|
|
246
276
|
|
data/lib/dynamoid/finders.rb
CHANGED
|
@@ -133,12 +133,14 @@ module Dynamoid
|
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
read_options = options.slice(:consistent_read)
|
|
136
|
+
table = Dynamoid.adapter.describe_table(table_name)
|
|
136
137
|
|
|
137
138
|
items = if Dynamoid.config.backoff
|
|
138
139
|
items = []
|
|
139
140
|
backoff = nil
|
|
140
141
|
Dynamoid.adapter.read(table_name, ids, read_options) do |hash, has_unprocessed_items|
|
|
141
|
-
|
|
142
|
+
# Cannot use #table_name because it may contain a table ARN, but response contains always a table name
|
|
143
|
+
items += hash[table.name]
|
|
142
144
|
|
|
143
145
|
if has_unprocessed_items
|
|
144
146
|
backoff ||= Dynamoid.config.build_backoff
|
|
@@ -150,7 +152,9 @@ module Dynamoid
|
|
|
150
152
|
items
|
|
151
153
|
else
|
|
152
154
|
items = Dynamoid.adapter.read(table_name, ids, read_options)
|
|
153
|
-
|
|
155
|
+
|
|
156
|
+
# Cannot use #table_name because it may contain a table ARN, but response contains always a table name
|
|
157
|
+
items ? items[table.name] : []
|
|
154
158
|
end
|
|
155
159
|
|
|
156
160
|
if items.size == ids.size || !options[:raise_error]
|
data/lib/dynamoid/persistence.rb
CHANGED
|
@@ -28,9 +28,16 @@ module Dynamoid
|
|
|
28
28
|
|
|
29
29
|
module ClassMethods
|
|
30
30
|
def table_name
|
|
31
|
-
|
|
31
|
+
return @table_name if @table_name
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
if options[:arn]
|
|
34
|
+
@table_name = options[:arn]
|
|
35
|
+
return @table_name
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
base_name = options[:name] || base_class.name.split('::').last.downcase.pluralize
|
|
39
|
+
namespace = Dynamoid::Config.namespace.to_s
|
|
40
|
+
@table_name = [namespace, base_name].reject(&:empty?).join('_')
|
|
34
41
|
end
|
|
35
42
|
|
|
36
43
|
# Create a table.
|
|
@@ -209,6 +216,9 @@ module Dynamoid
|
|
|
209
216
|
# Raises an exception +Dynamoid::Errors::DocumentNotValid+ if validation
|
|
210
217
|
# failed.
|
|
211
218
|
#
|
|
219
|
+
# If any of the +before_*+ callbacks throws +:abort+ the creation is
|
|
220
|
+
# cancelled and +create!+ raises +Dynamoid::Errors::RecordNotSaved+.
|
|
221
|
+
#
|
|
212
222
|
# Accepts both Hash and Array of Hashes and can create several
|
|
213
223
|
# models.
|
|
214
224
|
#
|
|
@@ -448,6 +458,88 @@ module Dynamoid
|
|
|
448
458
|
Inc.call(self, hash_key_value, range_key_value, counters)
|
|
449
459
|
self
|
|
450
460
|
end
|
|
461
|
+
|
|
462
|
+
# Delete a model by a given primary key.
|
|
463
|
+
#
|
|
464
|
+
# Raises +Dynamoid::Errors::MissingHashKey+ if a partition key has value
|
|
465
|
+
# +nil+ and raises +Dynamoid::Errors::MissingRangeKey+ if a sort key is
|
|
466
|
+
# required but has value +nil+ or is missing.
|
|
467
|
+
#
|
|
468
|
+
# @param ids [String|Array] primary key or an array of primary keys
|
|
469
|
+
# @return nil
|
|
470
|
+
#
|
|
471
|
+
# @example Delete a model by given partition key:
|
|
472
|
+
# User.delete(user_id)
|
|
473
|
+
#
|
|
474
|
+
# @example Delete a model by given partition and sort keys:
|
|
475
|
+
# User.delete(user_id, sort_key)
|
|
476
|
+
#
|
|
477
|
+
# @example Delete multiple models by given partition keys:
|
|
478
|
+
# User.delete([id1, id2, id3])
|
|
479
|
+
#
|
|
480
|
+
# @example Delete multiple models by given partition and sort keys:
|
|
481
|
+
# User.delete([[id1, sk1], [id2, sk2], [id3, sk3]])
|
|
482
|
+
def delete(*ids)
|
|
483
|
+
if ids.empty?
|
|
484
|
+
raise Dynamoid::Errors::MissingHashKey
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
if ids[0].is_a?(Array)
|
|
488
|
+
# given multiple keys
|
|
489
|
+
# Model.delete([id1, id2, id3])
|
|
490
|
+
keys = ids[0] # ignore other arguments
|
|
491
|
+
|
|
492
|
+
ids = []
|
|
493
|
+
if self.range_key
|
|
494
|
+
# compound primary key
|
|
495
|
+
# expect [hash key, range key] pairs
|
|
496
|
+
|
|
497
|
+
range_key = []
|
|
498
|
+
|
|
499
|
+
# assume all elements are pairs, that's arrays
|
|
500
|
+
keys.each do |pk, sk|
|
|
501
|
+
raise Dynamoid::Errors::MissingHashKey if pk.nil?
|
|
502
|
+
raise Dynamoid::Errors::MissingRangeKey if self.range_key && sk.nil?
|
|
503
|
+
|
|
504
|
+
partition_key_dumped = cast_and_dump(hash_key, pk)
|
|
505
|
+
sort_key_dumped = cast_and_dump(self.range_key, sk)
|
|
506
|
+
|
|
507
|
+
ids << partition_key_dumped
|
|
508
|
+
range_key << sort_key_dumped
|
|
509
|
+
end
|
|
510
|
+
else
|
|
511
|
+
# simple primary key
|
|
512
|
+
|
|
513
|
+
range_key = nil
|
|
514
|
+
|
|
515
|
+
keys.each do |pk|
|
|
516
|
+
raise Dynamoid::Errors::MissingHashKey if pk.nil?
|
|
517
|
+
|
|
518
|
+
partition_key_dumped = cast_and_dump(hash_key, pk)
|
|
519
|
+
ids << partition_key_dumped
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
options = range_key ? { range_key: range_key } : {}
|
|
524
|
+
Dynamoid.adapter.delete(table_name, ids, options)
|
|
525
|
+
else
|
|
526
|
+
# given single primary key:
|
|
527
|
+
# Model.delete(partition_key)
|
|
528
|
+
# Model.delete(partition_key, sort_key)
|
|
529
|
+
|
|
530
|
+
partition_key, sort_key = ids
|
|
531
|
+
|
|
532
|
+
raise Dynamoid::Errors::MissingHashKey if partition_key.nil?
|
|
533
|
+
raise Dynamoid::Errors::MissingRangeKey if range_key? && sort_key.nil?
|
|
534
|
+
|
|
535
|
+
options = sort_key ? { range_key: cast_and_dump(self.range_key, sort_key) } : {}
|
|
536
|
+
partition_key_dumped = cast_and_dump(hash_key, partition_key)
|
|
537
|
+
|
|
538
|
+
Dynamoid.adapter.delete(table_name, partition_key_dumped, options)
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
nil
|
|
542
|
+
end
|
|
451
543
|
end
|
|
452
544
|
|
|
453
545
|
# Update document timestamps.
|
|
@@ -593,6 +685,9 @@ module Dynamoid
|
|
|
593
685
|
# user = User.new(age: -1)
|
|
594
686
|
# user.save!(validate: false) # => user
|
|
595
687
|
#
|
|
688
|
+
# If any of the +before_*+ callbacks throws +:abort+ the saving is
|
|
689
|
+
# cancelled and +save!+ raises +Dynamoid::Errors::RecordNotSaved+.
|
|
690
|
+
#
|
|
596
691
|
# +save!+ by default sets timestamps attributes - +created_at+ and
|
|
597
692
|
# +updated_at+ when creates new model and updates +updated_at+ attribute
|
|
598
693
|
# when updates already existing one.
|
|
@@ -688,6 +783,10 @@ module Dynamoid
|
|
|
688
783
|
# Raises a +Dynamoid::Errors::DocumentNotValid+ exception if some vaidation
|
|
689
784
|
# fails.
|
|
690
785
|
#
|
|
786
|
+
# If any of the +before_*+ callbacks throws +:abort+ the updating is
|
|
787
|
+
# cancelled and +update_attributes!+ raises
|
|
788
|
+
# +Dynamoid::Errors::RecordNotSaved+.
|
|
789
|
+
#
|
|
691
790
|
# Raises a +Dynamoid::Errors::UnknownAttribute+ exception if any of the
|
|
692
791
|
# attributes is not on the model
|
|
693
792
|
#
|
|
@@ -703,8 +802,6 @@ module Dynamoid
|
|
|
703
802
|
|
|
704
803
|
# Update a single attribute, saving the object afterwards.
|
|
705
804
|
#
|
|
706
|
-
# Returns +true+ if saving is successful and +false+ otherwise.
|
|
707
|
-
#
|
|
708
805
|
# user.update_attribute(:last_name, 'Tylor')
|
|
709
806
|
#
|
|
710
807
|
# Validation is skipped.
|
|
@@ -721,6 +818,28 @@ module Dynamoid
|
|
|
721
818
|
# final implementation is in the Dynamoid::Validation module
|
|
722
819
|
end
|
|
723
820
|
|
|
821
|
+
# Update a single attribute, saving the object afterwards.
|
|
822
|
+
#
|
|
823
|
+
# user.update_attribute!(:last_name, 'Tylor')
|
|
824
|
+
#
|
|
825
|
+
# Validation is skipped.
|
|
826
|
+
#
|
|
827
|
+
# If any of the +before_*+ callbacks throws +:abort+ the updating is
|
|
828
|
+
# cancelled and +update_attribute!+ raises
|
|
829
|
+
# +Dynamoid::Errors::RecordNotSaved+.
|
|
830
|
+
#
|
|
831
|
+
# Raises a +Dynamoid::Errors::UnknownAttribute+ exception if any of the
|
|
832
|
+
# attributes is not on the model
|
|
833
|
+
#
|
|
834
|
+
# @param attribute [Symbol] attribute name to update
|
|
835
|
+
# @param value [Object] the value to assign it
|
|
836
|
+
# @return [Dynamoid::Document] self
|
|
837
|
+
#
|
|
838
|
+
# @since 0.2.0
|
|
839
|
+
def update_attribute!(attribute, value)
|
|
840
|
+
# final implementation is in the Dynamoid::Validation module
|
|
841
|
+
end
|
|
842
|
+
|
|
724
843
|
# Update a model.
|
|
725
844
|
#
|
|
726
845
|
# Doesn't run validation. Runs only +update+ callbacks. Reloads all attribute values.
|
|
@@ -1017,13 +1136,12 @@ module Dynamoid
|
|
|
1017
1136
|
# @return [Dynamoid::Document|false] whether deleted successfully
|
|
1018
1137
|
# @since 0.2.0
|
|
1019
1138
|
def destroy
|
|
1020
|
-
|
|
1139
|
+
run_callbacks(:destroy) do
|
|
1021
1140
|
delete
|
|
1141
|
+
@destroyed = true
|
|
1022
1142
|
end
|
|
1023
1143
|
|
|
1024
|
-
@destroyed
|
|
1025
|
-
|
|
1026
|
-
ret == false ? false : self
|
|
1144
|
+
@destroyed ? self : false
|
|
1027
1145
|
end
|
|
1028
1146
|
|
|
1029
1147
|
# Delete a model.
|
|
@@ -1086,6 +1204,7 @@ module Dynamoid
|
|
|
1086
1204
|
|
|
1087
1205
|
self
|
|
1088
1206
|
rescue Dynamoid::Errors::ConditionalCheckFailedException
|
|
1207
|
+
@destroyed = false
|
|
1089
1208
|
raise Dynamoid::Errors::StaleObjectError.new(self, 'delete')
|
|
1090
1209
|
end
|
|
1091
1210
|
end
|
|
@@ -42,6 +42,18 @@ module Dynamoid
|
|
|
42
42
|
def action_request
|
|
43
43
|
raise 'Not implemented'
|
|
44
44
|
end
|
|
45
|
+
|
|
46
|
+
# copied from aws_sdk_v3
|
|
47
|
+
def sanitize_item(attributes)
|
|
48
|
+
config_value = Dynamoid.config.store_attribute_with_nil_value
|
|
49
|
+
store_attribute_with_nil_value = config_value.nil? ? false : !!config_value
|
|
50
|
+
|
|
51
|
+
attributes.reject do |_, v|
|
|
52
|
+
!store_attribute_with_nil_value && v.nil?
|
|
53
|
+
end.transform_values do |v|
|
|
54
|
+
v.is_a?(Hash) ? v.stringify_keys : v
|
|
55
|
+
end
|
|
56
|
+
end
|
|
45
57
|
end
|
|
46
58
|
end
|
|
47
59
|
end
|
|
@@ -15,10 +15,10 @@ module Dynamoid
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def on_registration
|
|
18
|
-
validate_model!
|
|
19
|
-
|
|
20
18
|
@aborted = true
|
|
21
19
|
@model.run_callbacks(:destroy) do
|
|
20
|
+
validate_primary_key!
|
|
21
|
+
|
|
22
22
|
@aborted = false
|
|
23
23
|
true
|
|
24
24
|
end
|
|
@@ -70,7 +70,7 @@ module Dynamoid
|
|
|
70
70
|
|
|
71
71
|
private
|
|
72
72
|
|
|
73
|
-
def
|
|
73
|
+
def validate_primary_key!
|
|
74
74
|
raise Dynamoid::Errors::MissingHashKey if @model.hash_key.nil?
|
|
75
75
|
raise Dynamoid::Errors::MissingRangeKey if @model_class.range_key? && @model.range_value.nil?
|
|
76
76
|
end
|
|
@@ -20,7 +20,12 @@ module Dynamoid
|
|
|
20
20
|
|
|
21
21
|
def set(attributes)
|
|
22
22
|
validate_attribute_names!(attributes.keys)
|
|
23
|
-
|
|
23
|
+
if Dynamoid.config.store_attribute_with_nil_value
|
|
24
|
+
@attributes_to_set.merge!(attributes)
|
|
25
|
+
else
|
|
26
|
+
@attributes_to_set.merge!(attributes.reject { |_, v| v.nil? })
|
|
27
|
+
@attributes_to_remove += attributes.select { |_, v| v.nil? }.keys
|
|
28
|
+
end
|
|
24
29
|
end
|
|
25
30
|
|
|
26
31
|
# adds to array of fields for use in REMOVE update expression
|
|
@@ -18,8 +18,6 @@ module Dynamoid
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def on_registration
|
|
21
|
-
validate_model!
|
|
22
|
-
|
|
23
21
|
if @options[:validate] != false && !(@valid = @model.valid?)
|
|
24
22
|
if @options[:raise_error]
|
|
25
23
|
raise Dynamoid::Errors::DocumentNotValid, @model
|
|
@@ -35,6 +33,8 @@ module Dynamoid
|
|
|
35
33
|
@model.run_callbacks(:save) do
|
|
36
34
|
@model.run_callbacks(callback_name) do
|
|
37
35
|
@model.run_callbacks(:validate) do
|
|
36
|
+
validate_primary_key!
|
|
37
|
+
|
|
38
38
|
@aborted = false
|
|
39
39
|
true
|
|
40
40
|
end
|
|
@@ -88,7 +88,7 @@ module Dynamoid
|
|
|
88
88
|
|
|
89
89
|
private
|
|
90
90
|
|
|
91
|
-
def
|
|
91
|
+
def validate_primary_key!
|
|
92
92
|
raise Dynamoid::Errors::MissingHashKey if !@was_new_record && @model.hash_key.nil?
|
|
93
93
|
raise Dynamoid::Errors::MissingRangeKey if @model_class.range_key? && @model.range_value.nil?
|
|
94
94
|
end
|
|
@@ -97,6 +97,7 @@ module Dynamoid
|
|
|
97
97
|
touch_model_timestamps(skip_created_at: false)
|
|
98
98
|
|
|
99
99
|
attributes_dumped = Dynamoid::Dumping.dump_attributes(@model.attributes, @model_class.attributes)
|
|
100
|
+
attributes_dumped = sanitize_item(attributes_dumped)
|
|
100
101
|
|
|
101
102
|
# require primary key not to exist yet
|
|
102
103
|
condition = "attribute_not_exists(#{@model_class.hash_key})"
|
|
@@ -126,7 +127,8 @@ module Dynamoid
|
|
|
126
127
|
|
|
127
128
|
# Build UpdateExpression and keep names and values placeholders mapping
|
|
128
129
|
# in ExpressionAttributeNames and ExpressionAttributeValues.
|
|
129
|
-
|
|
130
|
+
set_expression_statements = []
|
|
131
|
+
remove_expression_statements = []
|
|
130
132
|
expression_attribute_names = {}
|
|
131
133
|
expression_attribute_values = {}
|
|
132
134
|
|
|
@@ -134,12 +136,18 @@ module Dynamoid
|
|
|
134
136
|
name_placeholder = "#_n#{i}"
|
|
135
137
|
value_placeholder = ":_s#{i}"
|
|
136
138
|
|
|
137
|
-
|
|
139
|
+
if value || Dynamoid.config.store_attribute_with_nil_value
|
|
140
|
+
set_expression_statements << "#{name_placeholder} = #{value_placeholder}"
|
|
141
|
+
expression_attribute_values[value_placeholder] = value
|
|
142
|
+
else
|
|
143
|
+
remove_expression_statements << name_placeholder
|
|
144
|
+
end
|
|
138
145
|
expression_attribute_names[name_placeholder] = name
|
|
139
|
-
expression_attribute_values[value_placeholder] = value
|
|
140
146
|
end
|
|
141
147
|
|
|
142
|
-
update_expression =
|
|
148
|
+
update_expression = ''
|
|
149
|
+
update_expression += "SET #{set_expression_statements.join(', ')}" if set_expression_statements.any?
|
|
150
|
+
update_expression += " REMOVE #{remove_expression_statements.join(', ')}" if remove_expression_statements.any?
|
|
143
151
|
|
|
144
152
|
{
|
|
145
153
|
update: {
|
|
@@ -54,7 +54,15 @@ module Dynamoid
|
|
|
54
54
|
changes = add_timestamps(changes, skip_created_at: true)
|
|
55
55
|
changes_dumped = Dynamoid::Dumping.dump_attributes(changes, @model_class.attributes)
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
if Dynamoid.config.store_attribute_with_nil_value
|
|
58
|
+
builder.set_attributes(changes_dumped)
|
|
59
|
+
else
|
|
60
|
+
nil_attributes = changes_dumped.select { |_, v| v.nil? }
|
|
61
|
+
non_nil_attributes = changes_dumped.reject { |_, v| v.nil? } # rubocop:disable Style/PartitionInsteadOfDoubleSelect
|
|
62
|
+
|
|
63
|
+
builder.remove_attributes(nil_attributes.keys)
|
|
64
|
+
builder.set_attributes(non_nil_attributes)
|
|
65
|
+
end
|
|
58
66
|
|
|
59
67
|
# given a block
|
|
60
68
|
if @item_updater
|
|
@@ -54,7 +54,8 @@ module Dynamoid
|
|
|
54
54
|
|
|
55
55
|
# Build UpdateExpression and keep names and values placeholders mapping
|
|
56
56
|
# in ExpressionAttributeNames and ExpressionAttributeValues.
|
|
57
|
-
|
|
57
|
+
set_expression_statements = []
|
|
58
|
+
remove_expression_statements = []
|
|
58
59
|
expression_attribute_names = {}
|
|
59
60
|
expression_attribute_values = {}
|
|
60
61
|
|
|
@@ -62,12 +63,18 @@ module Dynamoid
|
|
|
62
63
|
name_placeholder = "#_n#{i}"
|
|
63
64
|
value_placeholder = ":_s#{i}"
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
if value || Dynamoid.config.store_attribute_with_nil_value
|
|
67
|
+
set_expression_statements << "#{name_placeholder} = #{value_placeholder}"
|
|
68
|
+
expression_attribute_values[value_placeholder] = value
|
|
69
|
+
else
|
|
70
|
+
remove_expression_statements << name_placeholder
|
|
71
|
+
end
|
|
66
72
|
expression_attribute_names[name_placeholder] = name
|
|
67
|
-
expression_attribute_values[value_placeholder] = value
|
|
68
73
|
end
|
|
69
74
|
|
|
70
|
-
update_expression =
|
|
75
|
+
update_expression = ''
|
|
76
|
+
update_expression += "SET #{set_expression_statements.join(', ')}" if set_expression_statements.any?
|
|
77
|
+
update_expression += " REMOVE #{remove_expression_statements.join(', ')}" if remove_expression_statements.any?
|
|
71
78
|
|
|
72
79
|
{
|
|
73
80
|
update: {
|
data/lib/dynamoid/validations.rb
CHANGED
|
@@ -47,6 +47,14 @@ module Dynamoid
|
|
|
47
47
|
self
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
def update_attribute!(attribute, value)
|
|
51
|
+
write_attribute(attribute, value)
|
|
52
|
+
save!(validate: false)
|
|
53
|
+
self
|
|
54
|
+
rescue Dynamoid::Errors::StaleObjectError
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
50
58
|
module ClassMethods
|
|
51
59
|
# Override validates_presence_of to handle false values as present.
|
|
52
60
|
#
|
data/lib/dynamoid/version.rb
CHANGED
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.
|
|
4
|
+
version: 3.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Josh Symonds
|
|
@@ -18,10 +18,9 @@ authors:
|
|
|
18
18
|
- Brian Glusman
|
|
19
19
|
- Peter Boling
|
|
20
20
|
- Andrew Konchin
|
|
21
|
-
autorequire:
|
|
22
21
|
bindir: bin
|
|
23
22
|
cert_chain: []
|
|
24
|
-
date:
|
|
23
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
25
24
|
dependencies:
|
|
26
25
|
- !ruby/object:Gem::Dependency
|
|
27
26
|
name: activemodel
|
|
@@ -264,14 +263,13 @@ licenses:
|
|
|
264
263
|
- MIT
|
|
265
264
|
metadata:
|
|
266
265
|
homepage_uri: http://github.com/Dynamoid/dynamoid
|
|
267
|
-
source_code_uri: https://github.com/Dynamoid/dynamoid/tree/v3.
|
|
268
|
-
changelog_uri: https://github.com/Dynamoid/dynamoid/blob/v3.
|
|
266
|
+
source_code_uri: https://github.com/Dynamoid/dynamoid/tree/v3.13.0
|
|
267
|
+
changelog_uri: https://github.com/Dynamoid/dynamoid/blob/v3.13.0/CHANGELOG.md
|
|
269
268
|
bug_tracker_uri: https://github.com/Dynamoid/dynamoid/issues
|
|
270
|
-
documentation_uri: https://www.rubydoc.info/gems/dynamoid/3.
|
|
269
|
+
documentation_uri: https://www.rubydoc.info/gems/dynamoid/3.13.0
|
|
271
270
|
funding_uri: https://opencollective.com/dynamoid
|
|
272
271
|
wiki_uri: https://github.com/Dynamoid/dynamoid/wiki
|
|
273
272
|
rubygems_mfa_required: 'true'
|
|
274
|
-
post_install_message:
|
|
275
273
|
rdoc_options: []
|
|
276
274
|
require_paths:
|
|
277
275
|
- lib
|
|
@@ -286,8 +284,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
286
284
|
- !ruby/object:Gem::Version
|
|
287
285
|
version: '0'
|
|
288
286
|
requirements: []
|
|
289
|
-
rubygems_version: 3.
|
|
290
|
-
signing_key:
|
|
287
|
+
rubygems_version: 3.6.9
|
|
291
288
|
specification_version: 4
|
|
292
289
|
summary: Dynamoid is an ORM for Amazon's DynamoDB
|
|
293
290
|
test_files: []
|