dynamoid 3.8.0 → 3.10.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 +54 -3
- data/README.md +111 -60
- data/SECURITY.md +17 -0
- data/dynamoid.gemspec +65 -0
- data/lib/dynamoid/adapter.rb +20 -13
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +2 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/execute_statement.rb +62 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/filter_expression_convertor.rb +78 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +28 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb +3 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/projection_expression_convertor.rb +38 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +46 -61
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +33 -27
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +116 -70
- data/lib/dynamoid/associations/belongs_to.rb +6 -6
- data/lib/dynamoid/associations.rb +1 -1
- data/lib/dynamoid/components.rb +2 -3
- data/lib/dynamoid/config/options.rb +12 -12
- data/lib/dynamoid/config.rb +1 -0
- data/lib/dynamoid/criteria/chain.rb +101 -138
- data/lib/dynamoid/criteria/key_fields_detector.rb +6 -7
- data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +2 -2
- data/lib/dynamoid/criteria/where_conditions.rb +29 -0
- data/lib/dynamoid/dirty.rb +57 -57
- data/lib/dynamoid/document.rb +39 -3
- data/lib/dynamoid/dumping.rb +2 -2
- data/lib/dynamoid/errors.rb +2 -0
- data/lib/dynamoid/fields/declare.rb +6 -6
- data/lib/dynamoid/fields.rb +9 -27
- data/lib/dynamoid/finders.rb +26 -30
- data/lib/dynamoid/indexes.rb +7 -10
- data/lib/dynamoid/loadable.rb +2 -2
- data/lib/dynamoid/log/formatter.rb +19 -4
- data/lib/dynamoid/persistence/import.rb +4 -1
- data/lib/dynamoid/persistence/inc.rb +66 -0
- data/lib/dynamoid/persistence/save.rb +55 -12
- data/lib/dynamoid/persistence/update_fields.rb +2 -2
- data/lib/dynamoid/persistence/update_validations.rb +2 -2
- data/lib/dynamoid/persistence.rb +128 -48
- data/lib/dynamoid/type_casting.rb +15 -14
- data/lib/dynamoid/undumping.rb +1 -1
- data/lib/dynamoid/version.rb +1 -1
- metadata +27 -49
- data/lib/dynamoid/criteria/ignored_conditions_detector.rb +0 -41
- data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2cc1feec6853b756fff467fa687cf2eaa3a2dc5f0a88dac948e5a92a4aa1073
|
4
|
+
data.tar.gz: 7165e081fa9979c45e7402265138e62cb6ee3c1f6f594ab6949c1f984621bc89
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e18700ff74b9466e1ec166706446a1dca5248c6b8c33365e469c74e9a67b92f4afc9cacb0a06c2698d5165f4b68a93cdd214b83f5e3b749b92bcaff06fbc7628
|
7
|
+
data.tar.gz: 9b18fb2522f90eb3cf9b0b6014ca24beb08bcdebf403c57487222c46ec71d306f4e85e686aa89607c77ad7d994359a934537e8d22202b145bd478c0862bf497b
|
data/CHANGELOG.md
CHANGED
@@ -5,15 +5,66 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
|
-
### Fixed
|
9
8
|
|
9
|
+
### Fixed
|
10
10
|
### Added
|
11
|
+
### Changed
|
12
|
+
### Removed
|
11
13
|
|
14
|
+
## 3.10.0
|
15
|
+
### Fixed
|
16
|
+
* [#681](https://github.com/Dynamoid/dynamoid/pull/681) Fixed saving persisted model and deleting attributes with `nil` value if `config.store_attribute_with_nil_value` is `false`
|
17
|
+
* [#716](https://github.com/Dynamoid/dynamoid/pull/716), [#691](https://github.com/Dynamoid/dynamoid/pull/691), [#687](https://github.com/Dynamoid/dynamoid/pull/687), [#660](https://github.com/Dynamoid/dynamoid/pull/660) Numerous fixes in README.md and RDoc documentation (@ndjndj, @kiharito, @dunkOnIT)
|
18
|
+
### Added
|
19
|
+
* [#656](https://github.com/Dynamoid/dynamoid/pull/656) Added a `create_table_on_save` configuration flag to create table on save (@imaximix)
|
20
|
+
* [#697](https://github.com/Dynamoid/dynamoid/pull/697) Ensure Ruby 3.3 and Rails 7.1 versions are supported and added them on CI
|
12
21
|
### Changed
|
22
|
+
* [#655](https://github.com/Dynamoid/dynamoid/pull/655) Support multiple `where` in the same chain with multiple conditions for the same field
|
13
23
|
|
14
|
-
|
24
|
+
## 3.9.0 / 2023-04-13
|
25
|
+
### Fixed
|
26
|
+
* [#610](https://github.com/Dynamoid/dynamoid/pull/610) Specs in JRuby; Support for JRuby 9.4.0.0 (@pboling)
|
27
|
+
* [#624](https://github.com/Dynamoid/dynamoid/pull/624) Fixed `#increment!`/`#decrement!` methods and made them compatible with Rails counterparts
|
28
|
+
* [#626](https://github.com/Dynamoid/dynamoid/pull/626) Fixed saving empty Set and String and replacing with `nil` in `#update`, `#update!`, `.update_fields`, and `.upsert` methods
|
29
|
+
* [#628](https://github.com/Dynamoid/dynamoid/pull/628) Fixed `.import` method to mark persisted model attributes as not changed/not dirty
|
30
|
+
* [#632](https://github.com/Dynamoid/dynamoid/pull/632) Fixed `#save` called with `touch: false` option to set `updated_at` attribute even for a new record (to comply with Rails)
|
31
|
+
* [#634](https://github.com/Dynamoid/dynamoid/pull/634) Fixed model callbacks:
|
32
|
+
* changed order of `save` and `create`/`update` callbacks - `save` callbacks are outer for the `create`/`update` ones
|
33
|
+
* removed `before_initialize` and `around_initialize` callbacks - there should be only `after_initialize` one
|
34
|
+
* [#634](https://github.com/Dynamoid/dynamoid/pull/634) Fixed `#touch` method compatibility with a Rails counterpart:
|
35
|
+
* don't save other modified attributes - only timestamps
|
36
|
+
* don't perform validation and don't call `save`/`create`/`update` callbacks
|
37
|
+
* accept a list of attribute names, but not one name
|
38
|
+
* accept a `:time` option
|
39
|
+
### Added
|
40
|
+
* [#611](https://github.com/Dynamoid/dynamoid/pull/611) Add `rubocop-md` (@pboling)
|
41
|
+
* [#612](https://github.com/Dynamoid/dynamoid/pull/612) Add `rubocop-rspec` (@pboling)
|
42
|
+
* [#613](https://github.com/Dynamoid/dynamoid/pull/613) Add `rubocop-performance` and `rubocop-rake` (@pboling)
|
43
|
+
* Added `funding_uri` set to open collective: https://opencollective.com/dynamoid
|
44
|
+
* Added `required_ruby_version` as `>= 2.3.0` (which was already the minimum supported version of Ruby)
|
45
|
+
* [#616](https://github.com/Dynamoid/dynamoid/pull/616) Upgrade `simplecov` (& remove `coveralls`) (@pboling)
|
46
|
+
* Setup GitHub actions for Code Coverage
|
47
|
+
* Setup GitHub actions for RuboCop linting
|
48
|
+
* Automate coverage feedback on Pull Requests via GitHub Actions and CodeCov
|
49
|
+
* [#618](https://github.com/Dynamoid/dynamoid/pull/618) Upgrade README Badges (@pboling)
|
50
|
+
* [#624](https://github.com/Dynamoid/dynamoid/pull/624) Added `:touch` option for `.inc` method to be more compatible with the Rails counterpart method `.update_counters`
|
51
|
+
* [#627](https://github.com/Dynamoid/dynamoid/pull/627) Made the following methods in the Dirty API public (to comply with Rails):
|
52
|
+
* `clear_changes_information`
|
53
|
+
* `changes_applied`
|
54
|
+
* `clear_attribute_changes`
|
55
|
+
* [#630](https://github.com/Dynamoid/dynamoid/pull/630) Added `Dynamoid::Adapter#execute` method to run PartiQL queries
|
56
|
+
* [#634](https://github.com/Dynamoid/dynamoid/pull/634) Added `after_touch` callback and run it in the following methods:
|
57
|
+
* `#touch`
|
58
|
+
* `#increment!`
|
59
|
+
* `#decrement!`
|
60
|
+
* [#642](https://github.com/Dynamoid/dynamoid/pull/642) Run specs on CI against Ruby 3.2
|
61
|
+
* [#645](https://github.com/Dynamoid/dynamoid/pull/645) Added `after_find` callback
|
62
|
+
### Changed
|
63
|
+
* [#610](https://github.com/Dynamoid/dynamoid/pull/610) Switch to [`rubocop-lts`](https://rubocop-lts.gitlab.io/) (@pboling)
|
64
|
+
* [#633](https://github.com/Dynamoid/dynamoid/pull/633) Change `#inspect` method to return only attributes
|
65
|
+
* [#623](https://github.com/Dynamoid/dynamoid/pull/623) Optimized performance of persisting to send only changed attributes in a request to DynamoDB
|
15
66
|
|
16
|
-
## 3.8.0
|
67
|
+
## 3.8.0 / 2022-11-09
|
17
68
|
### Fixed
|
18
69
|
* [#525](https://github.com/Dynamoid/dynamoid/pull/525) Don't mark an attribute as changed if new assigned value equals the old one (@a5-stable)
|
19
70
|
* Minor changes in the documentation:
|
data/README.md
CHANGED
@@ -1,21 +1,18 @@
|
|
1
1
|
# Dynamoid
|
2
2
|
|
3
|
-
[![
|
4
|
-
[![
|
5
|
-
[![
|
6
|
-
[![
|
7
|
-
[![
|
8
|
-
[![
|
9
|
-
![
|
3
|
+
[![Gem Version][⛳️version-img]][⛳️gem]
|
4
|
+
[![Supported Build Status][🏘sup-wf-img]][🏘sup-wf]
|
5
|
+
[![Maintainability][⛳cclim-maint-img♻️]][⛳cclim-maint]
|
6
|
+
[![Coveralls][🏘coveralls-img]][🏘coveralls]
|
7
|
+
[![CodeCov][🖇codecov-img♻️]][🖇codecov]
|
8
|
+
[![Helpers][🖇triage-help-img]][🖇triage-help]
|
9
|
+
[![Contributors][🖐contributors-img]][🖐contributors]
|
10
|
+
[![RubyDoc.info][🚎yard-img]][🚎yard]
|
11
|
+
[![License][🖇src-license-img]][🖇src-license]
|
10
12
|
[![GitMoji][🖐gitmoji-img]][🖐gitmoji]
|
11
|
-
[![SemVer 2.0.0][🧮semver-img]][semver]
|
13
|
+
[![SemVer 2.0.0][🧮semver-img]][🧮semver]
|
12
14
|
[![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
|
15
|
+
[![Sponsor Project][🖇sponsor-img]][🖇sponsor]
|
19
16
|
|
20
17
|
Dynamoid is an ORM for Amazon's DynamoDB for Ruby applications. It
|
21
18
|
provides similar functionality to ActiveRecord and improves on Amazon's
|
@@ -70,10 +67,10 @@ For example, to configure AWS access:
|
|
70
67
|
Create `config/initializers/aws.rb` as follows:
|
71
68
|
|
72
69
|
```ruby
|
73
|
-
Aws.config.update(
|
70
|
+
Aws.config.update(
|
74
71
|
region: 'us-west-2',
|
75
72
|
credentials: Aws::Credentials.new('REPLACE_WITH_ACCESS_KEY_ID', 'REPLACE_WITH_SECRET_ACCESS_KEY'),
|
76
|
-
|
73
|
+
)
|
77
74
|
```
|
78
75
|
|
79
76
|
Alternatively, if you don't want Aws connection settings to be
|
@@ -97,16 +94,16 @@ elsewhere in your project, etc.), you may do so:
|
|
97
94
|
require 'dynamoid'
|
98
95
|
|
99
96
|
credentials = Aws::AssumeRoleCredentials.new(
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
97
|
+
region: region,
|
98
|
+
access_key_id: key,
|
99
|
+
secret_access_key: secret,
|
100
|
+
role_arn: role_arn,
|
101
|
+
role_session_name: 'our-session'
|
102
|
+
)
|
106
103
|
|
107
104
|
Dynamoid.configure do |config|
|
108
105
|
config.region = 'us-west-2',
|
109
|
-
|
106
|
+
config.credentials = credentials
|
110
107
|
end
|
111
108
|
```
|
112
109
|
|
@@ -135,8 +132,8 @@ end
|
|
135
132
|
Dynamoid supports Ruby >= 2.3 and Rails >= 4.2.
|
136
133
|
|
137
134
|
Its compatibility is tested against following Ruby versions: 2.3, 2.4,
|
138
|
-
2.5, 2.6, 2.7 and 3.
|
139
|
-
5.2, 6.0, 6.1 and 7.
|
135
|
+
2.5, 2.6, 2.7, 3.0, 3.1, 3.2 and 3.3, JRuby 9.4.x and against Rails versions: 4.2, 5.0, 5.1,
|
136
|
+
5.2, 6.0, 6.1, 7.0 and 7.1.
|
140
137
|
|
141
138
|
## Setup
|
142
139
|
|
@@ -360,7 +357,6 @@ class User
|
|
360
357
|
field :number, :number
|
361
358
|
field :joined_at, :datetime
|
362
359
|
field :hash, :serialized
|
363
|
-
|
364
360
|
end
|
365
361
|
```
|
366
362
|
|
@@ -406,7 +402,7 @@ class Money
|
|
406
402
|
'serialized representation as a string'
|
407
403
|
end
|
408
404
|
|
409
|
-
def self.dynamoid_load(
|
405
|
+
def self.dynamoid_load(_serialized_str)
|
410
406
|
# parse serialized representation and return a Money instance
|
411
407
|
Money.new(1.23)
|
412
408
|
end
|
@@ -431,7 +427,7 @@ serializing. Example:
|
|
431
427
|
class Money; end
|
432
428
|
|
433
429
|
class MoneyAdapter
|
434
|
-
def self.dynamoid_load(
|
430
|
+
def self.dynamoid_load(_money_serialized_str)
|
435
431
|
Money.new(1.23)
|
436
432
|
end
|
437
433
|
|
@@ -550,9 +546,18 @@ model.save(validate: false)
|
|
550
546
|
|
551
547
|
### Callbacks
|
552
548
|
|
553
|
-
Dynamoid also employs ActiveModel callbacks. Right now
|
554
|
-
|
555
|
-
|
549
|
+
Dynamoid also employs ActiveModel callbacks. Right now the following
|
550
|
+
callbacks are supported:
|
551
|
+
- `save` (before, after, around)
|
552
|
+
- `create` (before, after, around)
|
553
|
+
- `update` (before, after, around)
|
554
|
+
- `validation` (before, after)
|
555
|
+
- `destroy` (before, after, around)
|
556
|
+
- `after_touch`
|
557
|
+
- `after_initialize`
|
558
|
+
- `after_find`
|
559
|
+
|
560
|
+
Example:
|
556
561
|
|
557
562
|
```ruby
|
558
563
|
class User
|
@@ -684,14 +689,14 @@ address.save
|
|
684
689
|
To create multiple documents at once:
|
685
690
|
|
686
691
|
```ruby
|
687
|
-
User.create([{name: 'Josh'}, {name: 'Nick'}])
|
692
|
+
User.create([{ name: 'Josh' }, { name: 'Nick' }])
|
688
693
|
```
|
689
694
|
|
690
695
|
There is an efficient and low-level way to create multiple documents
|
691
696
|
(without validation and callbacks running):
|
692
697
|
|
693
698
|
```ruby
|
694
|
-
users = User.import([{name: 'Josh'}, {name: 'Nick'}])
|
699
|
+
users = User.import([{ name: 'Josh' }, { name: 'Nick' }])
|
695
700
|
```
|
696
701
|
|
697
702
|
### Querying
|
@@ -718,18 +723,6 @@ join, but instead finds all the user's addresses and naively filters
|
|
718
723
|
them in Ruby. For large associations this is a performance hit compared
|
719
724
|
to relational database engines.
|
720
725
|
|
721
|
-
**WARNING:** There is a limitation of conditions passed to `where`
|
722
|
-
method. Only one condition for some particular field could be specified.
|
723
|
-
The last one only will be applied and others will be ignored. E.g. in
|
724
|
-
examples:
|
725
|
-
|
726
|
-
```ruby
|
727
|
-
User.where('age.gt': 10, 'age.lt': 20)
|
728
|
-
User.where(name: 'Mike').where('name.begins_with': 'Ed')
|
729
|
-
```
|
730
|
-
|
731
|
-
the first one will be ignored and the last one will be used.
|
732
|
-
|
733
726
|
**Warning:** There is a caveat with filtering documents by `nil` value
|
734
727
|
attribute. By default Dynamoid ignores attributes with `nil` value and
|
735
728
|
doesn't store them in a DynamoDB document. This behavior could be
|
@@ -747,7 +740,7 @@ If Dynamoid keeps `nil` value attributes `eq`/`ne` operators should be
|
|
747
740
|
used instead:
|
748
741
|
|
749
742
|
```ruby
|
750
|
-
Address.where(
|
743
|
+
Address.where(postcode: nil)
|
751
744
|
Address.where('postcode.ne': nil)
|
752
745
|
```
|
753
746
|
|
@@ -796,8 +789,8 @@ for requesting documents in batches:
|
|
796
789
|
|
797
790
|
```ruby
|
798
791
|
# Do some maintenance on the entire table without flooding DynamoDB
|
799
|
-
Address.batch(100).each { |
|
800
|
-
Address.record_limit(10_000).batch(100).each {
|
792
|
+
Address.batch(100).each { |addr| addr.do_some_work && sleep(0.01) }
|
793
|
+
Address.record_limit(10_000).batch(100).each { |addr| addr.do_some_work && sleep(0.01) } # Batch specified as part of a chain
|
801
794
|
```
|
802
795
|
|
803
796
|
The implication of batches is that the underlying requests are done in
|
@@ -849,13 +842,13 @@ operators are available: `gt`, `lt`, `gte`, `lte`, `begins_with`,
|
|
849
842
|
`between` as well as equality:
|
850
843
|
|
851
844
|
```ruby
|
852
|
-
Address.where(latitude:
|
853
|
-
Address.where('latitude.gt':
|
854
|
-
Address.where('latitude.lt':
|
855
|
-
Address.where('latitude.gte':
|
856
|
-
Address.where('latitude.lte':
|
845
|
+
Address.where(latitude: 10_212)
|
846
|
+
Address.where('latitude.gt': 10_212)
|
847
|
+
Address.where('latitude.lt': 10_212)
|
848
|
+
Address.where('latitude.gte': 10_212)
|
849
|
+
Address.where('latitude.lte': 10_212)
|
857
850
|
Address.where('city.begins_with': 'Lon')
|
858
|
-
Address.where('latitude.between': [
|
851
|
+
Address.where('latitude.between': [10_212, 20_000])
|
859
852
|
```
|
860
853
|
|
861
854
|
You are able to filter results on the DynamoDB side and specify
|
@@ -863,7 +856,7 @@ conditions for non-key fields. Following additional operators are
|
|
863
856
|
available: `in`, `contains`, `not_contains`, `null`, `not_null`:
|
864
857
|
|
865
858
|
```ruby
|
866
|
-
Address.where('city.in': [
|
859
|
+
Address.where('city.in': %w[London Edenburg Birmingham])
|
867
860
|
Address.where('city.contains': ['on'])
|
868
861
|
Address.where('city.not_contains': ['ing'])
|
869
862
|
Address.where('postcode.null': false)
|
@@ -921,8 +914,8 @@ If you have a range index, Dynamoid provides a number of additional
|
|
921
914
|
other convenience methods to make your life a little easier:
|
922
915
|
|
923
916
|
```ruby
|
924
|
-
User.where(
|
925
|
-
User.where(
|
917
|
+
User.where('created_at.gt': DateTime.now - 1.day).all
|
918
|
+
User.where('created_at.lt': DateTime.now - 1.day).all
|
926
919
|
```
|
927
920
|
|
928
921
|
It also supports `gte` and `lte`. Turning those into symbols and
|
@@ -965,6 +958,14 @@ Address.upsert(id, city: 'Chicago')
|
|
965
958
|
Address.upsert(id, { city: 'Chicago' }, if: { deliverable: true })
|
966
959
|
```
|
967
960
|
|
961
|
+
By default, `#upsert` will update all attributes of the document if it already exists.
|
962
|
+
To idempotently create-but-not-update a record, apply the `unless_exists` condition
|
963
|
+
to its keys when you upsert.
|
964
|
+
|
965
|
+
```ruby
|
966
|
+
Address.upsert(id, { city: 'Chicago' }, { unless_exists: [:id] })
|
967
|
+
```
|
968
|
+
|
968
969
|
### Deleting
|
969
970
|
|
970
971
|
In order to delete some items `delete_all` method should be used. Any
|
@@ -1046,6 +1047,21 @@ resolving the fields with a second query against the table since a query
|
|
1046
1047
|
against GSI then a query on base table is still likely faster than scan
|
1047
1048
|
on the base table*
|
1048
1049
|
|
1050
|
+
### PartiQL
|
1051
|
+
|
1052
|
+
To run PartiQL statements `Dynamoid.adapter.execute` method should be
|
1053
|
+
used:
|
1054
|
+
|
1055
|
+
```ruby
|
1056
|
+
Dynamoid.adapter.execute("UPDATE users SET name = 'Mike' WHERE id = '1'")
|
1057
|
+
```
|
1058
|
+
|
1059
|
+
Parameters are also supported:
|
1060
|
+
|
1061
|
+
```ruby
|
1062
|
+
Dynamoid.adapter.execute('SELECT * FROM users WHERE id = ?', ['1'])
|
1063
|
+
```
|
1064
|
+
|
1049
1065
|
## Configuration
|
1050
1066
|
|
1051
1067
|
Listed below are all configuration options.
|
@@ -1116,12 +1132,12 @@ Listed below are all configuration options.
|
|
1116
1132
|
in ISO 8601 string format. Default is `false`
|
1117
1133
|
* `store_boolean_as_native` - if `true` Dynamoid stores boolean fields
|
1118
1134
|
as native DynamoDB boolean values. Otherwise boolean fields are stored
|
1119
|
-
as string values `'t'` and `'f'`. Default is true
|
1135
|
+
as string values `'t'` and `'f'`. Default is `true`
|
1120
1136
|
* `backoff` - is a hash: key is a backoff strategy (symbol), value is
|
1121
1137
|
parameters for the strategy. Is used in batch operations. Default id
|
1122
1138
|
`nil`
|
1123
1139
|
* `backoff_strategies`: is a hash and contains all available strategies.
|
1124
|
-
Default is { constant: ..., exponential: ...}
|
1140
|
+
Default is `{ constant: ..., exponential: ...}`
|
1125
1141
|
* `log_formatter`: overrides default AWS SDK formatter. There are
|
1126
1142
|
several canned formatters: `Aws::Log::Formatter.default`,
|
1127
1143
|
`Aws::Log::Formatter.colored` and `Aws::Log::Formatter.short`. Please
|
@@ -1139,6 +1155,9 @@ Listed below are all configuration options.
|
|
1139
1155
|
* `http_read_timeout`:The number of seconds to wait for HTTP response
|
1140
1156
|
data. Default option value is `nil`. If not specified effected value
|
1141
1157
|
is `60`
|
1158
|
+
* `create_table_on_save`: if `true` then Dynamoid creates a
|
1159
|
+
corresponding table in DynamoDB at model persisting if the table
|
1160
|
+
doesn't exist yet. Default is `true`
|
1142
1161
|
|
1143
1162
|
|
1144
1163
|
## Concurrency
|
@@ -1277,11 +1296,18 @@ RSpec.configure do |config|
|
|
1277
1296
|
end
|
1278
1297
|
```
|
1279
1298
|
|
1299
|
+
In addition, the first test for each model may fail if the relevant models are not included in `included_models`. This can be fixed by adding this line before the `DynamoidReset` module:
|
1300
|
+
```ruby
|
1301
|
+
Dir[File.join(Dynamoid::Config.models_dir, '**/*.rb')].sort.each { |file| require file }
|
1302
|
+
```
|
1303
|
+
Note that this will require _all_ models in your models folder - you can also explicitly require only certain models if you would prefer to.
|
1304
|
+
|
1280
1305
|
In Rails, you may also want to ensure you do not delete non-test data
|
1281
1306
|
accidentally by adding the following to your test environment setup:
|
1282
1307
|
|
1283
1308
|
```ruby
|
1284
1309
|
raise "Tests should be run in 'test' environment only" if Rails.env != 'test'
|
1310
|
+
|
1285
1311
|
Dynamoid.configure do |config|
|
1286
1312
|
config.namespace = "#{Rails.application.railtie_name}_#{Rails.env}"
|
1287
1313
|
end
|
@@ -1406,4 +1432,29 @@ See [LICENSE][license] for the official [Copyright Notice][copyright-notice-expl
|
|
1406
1432
|
|
1407
1433
|
[security]: https://github.com/Dynamoid/dynamoid/blob/master/SECURITY.md
|
1408
1434
|
|
1409
|
-
[
|
1435
|
+
[⛳️gem]: https://rubygems.org/gems/dynamoid
|
1436
|
+
[⛳️version-img]: http://img.shields.io/gem/v/dynamoid.svg
|
1437
|
+
[⛳cclim-maint]: https://codeclimate.com/github/Dynamoid/dynamoid/maintainability
|
1438
|
+
[⛳cclim-maint-img♻️]: https://api.codeclimate.com/v1/badges/27fd8b6b7ff338fa4914/maintainability
|
1439
|
+
[🏘coveralls]: https://coveralls.io/github/Dynamoid/dynamoid?branch=master
|
1440
|
+
[🏘coveralls-img]: https://coveralls.io/repos/github/Dynamoid/dynamoid/badge.svg?branch=master
|
1441
|
+
[🖇codecov]: https://codecov.io/gh/Dynamoid/dynamoid
|
1442
|
+
[🖇codecov-img♻️]: https://codecov.io/gh/Dynamoid/dynamoid/branch/master/graph/badge.svg?token=84WeeoxaN9
|
1443
|
+
[🖇src-license]: https://github.com/Dynamoid/dynamoid/blob/master/LICENSE.txt
|
1444
|
+
[🖇src-license-img]: https://img.shields.io/badge/License-MIT-green.svg
|
1445
|
+
[🖐gitmoji]: https://gitmoji.dev
|
1446
|
+
[🖐gitmoji-img]: https://img.shields.io/badge/gitmoji-3.9.0-FFDD67.svg?style=flat
|
1447
|
+
[🚎yard]: https://www.rubydoc.info/gems/dynamoid
|
1448
|
+
[🚎yard-img]: https://img.shields.io/badge/yard-docs-blue.svg?style=flat
|
1449
|
+
[🧮semver]: http://semver.org/
|
1450
|
+
[🧮semver-img]: https://img.shields.io/badge/semver-2.0.0-FFDD67.svg?style=flat
|
1451
|
+
[🖐contributors]: https://github.com/Dynamoid/dynamoid/graphs/contributors
|
1452
|
+
[🖐contributors-img]: https://img.shields.io/github/contributors-anon/Dynamoid/dynamoid
|
1453
|
+
[📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
|
1454
|
+
[📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-FFDD67.svg?style=flat
|
1455
|
+
[🖇sponsor-img]: https://img.shields.io/opencollective/all/dynamoid
|
1456
|
+
[🖇sponsor]: https://opencollective.com/dynamoid
|
1457
|
+
[🖇triage-help]: https://www.codetriage.com/dynamoid/dynamoid
|
1458
|
+
[🖇triage-help-img]: https://www.codetriage.com/dynamoid/dynamoid/badges/users.svg
|
1459
|
+
[🏘sup-wf]: https://github.com/Dynamoid/dynamoid/actions/workflows/ci.yml?query=branch%3Amaster
|
1460
|
+
[🏘sup-wf-img]: https://github.com/Dynamoid/dynamoid/actions/workflows/ci.yml/badge.svg?branch=master
|
data/SECURITY.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Security Policy
|
2
|
+
|
3
|
+
## Supported Versions
|
4
|
+
|
5
|
+
| Version | Supported |
|
6
|
+
|---------|-----------|
|
7
|
+
| 3.7.x | ✅ |
|
8
|
+
| <= 3.6 | ❌ |
|
9
|
+
| 2.x | ❌ |
|
10
|
+
| 1.x | ❌ |
|
11
|
+
| 0.x | ❌ |
|
12
|
+
|
13
|
+
## Reporting a Vulnerability
|
14
|
+
|
15
|
+
Peter Boling is responsible for the security maintenance of this gem. Please find a way
|
16
|
+
to [contact him directly](https://railsbling.com/contact) to report the issue. Include as much relevant information as
|
17
|
+
possible.
|
data/dynamoid.gemspec
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'dynamoid/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'dynamoid'
|
9
|
+
spec.version = Dynamoid::VERSION
|
10
|
+
|
11
|
+
# Keep in sync with README
|
12
|
+
spec.authors = [
|
13
|
+
'Josh Symonds',
|
14
|
+
'Logan Bowers',
|
15
|
+
'Craig Heneveld',
|
16
|
+
'Anatha Kumaran',
|
17
|
+
'Jason Dew',
|
18
|
+
'Luis Arias',
|
19
|
+
'Stefan Neculai',
|
20
|
+
'Philip White',
|
21
|
+
'Peeyush Kumar',
|
22
|
+
'Sumanth Ravipati',
|
23
|
+
'Pascal Corpet',
|
24
|
+
'Brian Glusman',
|
25
|
+
'Peter Boling',
|
26
|
+
'Andrew Konchin'
|
27
|
+
]
|
28
|
+
spec.email = ['andry.konchin@gmail.com', 'peter.boling@gmail.com', 'brian@stellaservice.com']
|
29
|
+
|
30
|
+
spec.description = "Dynamoid is an ORM for Amazon's DynamoDB that supports offline development, associations, querying, and everything else you'd expect from an ActiveRecord-style replacement."
|
31
|
+
spec.summary = "Dynamoid is an ORM for Amazon's DynamoDB"
|
32
|
+
# Ignore not commited files
|
33
|
+
spec.files = Dir[
|
34
|
+
'CHANGELOG.md',
|
35
|
+
'dynamoid.gemspec',
|
36
|
+
'lib/**/*',
|
37
|
+
'LICENSE.txt',
|
38
|
+
'README.md',
|
39
|
+
'SECURITY.md'
|
40
|
+
]
|
41
|
+
spec.homepage = 'http://github.com/Dynamoid/dynamoid'
|
42
|
+
spec.licenses = ['MIT']
|
43
|
+
spec.require_paths = ['lib']
|
44
|
+
|
45
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
46
|
+
spec.metadata['source_code_uri'] = "https://github.com/Dynamoid/dynamoid/tree/v#{spec.version}"
|
47
|
+
spec.metadata['changelog_uri'] = "https://github.com/Dynamoid/dynamoid/blob/v#{spec.version}/CHANGELOG.md"
|
48
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/Dynamoid/dynamoid/issues'
|
49
|
+
spec.metadata['documentation_uri'] = "https://www.rubydoc.info/gems/dynamoid/#{spec.version}"
|
50
|
+
spec.metadata['funding_uri'] = 'https://opencollective.com/dynamoid'
|
51
|
+
spec.metadata['wiki_uri'] = 'https://github.com/Dynamoid/dynamoid/wiki'
|
52
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
53
|
+
|
54
|
+
spec.add_runtime_dependency 'activemodel', '>=4'
|
55
|
+
spec.add_runtime_dependency 'aws-sdk-dynamodb', '~> 1.0'
|
56
|
+
spec.add_runtime_dependency 'concurrent-ruby', '>= 1.0'
|
57
|
+
|
58
|
+
spec.add_development_dependency 'appraisal'
|
59
|
+
spec.add_development_dependency 'bundler'
|
60
|
+
spec.add_development_dependency 'pry', '~> 0.14'
|
61
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
62
|
+
spec.add_development_dependency 'rexml'
|
63
|
+
spec.add_development_dependency 'rspec', '~> 3.12'
|
64
|
+
spec.add_development_dependency 'yard'
|
65
|
+
end
|
data/lib/dynamoid/adapter.rb
CHANGED
@@ -142,7 +142,7 @@ module Dynamoid
|
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
145
|
-
%i[batch_get_item delete_item get_item list_tables put_item truncate batch_write_item batch_delete_item].each do |m|
|
145
|
+
%i[batch_get_item delete_item get_item list_tables put_item truncate batch_write_item batch_delete_item execute].each do |m|
|
146
146
|
# Method delegation with benchmark to the underlying adapter. Faster than relying on method_missing.
|
147
147
|
#
|
148
148
|
# @since 0.2.0
|
@@ -163,6 +163,7 @@ module Dynamoid
|
|
163
163
|
# https://eregon.me/blog/2019/11/10/the-delegation-challenge-of-ruby27.html
|
164
164
|
|
165
165
|
return benchmark(method, *args) { adapter.send(method, *args, &block) } if adapter.respond_to?(method)
|
166
|
+
|
166
167
|
super
|
167
168
|
end
|
168
169
|
|
@@ -170,19 +171,25 @@ module Dynamoid
|
|
170
171
|
# only really useful for range queries, since it can only find by one hash key at once. Only provide
|
171
172
|
# one range key to the hash.
|
172
173
|
#
|
174
|
+
# Dynamoid.adapter.query('users', { id: [[:eq, '1']], age: [[:between, [10, 30]]] }, { batch_size: 1000 })
|
175
|
+
#
|
173
176
|
# @param [String] table_name the name of the table
|
174
|
-
# @param [
|
175
|
-
# @
|
176
|
-
# @
|
177
|
-
# @option
|
178
|
-
# @option
|
179
|
-
# @option
|
180
|
-
# @option
|
181
|
-
#
|
182
|
-
# @
|
183
|
-
#
|
184
|
-
|
185
|
-
|
177
|
+
# @param [Array[Array]] key_conditions conditions for the primary key attributes
|
178
|
+
# @param [Array[Array]] non_key_conditions (optional) conditions for non-primary key attributes
|
179
|
+
# @param [Hash] options (optional) the options to query the table with
|
180
|
+
# @option options [Boolean] :consistent_read You can set the ConsistentRead parameter to true and obtain a strongly consistent result
|
181
|
+
# @option options [Boolean] :scan_index_forward Specifies the order for index traversal: If true (default), the traversal is performed in ascending order; if false, the traversal is performed in descending order.
|
182
|
+
# @option options [Symbop] :select The attributes to be returned in the result (one of ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, ...)
|
183
|
+
# @option options [Symbol] :index_name The name of an index to query. This index can be any local secondary index or global secondary index on the table.
|
184
|
+
# @option options [Hash] :exclusive_start_key The primary key of the first item that this operation will evaluate.
|
185
|
+
# @option options [Integer] :batch_size The number of items to lazily load one by one
|
186
|
+
# @option options [Integer] :record_limit The maximum number of items to return (not necessarily the number of evaluated items)
|
187
|
+
# @option options [Integer] :scan_limit The maximum number of items to evaluate (not necessarily the number of matching items)
|
188
|
+
# @option options [Array[Symbol]] :project The attributes to retrieve from the table
|
189
|
+
#
|
190
|
+
# @return [Enumerable] matching items
|
191
|
+
def query(table_name, key_conditions, non_key_conditions = {}, options = {})
|
192
|
+
adapter.query(table_name, key_conditions, non_key_conditions, options)
|
186
193
|
end
|
187
194
|
|
188
195
|
def self.adapter_plugin_class
|
@@ -29,7 +29,7 @@ module Dynamoid
|
|
29
29
|
gs_indexes = options[:global_secondary_indexes]
|
30
30
|
|
31
31
|
key_schema = {
|
32
|
-
hash_key_schema: { key =>
|
32
|
+
hash_key_schema: { key => options[:hash_key_type] || :string },
|
33
33
|
range_key_schema: options[:range_key]
|
34
34
|
}
|
35
35
|
attribute_definitions = build_all_attribute_definitions(
|
@@ -69,7 +69,7 @@ module Dynamoid
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
resp = client.create_table(client_opts)
|
72
|
-
options[:sync] = true if !options.key?(:sync) && ls_indexes.present? || gs_indexes.present?
|
72
|
+
options[:sync] = true if (!options.key?(:sync) && ls_indexes.present?) || gs_indexes.present?
|
73
73
|
|
74
74
|
if options[:sync]
|
75
75
|
status = PARSE_TABLE_STATUS.call(resp, :table_description)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dynamoid
|
4
|
+
# @private
|
5
|
+
module AdapterPlugin
|
6
|
+
class AwsSdkV3
|
7
|
+
# Excecute a PartiQL query
|
8
|
+
#
|
9
|
+
# Documentation:
|
10
|
+
# - https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ExecuteStatement.html
|
11
|
+
# - https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html#execute_statement-instance_method
|
12
|
+
#
|
13
|
+
# NOTE: For reads result may be paginated. Only pagination with NextToken
|
14
|
+
# is implemented. Currently LastEvaluatedKey in response cannot be fed to
|
15
|
+
# ExecuteStatement to get the next page.
|
16
|
+
#
|
17
|
+
# See also:
|
18
|
+
# - https://repost.aws/questions/QUgNPbBYWiRoOlMsJv-XzrWg/how-to-use-last-evaluated-key-in-execute-statement-request
|
19
|
+
# - https://stackoverflow.com/questions/71438439/aws-dynamodb-executestatement-pagination
|
20
|
+
class ExecuteStatement
|
21
|
+
attr_reader :client, :statement, :parameters, :options
|
22
|
+
|
23
|
+
def initialize(client, statement, parameters, options)
|
24
|
+
@client = client
|
25
|
+
@statement = statement
|
26
|
+
@parameters = parameters
|
27
|
+
@options = options.symbolize_keys.slice(:consistent_read)
|
28
|
+
end
|
29
|
+
|
30
|
+
def call
|
31
|
+
request = {
|
32
|
+
statement: @statement,
|
33
|
+
parameters: @parameters,
|
34
|
+
consistent_read: @options[:consistent_read],
|
35
|
+
}
|
36
|
+
|
37
|
+
response = client.execute_statement(request)
|
38
|
+
|
39
|
+
unless response.next_token
|
40
|
+
return response_to_items(response)
|
41
|
+
end
|
42
|
+
|
43
|
+
Enumerator.new do |yielder|
|
44
|
+
yielder.yield(response_to_items(response))
|
45
|
+
|
46
|
+
while response.next_token
|
47
|
+
request[:next_token] = response.next_token
|
48
|
+
response = client.execute_statement(request)
|
49
|
+
yielder.yield(response_to_items(response))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def response_to_items(response)
|
57
|
+
response.items.map(&:symbolize_keys)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|