dynamoid 3.8.0 → 3.10.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 +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
|