dynamoid 3.7.0 → 3.8.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 +206 -238
- data/LICENSE.txt +2 -2
- data/README.md +43 -18
- data/lib/dynamoid/adapter.rb +5 -8
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/batch_get_item.rb +10 -1
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +20 -14
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +2 -1
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +45 -52
- data/lib/dynamoid/associations/single_association.rb +28 -9
- data/lib/dynamoid/criteria/chain.rb +10 -6
- data/lib/dynamoid/criteria/key_fields_detector.rb +1 -1
- data/lib/dynamoid/criteria.rb +6 -7
- data/lib/dynamoid/dirty.rb +6 -9
- data/lib/dynamoid/document.rb +4 -10
- data/lib/dynamoid/fields.rb +16 -17
- data/lib/dynamoid/indexes.rb +45 -38
- data/lib/dynamoid/loadable.rb +6 -1
- data/lib/dynamoid/persistence/update_fields.rb +1 -1
- data/lib/dynamoid/persistence/upsert.rb +1 -1
- data/lib/dynamoid/persistence.rb +61 -18
- data/lib/dynamoid/undumping.rb +18 -0
- data/lib/dynamoid/validations.rb +6 -0
- data/lib/dynamoid/version.rb +1 -1
- data/lib/dynamoid.rb +1 -0
- metadata +40 -37
data/LICENSE.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
MIT License
|
|
1
|
+
The MIT License (MIT)
|
|
2
2
|
|
|
3
3
|
Copyright (c) 2012 Josh Symonds
|
|
4
|
-
Copyright (c) 2013 -
|
|
4
|
+
Copyright (c) 2013 - 2022 Dynamoid, https://github.com/Dynamoid
|
|
5
5
|
|
|
6
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
# Dynamoid
|
|
2
2
|
|
|
3
|
-
[](https://github.com/Dynamoid/dynamoid/actions/workflows/ci.yml/badge.svg?branch=master)
|
|
4
4
|
[](https://codeclimate.com/github/Dynamoid/dynamoid)
|
|
5
5
|
[](https://coveralls.io/github/Dynamoid/dynamoid?branch=master)
|
|
6
6
|
[](https://www.codetriage.com/dynamoid/dynamoid)
|
|
7
7
|
[](https://www.rubydoc.info/github/Dynamoid/dynamoid/frames)
|
|
8
8
|
[](http://inch-ci.org/github/Dynamoid/Dynamoid)
|
|
9
9
|

|
|
10
|
+
[![GitMoji][🖐gitmoji-img]][🖐gitmoji]
|
|
11
|
+
[![SemVer 2.0.0][🧮semver-img]][semver]
|
|
12
|
+
[![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog]
|
|
13
|
+
|
|
14
|
+
[🖐gitmoji]: https://gitmoji.dev
|
|
15
|
+
[🖐gitmoji-img]: https://img.shields.io/badge/gitmoji-3.9.0-FFDD67.svg?style=flat
|
|
16
|
+
[🧮semver-img]: https://img.shields.io/badge/semver-2.0.0-FFDD67.svg?style=flat
|
|
17
|
+
[📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
|
|
18
|
+
[📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-FFDD67.svg?style=flat
|
|
10
19
|
|
|
11
20
|
Dynamoid is an ORM for Amazon's DynamoDB for Ruby applications. It
|
|
12
21
|
provides similar functionality to ActiveRecord and improves on Amazon's
|
|
@@ -127,7 +136,7 @@ Dynamoid supports Ruby >= 2.3 and Rails >= 4.2.
|
|
|
127
136
|
|
|
128
137
|
Its compatibility is tested against following Ruby versions: 2.3, 2.4,
|
|
129
138
|
2.5, 2.6, 2.7 and 3.0, JRuby 9.2.x and against Rails versions: 4.2, 5.0, 5.1,
|
|
130
|
-
5.2, 6.0
|
|
139
|
+
5.2, 6.0, 6.1 and 7.0.
|
|
131
140
|
|
|
132
141
|
## Setup
|
|
133
142
|
|
|
@@ -468,6 +477,10 @@ Second argument, type, is optional. Default type is `string`.
|
|
|
468
477
|
Just like in ActiveRecord (or your other favorite ORM), Dynamoid uses
|
|
469
478
|
associations to create links between models.
|
|
470
479
|
|
|
480
|
+
**WARNING:** Associations are not supported for models with compound
|
|
481
|
+
primary key. If a model declares a range key it should not declare any
|
|
482
|
+
association itself and be referenced by an association in another model.
|
|
483
|
+
|
|
471
484
|
The only supported associations (so far) are `has_many`, `has_one`,
|
|
472
485
|
`has_and_belongs_to_many`, and `belongs_to`. Associations are very
|
|
473
486
|
simple to create: just specify the type, the name, and then any options
|
|
@@ -1073,7 +1086,7 @@ Listed below are all configuration options.
|
|
|
1073
1086
|
when referring to them. Isn't thread safe. Default is `false`.
|
|
1074
1087
|
`Use Dynamoid::Middleware::IdentityMap` to clear identity map for each HTTP request
|
|
1075
1088
|
* `timestamps` - by default Dynamoid sets `created_at` and `updated_at`
|
|
1076
|
-
fields
|
|
1089
|
+
fields at model creation and updating. You can disable this
|
|
1077
1090
|
behavior by setting `false` value
|
|
1078
1091
|
* `sync_retry_max_times` - when Dynamoid creates or deletes table
|
|
1079
1092
|
synchronously it checks for completion specified times. Default is 60
|
|
@@ -1335,7 +1348,7 @@ DynamoDB running locally. Follow these steps to setup your test
|
|
|
1335
1348
|
environment.
|
|
1336
1349
|
|
|
1337
1350
|
* First download and unpack the latest version of DynamoDB. We have a
|
|
1338
|
-
script that will do this for you if you use homebrew on a Mac.
|
|
1351
|
+
script that will do this for you if you use bash, and homebrew on a Mac.
|
|
1339
1352
|
|
|
1340
1353
|
```shell
|
|
1341
1354
|
bin/setup
|
|
@@ -1360,25 +1373,37 @@ environment.
|
|
|
1360
1373
|
bin/stop_dynamodblocal
|
|
1361
1374
|
```
|
|
1362
1375
|
|
|
1363
|
-
If you
|
|
1364
|
-
|
|
1365
|
-
2.3.8 2.4.6 2.5.5 2.6.3 2.7.0 3.0.0 9.2.14.0)`. When you run
|
|
1366
|
-
`bundle exec wwtd` it will take care of starting and stopping the local
|
|
1367
|
-
dynamodb instance.
|
|
1368
|
-
|
|
1376
|
+
If you run into issues, please try these steps first.
|
|
1377
|
+
NOTE: You can use any version manager: rvm, rbenv, chruby, asdf-ruby
|
|
1369
1378
|
```shell
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
gem
|
|
1379
|
+
asdf install ruby 3.1.1
|
|
1380
|
+
asdf local ruby 3.1.1
|
|
1381
|
+
gem update --system
|
|
1373
1382
|
bundle install
|
|
1374
1383
|
```
|
|
1375
1384
|
|
|
1376
|
-
##
|
|
1385
|
+
## Security
|
|
1386
|
+
|
|
1387
|
+
See [SECURITY.md][security].
|
|
1388
|
+
|
|
1389
|
+
## Related links
|
|
1390
|
+
|
|
1391
|
+
This documentation may be useful for the contributors:
|
|
1392
|
+
- <https://docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/welcome.html>
|
|
1393
|
+
- <https://docs.aws.amazon.com/sdk-for-ruby/v3/api/index.html>
|
|
1394
|
+
|
|
1395
|
+
## License
|
|
1396
|
+
|
|
1397
|
+
The gem is available as open source under the terms of
|
|
1398
|
+
the [MIT License][license] [][license-ref].
|
|
1399
|
+
See [LICENSE][license] for the official [Copyright Notice][copyright-notice-explainer].
|
|
1400
|
+
|
|
1401
|
+
[copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
|
1377
1402
|
|
|
1378
|
-
|
|
1403
|
+
[license]: https://github.com/Dynamoid/dynamoid/blob/master/LICENSE.txt
|
|
1379
1404
|
|
|
1380
|
-
|
|
1405
|
+
[license-ref]: https://opensource.org/licenses/MIT
|
|
1381
1406
|
|
|
1382
|
-
|
|
1407
|
+
[security]: https://github.com/Dynamoid/dynamoid/blob/master/SECURITY.md
|
|
1383
1408
|
|
|
1384
|
-
|
|
1409
|
+
[semver]: http://semver.org/
|
data/lib/dynamoid/adapter.rb
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
# https://github.com/ruby-concurrency/concurrent-ruby/pull/377
|
|
5
|
-
require 'concurrent'
|
|
3
|
+
require 'concurrent/atom'
|
|
6
4
|
require 'dynamoid/adapter_plugin/aws_sdk_v3'
|
|
7
5
|
|
|
8
|
-
# encoding: utf-8
|
|
9
6
|
module Dynamoid
|
|
10
7
|
# Adapter's value-add:
|
|
11
8
|
# 1) For the rest of Dynamoid, the gateway to DynamoDB.
|
|
@@ -44,7 +41,7 @@ module Dynamoid
|
|
|
44
41
|
|
|
45
42
|
# Shows how long it takes a method to run on the adapter. Useful for generating logged output.
|
|
46
43
|
#
|
|
47
|
-
# @param [Symbol] method the name of the method to appear in the log
|
|
44
|
+
# @param [Symbol|String] method the name of the method to appear in the log
|
|
48
45
|
# @param [Array] args the arguments to the method to appear in the log
|
|
49
46
|
# @yield the actual code to benchmark
|
|
50
47
|
#
|
|
@@ -78,7 +75,7 @@ module Dynamoid
|
|
|
78
75
|
# that the batch get will acquire the correct record.
|
|
79
76
|
#
|
|
80
77
|
# @param [String] table the name of the table to write the object to
|
|
81
|
-
# @param [Array] ids to fetch
|
|
78
|
+
# @param [String, Array] ids to fetch; can also be a string of just one id
|
|
82
79
|
# @param [Hash] options Passed to the underlying query. The :range_key option is required whenever the table has a range key,
|
|
83
80
|
# unless multiple ids are passed in.
|
|
84
81
|
#
|
|
@@ -94,7 +91,7 @@ module Dynamoid
|
|
|
94
91
|
# Delete an item from a table.
|
|
95
92
|
#
|
|
96
93
|
# @param [String] table the name of the table to write the object to
|
|
97
|
-
# @param [Array] ids to delete
|
|
94
|
+
# @param [String, Array] ids to delete; can also be a string of just one id
|
|
98
95
|
# @param [Hash] options allowed only +range_key+ - range key or array of
|
|
99
96
|
# range keys of the record to delete, can also be
|
|
100
97
|
# a string of just one range_key, and +conditions+
|
|
@@ -150,7 +147,7 @@ module Dynamoid
|
|
|
150
147
|
#
|
|
151
148
|
# @since 0.2.0
|
|
152
149
|
define_method(m) do |*args, &blk|
|
|
153
|
-
benchmark(m
|
|
150
|
+
benchmark(m, *args) { adapter.send(m, *args, &blk) }
|
|
154
151
|
end
|
|
155
152
|
end
|
|
156
153
|
|
|
@@ -86,7 +86,16 @@ module Dynamoid
|
|
|
86
86
|
def unprocessed_ids(table)
|
|
87
87
|
# unprocessed_keys Hash contains as values instances of
|
|
88
88
|
# Aws::DynamoDB::Types::KeysAndAttributes
|
|
89
|
-
@api_response.unprocessed_keys[table.name].keys.map
|
|
89
|
+
@api_response.unprocessed_keys[table.name].keys.map do |h|
|
|
90
|
+
# If a table has a composite primary key then we need to return an array
|
|
91
|
+
# of [hash key, range key]. Otherwise just return hash key's
|
|
92
|
+
# value.
|
|
93
|
+
if table.range_key.nil?
|
|
94
|
+
h[table.hash_key.to_s]
|
|
95
|
+
else
|
|
96
|
+
[h[table.hash_key.to_s], h[table.range_key.to_s]]
|
|
97
|
+
end
|
|
98
|
+
end
|
|
90
99
|
end
|
|
91
100
|
|
|
92
101
|
def items_grouped_by_table
|
|
@@ -6,6 +6,10 @@ module Dynamoid
|
|
|
6
6
|
class AwsSdkV3
|
|
7
7
|
# Mimics behavior of the yielded object on DynamoDB's update_item API (high level).
|
|
8
8
|
class ItemUpdater
|
|
9
|
+
ADD = 'ADD'
|
|
10
|
+
DELETE = 'DELETE'
|
|
11
|
+
PUT = 'PUT'
|
|
12
|
+
|
|
9
13
|
attr_reader :table, :key, :range_key
|
|
10
14
|
|
|
11
15
|
def initialize(table, key, range_key = nil)
|
|
@@ -31,11 +35,15 @@ module Dynamoid
|
|
|
31
35
|
#
|
|
32
36
|
# Removes values from the sets of the given columns
|
|
33
37
|
#
|
|
34
|
-
# @param [Hash] values keys of the hash are the columns, values are Arrays/Sets of items
|
|
35
|
-
#
|
|
38
|
+
# @param [Hash|Symbol|String] values keys of the hash are the columns, values are Arrays/Sets of items
|
|
39
|
+
# to remove
|
|
36
40
|
#
|
|
37
41
|
def delete(values)
|
|
38
|
-
|
|
42
|
+
if values.is_a?(Hash)
|
|
43
|
+
@deletions.merge!(sanitize_attributes(values))
|
|
44
|
+
else
|
|
45
|
+
@deletions.merge!(values.to_s => nil)
|
|
46
|
+
end
|
|
39
47
|
end
|
|
40
48
|
|
|
41
49
|
#
|
|
@@ -48,29 +56,31 @@ module Dynamoid
|
|
|
48
56
|
#
|
|
49
57
|
# Returns an AttributeUpdates hash suitable for passing to the V2 Client API
|
|
50
58
|
#
|
|
51
|
-
def
|
|
52
|
-
|
|
59
|
+
def attribute_updates
|
|
60
|
+
result = {}
|
|
53
61
|
|
|
54
62
|
@additions.each do |k, v|
|
|
55
|
-
|
|
63
|
+
result[k] = {
|
|
56
64
|
action: ADD,
|
|
57
65
|
value: v
|
|
58
66
|
}
|
|
59
67
|
end
|
|
68
|
+
|
|
60
69
|
@deletions.each do |k, v|
|
|
61
|
-
|
|
70
|
+
result[k] = {
|
|
62
71
|
action: DELETE
|
|
63
72
|
}
|
|
64
|
-
|
|
73
|
+
result[k][:value] = v unless v.nil?
|
|
65
74
|
end
|
|
75
|
+
|
|
66
76
|
@updates.each do |k, v|
|
|
67
|
-
|
|
77
|
+
result[k] = {
|
|
68
78
|
action: PUT,
|
|
69
79
|
value: v
|
|
70
80
|
}
|
|
71
81
|
end
|
|
72
82
|
|
|
73
|
-
|
|
83
|
+
result
|
|
74
84
|
end
|
|
75
85
|
|
|
76
86
|
private
|
|
@@ -80,10 +90,6 @@ module Dynamoid
|
|
|
80
90
|
v.is_a?(Hash) ? v.stringify_keys : v
|
|
81
91
|
end
|
|
82
92
|
end
|
|
83
|
-
|
|
84
|
-
ADD = 'ADD'
|
|
85
|
-
DELETE = 'DELETE'
|
|
86
|
-
PUT = 'PUT'
|
|
87
93
|
end
|
|
88
94
|
end
|
|
89
95
|
end
|
|
@@ -163,30 +163,28 @@ module Dynamoid
|
|
|
163
163
|
def batch_write_item(table_name, objects, options = {})
|
|
164
164
|
items = objects.map { |o| sanitize_item(o) }
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
requests = batch.map { |item| { put_request: { item: item } } }
|
|
170
|
-
|
|
171
|
-
response = client.batch_write_item(
|
|
172
|
-
{
|
|
173
|
-
request_items: {
|
|
174
|
-
table_name => requests
|
|
175
|
-
},
|
|
176
|
-
return_consumed_capacity: 'TOTAL',
|
|
177
|
-
return_item_collection_metrics: 'SIZE'
|
|
178
|
-
}.merge!(options)
|
|
179
|
-
)
|
|
166
|
+
while items.present?
|
|
167
|
+
batch = items.shift(BATCH_WRITE_ITEM_REQUESTS_LIMIT)
|
|
168
|
+
requests = batch.map { |item| { put_request: { item: item } } }
|
|
180
169
|
|
|
181
|
-
|
|
170
|
+
response = client.batch_write_item(
|
|
171
|
+
{
|
|
172
|
+
request_items: {
|
|
173
|
+
table_name => requests
|
|
174
|
+
},
|
|
175
|
+
return_consumed_capacity: 'TOTAL',
|
|
176
|
+
return_item_collection_metrics: 'SIZE'
|
|
177
|
+
}.merge!(options)
|
|
178
|
+
)
|
|
182
179
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
180
|
+
yield(response.unprocessed_items.present?) if block_given?
|
|
181
|
+
|
|
182
|
+
if response.unprocessed_items.present?
|
|
183
|
+
items += response.unprocessed_items[table_name].map { |r| r.put_request.item }
|
|
186
184
|
end
|
|
187
|
-
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
|
|
188
|
-
raise Dynamoid::Errors::ConditionalCheckFailedException, e
|
|
189
185
|
end
|
|
186
|
+
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
|
|
187
|
+
raise Dynamoid::Errors::ConditionalCheckFailedException, e
|
|
190
188
|
end
|
|
191
189
|
|
|
192
190
|
# Get many items at once from DynamoDB. More efficient than getting each item individually.
|
|
@@ -258,17 +256,15 @@ module Dynamoid
|
|
|
258
256
|
end
|
|
259
257
|
end
|
|
260
258
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
)
|
|
268
|
-
end
|
|
269
|
-
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
|
|
270
|
-
raise Dynamoid::Errors::ConditionalCheckFailedException, e
|
|
259
|
+
requests.each do |items|
|
|
260
|
+
client.batch_write_item(
|
|
261
|
+
request_items: items,
|
|
262
|
+
return_consumed_capacity: 'TOTAL',
|
|
263
|
+
return_item_collection_metrics: 'SIZE'
|
|
264
|
+
)
|
|
271
265
|
end
|
|
266
|
+
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
|
|
267
|
+
raise Dynamoid::Errors::ConditionalCheckFailedException, e
|
|
272
268
|
end
|
|
273
269
|
|
|
274
270
|
# Create a table on DynamoDB. This usually takes a long time to complete.
|
|
@@ -416,20 +412,19 @@ module Dynamoid
|
|
|
416
412
|
conditions = options.delete(:conditions)
|
|
417
413
|
table = describe_table(table_name)
|
|
418
414
|
|
|
419
|
-
|
|
415
|
+
item_updater = ItemUpdater.new(table, key, range_key)
|
|
416
|
+
yield(item_updater)
|
|
420
417
|
|
|
421
418
|
raise "non-empty options: #{options}" unless options.empty?
|
|
422
419
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
raise Dynamoid::Errors::ConditionalCheckFailedException, e
|
|
432
|
-
end
|
|
420
|
+
result = client.update_item(table_name: table_name,
|
|
421
|
+
key: key_stanza(table, key, range_key),
|
|
422
|
+
attribute_updates: item_updater.attribute_updates,
|
|
423
|
+
expected: expected_stanza(conditions),
|
|
424
|
+
return_values: 'ALL_NEW')
|
|
425
|
+
result_item_to_hash(result[:attributes])
|
|
426
|
+
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
|
|
427
|
+
raise Dynamoid::Errors::ConditionalCheckFailedException, e
|
|
433
428
|
end
|
|
434
429
|
|
|
435
430
|
# List all tables on DynamoDB.
|
|
@@ -459,17 +454,15 @@ module Dynamoid
|
|
|
459
454
|
options ||= {}
|
|
460
455
|
item = sanitize_item(object)
|
|
461
456
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
raise Dynamoid::Errors::ConditionalCheckFailedException, e
|
|
472
|
-
end
|
|
457
|
+
client.put_item(
|
|
458
|
+
{
|
|
459
|
+
table_name: table_name,
|
|
460
|
+
item: item,
|
|
461
|
+
expected: expected_stanza(options)
|
|
462
|
+
}.merge!(options)
|
|
463
|
+
)
|
|
464
|
+
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
|
|
465
|
+
raise Dynamoid::Errors::ConditionalCheckFailedException, e
|
|
473
466
|
end
|
|
474
467
|
|
|
475
468
|
# Query the DynamoDB table. This employs DynamoDB's indexes so is generally faster than scanning, but is
|
|
@@ -64,18 +64,37 @@ module Dynamoid
|
|
|
64
64
|
target == other
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
target.
|
|
74
|
-
|
|
75
|
-
|
|
67
|
+
if ::RUBY_VERSION < '2.7'
|
|
68
|
+
# Delegate methods we don't find directly to the target.
|
|
69
|
+
#
|
|
70
|
+
# @private
|
|
71
|
+
# @since 0.2.0
|
|
72
|
+
def method_missing(method, *args, &block)
|
|
73
|
+
if target.respond_to?(method)
|
|
74
|
+
target.send(method, *args, &block)
|
|
75
|
+
else
|
|
76
|
+
super
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
else
|
|
80
|
+
# Delegate methods we don't find directly to the target.
|
|
81
|
+
#
|
|
82
|
+
# @private
|
|
83
|
+
# @since 0.2.0
|
|
84
|
+
def method_missing(method, *args, **kwargs, &block)
|
|
85
|
+
if target.respond_to?(method)
|
|
86
|
+
target.send(method, *args, **kwargs, &block)
|
|
87
|
+
else
|
|
88
|
+
super
|
|
89
|
+
end
|
|
76
90
|
end
|
|
77
91
|
end
|
|
78
92
|
|
|
93
|
+
# @private
|
|
94
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
95
|
+
target.respond_to?(method_name, include_private) || super
|
|
96
|
+
end
|
|
97
|
+
|
|
79
98
|
# @private
|
|
80
99
|
def nil?
|
|
81
100
|
target.nil?
|
|
@@ -187,10 +187,10 @@ module Dynamoid
|
|
|
187
187
|
def first(*args)
|
|
188
188
|
n = args.first || 1
|
|
189
189
|
|
|
190
|
-
return scan_limit(n).to_a.first(*args) if @query.blank?
|
|
190
|
+
return self.dup.scan_limit(n).to_a.first(*args) if @query.blank?
|
|
191
191
|
return super if @key_fields_detector.non_key_present?
|
|
192
192
|
|
|
193
|
-
record_limit(n).to_a.first(*args)
|
|
193
|
+
self.dup.record_limit(n).to_a.first(*args)
|
|
194
194
|
end
|
|
195
195
|
|
|
196
196
|
# Returns the last item matching the criteria.
|
|
@@ -486,15 +486,17 @@ module Dynamoid
|
|
|
486
486
|
# @return [Array]
|
|
487
487
|
def pluck(*args)
|
|
488
488
|
fields = args.map(&:to_sym)
|
|
489
|
-
|
|
489
|
+
|
|
490
|
+
scope = self.dup
|
|
491
|
+
scope.project(*fields)
|
|
490
492
|
|
|
491
493
|
if fields.many?
|
|
492
|
-
items.map do |item|
|
|
494
|
+
scope.items.map do |item|
|
|
493
495
|
fields.map { |key| Undumping.undump_field(item[key], source.attributes[key]) }
|
|
494
496
|
end.to_a
|
|
495
497
|
else
|
|
496
498
|
key = fields.first
|
|
497
|
-
items.map { |item| Undumping.undump_field(item[key], source.attributes[key]) }.to_a
|
|
499
|
+
scope.items.map { |item| Undumping.undump_field(item[key], source.attributes[key]) }.to_a
|
|
498
500
|
end
|
|
499
501
|
end
|
|
500
502
|
|
|
@@ -513,6 +515,7 @@ module Dynamoid
|
|
|
513
515
|
def items
|
|
514
516
|
raw_pages.lazy.flat_map { |items, _| items }
|
|
515
517
|
end
|
|
518
|
+
protected :items
|
|
516
519
|
|
|
517
520
|
# Arrays of records, sized based on the actual pages produced by DynamoDB
|
|
518
521
|
#
|
|
@@ -568,7 +571,7 @@ module Dynamoid
|
|
|
568
571
|
|
|
569
572
|
def issue_scan_warning
|
|
570
573
|
Dynamoid.logger.warn 'Queries without an index are forced to use scan and are generally much slower than indexed queries!'
|
|
571
|
-
Dynamoid.logger.warn "You can index this query by adding index declaration to #{source.to_s.
|
|
574
|
+
Dynamoid.logger.warn "You can index this query by adding index declaration to #{source.to_s.underscore}.rb:"
|
|
572
575
|
Dynamoid.logger.warn "* global_secondary_index hash_key: 'some-name', range_key: 'some-another-name'"
|
|
573
576
|
Dynamoid.logger.warn "* local_secondary_index range_key: 'some-name'"
|
|
574
577
|
Dynamoid.logger.warn "Not indexed attributes: #{query.keys.sort.collect { |name| ":#{name}" }.join(', ')}"
|
|
@@ -770,6 +773,7 @@ module Dynamoid
|
|
|
770
773
|
|
|
771
774
|
def scan_opts
|
|
772
775
|
opts = {}
|
|
776
|
+
opts[:index_name] = @key_fields_detector.index_name if @key_fields_detector.index_name
|
|
773
777
|
opts[:record_limit] = @record_limit if @record_limit
|
|
774
778
|
opts[:scan_limit] = @scan_limit if @scan_limit
|
|
775
779
|
opts[:batch_size] = @batch_size if @batch_size
|
data/lib/dynamoid/criteria.rb
CHANGED
|
@@ -9,12 +9,15 @@ module Dynamoid
|
|
|
9
9
|
|
|
10
10
|
# @private
|
|
11
11
|
module ClassMethods
|
|
12
|
-
%i[
|
|
12
|
+
%i[
|
|
13
|
+
where consistent all first last delete_all destroy_all each record_limit
|
|
14
|
+
scan_limit batch start scan_index_forward find_by_pages project pluck
|
|
15
|
+
].each do |name|
|
|
13
16
|
# Return a criteria chain in response to a method that will begin or end a chain. For more information,
|
|
14
17
|
# see Dynamoid::Criteria::Chain.
|
|
15
18
|
#
|
|
16
19
|
# @since 0.2.0
|
|
17
|
-
define_method(
|
|
20
|
+
define_method(name) do |*args, &blk|
|
|
18
21
|
# Don't use keywork arguments delegating (with **kw). It works in
|
|
19
22
|
# different way in different Ruby versions: <= 2.6, 2.7, 3.0 and in some
|
|
20
23
|
# future 3.x versions. Providing that there are no downstream methods
|
|
@@ -23,11 +26,7 @@ module Dynamoid
|
|
|
23
26
|
# https://eregon.me/blog/2019/11/10/the-delegation-challenge-of-ruby27.html
|
|
24
27
|
|
|
25
28
|
chain = Dynamoid::Criteria::Chain.new(self)
|
|
26
|
-
|
|
27
|
-
chain.send(meth, *args, &blk)
|
|
28
|
-
else
|
|
29
|
-
chain.send(meth, &blk)
|
|
30
|
-
end
|
|
29
|
+
chain.send(name, *args, &blk)
|
|
31
30
|
end
|
|
32
31
|
end
|
|
33
32
|
end
|
data/lib/dynamoid/dirty.rb
CHANGED
|
@@ -25,17 +25,15 @@ module Dynamoid
|
|
|
25
25
|
# @private
|
|
26
26
|
module ClassMethods
|
|
27
27
|
def update_fields(*)
|
|
28
|
-
|
|
29
|
-
model.send(:clear_changes_information)
|
|
28
|
+
super.tap do |model|
|
|
29
|
+
model.send(:clear_changes_information) if model
|
|
30
30
|
end
|
|
31
|
-
model
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
def upsert(*)
|
|
35
|
-
|
|
36
|
-
model.send(:clear_changes_information)
|
|
34
|
+
super.tap do |model|
|
|
35
|
+
model.send(:clear_changes_information) if model
|
|
37
36
|
end
|
|
38
|
-
model
|
|
39
37
|
end
|
|
40
38
|
|
|
41
39
|
def from_database(*)
|
|
@@ -47,10 +45,9 @@ module Dynamoid
|
|
|
47
45
|
|
|
48
46
|
# @private
|
|
49
47
|
def save(*)
|
|
50
|
-
|
|
51
|
-
changes_applied
|
|
48
|
+
super.tap do |status|
|
|
49
|
+
changes_applied if status
|
|
52
50
|
end
|
|
53
|
-
status
|
|
54
51
|
end
|
|
55
52
|
|
|
56
53
|
# @private
|
data/lib/dynamoid/document.rb
CHANGED
|
@@ -17,12 +17,6 @@ module Dynamoid
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
module ClassMethods
|
|
20
|
-
# @private
|
|
21
|
-
def table(options = {})
|
|
22
|
-
self.options = options
|
|
23
|
-
super if defined? super
|
|
24
|
-
end
|
|
25
|
-
|
|
26
20
|
def attr_readonly(*read_only_attributes)
|
|
27
21
|
self.read_only_attributes.concat read_only_attributes.map(&:to_s)
|
|
28
22
|
end
|
|
@@ -77,10 +71,10 @@ module Dynamoid
|
|
|
77
71
|
|
|
78
72
|
# Return the count of items for this class.
|
|
79
73
|
#
|
|
80
|
-
# It returns
|
|
81
|
-
# updates it
|
|
74
|
+
# It returns approximate value based on DynamoDB statistic. DynamoDB
|
|
75
|
+
# updates it periodically so the value can be no accurate.
|
|
82
76
|
#
|
|
83
|
-
# It's a
|
|
77
|
+
# It's a reletively cheap operation and doesn't read all the items in a
|
|
84
78
|
# table. It makes just one HTTP request to DynamoDB.
|
|
85
79
|
#
|
|
86
80
|
# @return [Integer] items count in a table
|
|
@@ -220,7 +214,7 @@ module Dynamoid
|
|
|
220
214
|
# Check equality of two models.
|
|
221
215
|
#
|
|
222
216
|
# A model is equal to another model only if their primary keys (hash key
|
|
223
|
-
# and
|
|
217
|
+
# and optionally range key) are equal.
|
|
224
218
|
#
|
|
225
219
|
# @return [true|false]
|
|
226
220
|
# @since 0.2.0
|