dynamoid 3.13.0 → 3.14.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 +41 -4
- data/README.md +60 -1584
- data/lib/dynamoid/associations/association.rb +10 -2
- data/lib/dynamoid/associations/belongs_to.rb +3 -1
- data/lib/dynamoid/associations/has_and_belongs_to_many.rb +0 -1
- data/lib/dynamoid/associations/has_many.rb +0 -1
- data/lib/dynamoid/associations/has_one.rb +1 -2
- data/lib/dynamoid/associations/many_association.rb +1 -0
- data/lib/dynamoid/associations.rb +64 -48
- data/lib/dynamoid/components.rb +1 -0
- data/lib/dynamoid/criteria/chain.rb +43 -30
- data/lib/dynamoid/dirty.rb +13 -3
- data/lib/dynamoid/document.rb +29 -21
- data/lib/dynamoid/fields.rb +55 -41
- data/lib/dynamoid/finders.rb +1 -1
- data/lib/dynamoid/indexes.rb +29 -21
- data/lib/dynamoid/persistence/inc.rb +15 -7
- data/lib/dynamoid/persistence/save.rb +21 -14
- data/lib/dynamoid/persistence.rb +88 -54
- data/lib/dynamoid/transactions/mutation/base.rb +76 -0
- data/lib/dynamoid/transactions/mutation/builders/delete_request_builder.rb +44 -0
- data/lib/dynamoid/transactions/mutation/builders/update_request_builder.rb +139 -0
- data/lib/dynamoid/transactions/mutation/create.rb +52 -0
- data/lib/dynamoid/transactions/mutation/delete_with_instance.rb +80 -0
- data/lib/dynamoid/transactions/mutation/delete_with_primary_key.rb +62 -0
- data/lib/dynamoid/transactions/mutation/destroy.rb +96 -0
- data/lib/dynamoid/transactions/mutation/import.rb +84 -0
- data/lib/dynamoid/transactions/mutation/inc.rb +101 -0
- data/lib/dynamoid/transactions/mutation/increment.rb +63 -0
- data/lib/dynamoid/transactions/mutation/save.rb +194 -0
- data/lib/dynamoid/transactions/mutation/touch.rb +99 -0
- data/lib/dynamoid/transactions/mutation/update_attributes.rb +49 -0
- data/lib/dynamoid/transactions/mutation/update_fields.rb +185 -0
- data/lib/dynamoid/transactions/mutation/upsert.rb +94 -0
- data/lib/dynamoid/transactions/mutation.rb +935 -0
- data/lib/dynamoid/transactions/retrieval/find.rb +140 -0
- data/lib/dynamoid/transactions/retrieval.rb +150 -0
- data/lib/dynamoid/transactions.rb +63 -0
- data/lib/dynamoid/version.rb +1 -1
- data/lib/dynamoid.rb +1 -2
- metadata +23 -17
- data/lib/dynamoid/transaction_read/find.rb +0 -137
- data/lib/dynamoid/transaction_read.rb +0 -146
- data/lib/dynamoid/transaction_write/base.rb +0 -59
- data/lib/dynamoid/transaction_write/create.rb +0 -49
- data/lib/dynamoid/transaction_write/delete_with_instance.rb +0 -65
- data/lib/dynamoid/transaction_write/delete_with_primary_key.rb +0 -64
- data/lib/dynamoid/transaction_write/destroy.rb +0 -84
- data/lib/dynamoid/transaction_write/item_updater.rb +0 -60
- data/lib/dynamoid/transaction_write/save.rb +0 -177
- data/lib/dynamoid/transaction_write/update_attributes.rb +0 -46
- data/lib/dynamoid/transaction_write/update_fields.rb +0 -247
- data/lib/dynamoid/transaction_write/upsert.rb +0 -113
- data/lib/dynamoid/transaction_write.rb +0 -673
data/lib/dynamoid/fields.rb
CHANGED
|
@@ -26,13 +26,15 @@ module Dynamoid
|
|
|
26
26
|
module ClassMethods
|
|
27
27
|
# Specify a field for a document.
|
|
28
28
|
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
29
|
+
# ```
|
|
30
|
+
# class User
|
|
31
|
+
# include Dynamoid::Document
|
|
31
32
|
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
33
|
+
# field :last_name
|
|
34
|
+
# field :age, :integer
|
|
35
|
+
# field :last_sign_in, :datetime
|
|
36
|
+
# end
|
|
37
|
+
# ```
|
|
36
38
|
#
|
|
37
39
|
# Its type determines how it is coerced when read in and out of the
|
|
38
40
|
# data store. You can specify +string+, +integer+, +number+, +set+, +array+,
|
|
@@ -101,35 +103,39 @@ module Dynamoid
|
|
|
101
103
|
#
|
|
102
104
|
# It works in the following way:
|
|
103
105
|
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
+
# ```
|
|
107
|
+
# class User
|
|
108
|
+
# include Dynamoid::Document
|
|
106
109
|
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
110
|
+
# field :age, :integer
|
|
111
|
+
# end
|
|
109
112
|
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
+
# user = User.new
|
|
114
|
+
# user.age # => nil
|
|
115
|
+
# user.age? # => false
|
|
113
116
|
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
117
|
+
# user.age = 20
|
|
118
|
+
# user.age? # => true
|
|
116
119
|
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
+
# user.age = '21'
|
|
121
|
+
# user.age # => 21 - integer
|
|
122
|
+
# user.age_before_type_cast # => '21' - string
|
|
123
|
+
# ```
|
|
120
124
|
#
|
|
121
125
|
# There is also an option +alias+ which allows to use another name for a
|
|
122
126
|
# field:
|
|
123
127
|
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
128
|
+
# ```
|
|
129
|
+
# class User
|
|
130
|
+
# include Dynamoid::Document
|
|
126
131
|
#
|
|
127
|
-
#
|
|
128
|
-
#
|
|
132
|
+
# field :firstName, :string, alias: :first_name
|
|
133
|
+
# end
|
|
129
134
|
#
|
|
130
|
-
#
|
|
131
|
-
#
|
|
132
|
-
#
|
|
135
|
+
# user = User.new(firstName: 'Michael')
|
|
136
|
+
# user.firstName # Michael
|
|
137
|
+
# user.first_name # Michael
|
|
138
|
+
# ```
|
|
133
139
|
#
|
|
134
140
|
# @param name [Symbol] name of the field
|
|
135
141
|
# @param type [Symbol] type of the field (optional)
|
|
@@ -152,11 +158,13 @@ module Dynamoid
|
|
|
152
158
|
|
|
153
159
|
# Declare a table range key.
|
|
154
160
|
#
|
|
155
|
-
#
|
|
156
|
-
#
|
|
161
|
+
# ```
|
|
162
|
+
# class User
|
|
163
|
+
# include Dynamoid::Document
|
|
157
164
|
#
|
|
158
|
-
#
|
|
159
|
-
#
|
|
165
|
+
# range :last_name
|
|
166
|
+
# end
|
|
167
|
+
# ```
|
|
160
168
|
#
|
|
161
169
|
# By default a range key is a string. In order to use any other type it
|
|
162
170
|
# should be specified as a second argument:
|
|
@@ -187,29 +195,35 @@ module Dynamoid
|
|
|
187
195
|
#
|
|
188
196
|
# The +table+ method can be used to override the defaults:
|
|
189
197
|
#
|
|
190
|
-
#
|
|
191
|
-
#
|
|
198
|
+
# ```
|
|
199
|
+
# class User
|
|
200
|
+
# include Dynamoid::Document
|
|
192
201
|
#
|
|
193
|
-
#
|
|
194
|
-
#
|
|
202
|
+
# table name: :customers, key: :uuid
|
|
203
|
+
# end
|
|
204
|
+
# ```
|
|
195
205
|
#
|
|
196
206
|
# The hash key field is declared by default and a type is a string. If
|
|
197
207
|
# another type is needed the field should be declared explicitly:
|
|
198
208
|
#
|
|
199
|
-
#
|
|
200
|
-
#
|
|
209
|
+
# ```
|
|
210
|
+
# class User
|
|
211
|
+
# include Dynamoid::Document
|
|
201
212
|
#
|
|
202
|
-
#
|
|
203
|
-
#
|
|
213
|
+
# field :id, :integer
|
|
214
|
+
# end
|
|
215
|
+
# ```
|
|
204
216
|
#
|
|
205
217
|
# To declare a new attribute with not-default type as a table hash key a
|
|
206
218
|
# :key_type option can be used:
|
|
207
219
|
#
|
|
208
|
-
#
|
|
209
|
-
#
|
|
220
|
+
# ```
|
|
221
|
+
# class User
|
|
222
|
+
# include Dynamoid::Document
|
|
210
223
|
#
|
|
211
|
-
#
|
|
212
|
-
#
|
|
224
|
+
# table key: :user_id, key_type: :integer
|
|
225
|
+
# end
|
|
226
|
+
# ```
|
|
213
227
|
#
|
|
214
228
|
# @param options [Hash] options to override default table settings
|
|
215
229
|
# @option options [Symbol] :name name of a table
|
data/lib/dynamoid/finders.rb
CHANGED
data/lib/dynamoid/indexes.rb
CHANGED
|
@@ -24,22 +24,26 @@ module Dynamoid
|
|
|
24
24
|
# Defines a Global Secondary index on a table. Keys can be specified as
|
|
25
25
|
# hash-only, or hash & range.
|
|
26
26
|
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
27
|
+
# ```
|
|
28
|
+
# class Post
|
|
29
|
+
# include Dynamoid::Document
|
|
29
30
|
#
|
|
30
|
-
#
|
|
31
|
+
# field :category
|
|
31
32
|
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
33
|
+
# global_secondary_index hash_key: :category
|
|
34
|
+
# end
|
|
35
|
+
# ```
|
|
34
36
|
#
|
|
35
37
|
# The full example with all the options being specified:
|
|
36
38
|
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
39
|
+
# ```
|
|
40
|
+
# global_secondary_index hash_key: :category,
|
|
41
|
+
# range_key: :created_at,
|
|
42
|
+
# name: 'posts_category_created_at_index',
|
|
43
|
+
# projected_attributes: :all,
|
|
44
|
+
# read_capacity: 100,
|
|
45
|
+
# write_capacity: 20
|
|
46
|
+
# ```
|
|
43
47
|
#
|
|
44
48
|
# Global secondary index should be declared after fields for mentioned
|
|
45
49
|
# hash key and optional range key are declared (with method +field+)
|
|
@@ -88,21 +92,25 @@ module Dynamoid
|
|
|
88
92
|
# Defines a local secondary index on a table. Will use the same primary
|
|
89
93
|
# hash key as the table.
|
|
90
94
|
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
95
|
+
# ```
|
|
96
|
+
# class Comment
|
|
97
|
+
# include Dynamoid::Document
|
|
93
98
|
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
99
|
+
# table hash_key: :post_id
|
|
100
|
+
# range :created_at, :datetime
|
|
101
|
+
# field :author_id
|
|
97
102
|
#
|
|
98
|
-
#
|
|
99
|
-
#
|
|
103
|
+
# local_secondary_index range_key: :author_id
|
|
104
|
+
# end
|
|
105
|
+
# ```
|
|
100
106
|
#
|
|
101
107
|
# The full example with all the options being specified:
|
|
102
108
|
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
109
|
+
# ```
|
|
110
|
+
# local_secondary_index range_key: :created_at,
|
|
111
|
+
# name: 'posts_created_at_index',
|
|
112
|
+
# projected_attributes: :all
|
|
113
|
+
# ```
|
|
106
114
|
#
|
|
107
115
|
# Local secondary index should be declared after fields for mentioned
|
|
108
116
|
# hash key and optional range key are declared (with method +field+) as
|
|
@@ -6,7 +6,7 @@ module Dynamoid
|
|
|
6
6
|
module Persistence
|
|
7
7
|
# @private
|
|
8
8
|
class Inc
|
|
9
|
-
def self.call(model_class, partition_key, sort_key = nil, counters)
|
|
9
|
+
def self.call(model_class, partition_key, sort_key = nil, counters) # rubocop:disable Style/OptionalArguments
|
|
10
10
|
new(model_class, partition_key, sort_key, counters).call
|
|
11
11
|
end
|
|
12
12
|
|
|
@@ -16,11 +16,14 @@ module Dynamoid
|
|
|
16
16
|
@partition_key = partition_key
|
|
17
17
|
@sort_key = sort_key
|
|
18
18
|
@counters = counters
|
|
19
|
+
@touch = @counters.delete(:touch)
|
|
19
20
|
end
|
|
20
21
|
# rubocop:enable Style/OptionalArguments
|
|
21
22
|
|
|
22
23
|
def call
|
|
23
|
-
|
|
24
|
+
validate_primary_key!
|
|
25
|
+
Dynamoid::Persistence::UpdateValidations.validate_attributes_exist(@model_class, @counters)
|
|
26
|
+
|
|
24
27
|
partition_key_dumped = cast_and_dump(@model_class.hash_key, @partition_key)
|
|
25
28
|
options = update_item_options(partition_key_dumped)
|
|
26
29
|
|
|
@@ -31,10 +34,10 @@ module Dynamoid
|
|
|
31
34
|
item_updater.add(name => value)
|
|
32
35
|
end
|
|
33
36
|
|
|
34
|
-
if touch
|
|
37
|
+
if @touch
|
|
35
38
|
value = DateTime.now.in_time_zone(Time.zone)
|
|
36
39
|
|
|
37
|
-
timestamp_attributes_to_touch
|
|
40
|
+
timestamp_attributes_to_touch.each do |name|
|
|
38
41
|
item_updater.set(name => value)
|
|
39
42
|
end
|
|
40
43
|
end
|
|
@@ -44,6 +47,11 @@ module Dynamoid
|
|
|
44
47
|
|
|
45
48
|
private
|
|
46
49
|
|
|
50
|
+
def validate_primary_key!
|
|
51
|
+
raise Dynamoid::Errors::MissingHashKey if @partition_key.nil?
|
|
52
|
+
raise Dynamoid::Errors::MissingRangeKey if @model_class.range_key? && @sort_key.nil?
|
|
53
|
+
end
|
|
54
|
+
|
|
47
55
|
def update_item_options(partition_key_dumped)
|
|
48
56
|
options = {}
|
|
49
57
|
|
|
@@ -63,12 +71,12 @@ module Dynamoid
|
|
|
63
71
|
options
|
|
64
72
|
end
|
|
65
73
|
|
|
66
|
-
def timestamp_attributes_to_touch
|
|
67
|
-
return [] unless touch
|
|
74
|
+
def timestamp_attributes_to_touch
|
|
75
|
+
return [] unless @touch
|
|
68
76
|
|
|
69
77
|
names = []
|
|
70
78
|
names << :updated_at if @model_class.timestamps_enabled?
|
|
71
|
-
names += Array.wrap(touch) if touch != true
|
|
79
|
+
names += Array.wrap(@touch) if @touch != true
|
|
72
80
|
names
|
|
73
81
|
end
|
|
74
82
|
|
|
@@ -30,12 +30,18 @@ module Dynamoid
|
|
|
30
30
|
|
|
31
31
|
# Add an optimistic locking check if the lock_version column exists
|
|
32
32
|
if @model.class.attributes[:lock_version]
|
|
33
|
-
@model.lock_version
|
|
33
|
+
if @model.lock_version.nil? && @model.new_record?
|
|
34
|
+
@model.lock_version = 1
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if @model.lock_version && !@model.changes[:lock_version]
|
|
38
|
+
@model.lock_version += 1
|
|
39
|
+
end
|
|
34
40
|
end
|
|
35
41
|
|
|
36
42
|
if @model.new_record?
|
|
37
43
|
attributes_dumped = Dumping.dump_attributes(@model.attributes, @model.class.attributes)
|
|
38
|
-
Dynamoid.adapter.write(@model.class.table_name, attributes_dumped,
|
|
44
|
+
Dynamoid.adapter.write(@model.class.table_name, attributes_dumped, conditions_to_create_item)
|
|
39
45
|
else
|
|
40
46
|
attributes_to_persist = @model.attributes.slice(*@model.changed.map(&:to_sym))
|
|
41
47
|
partition_key_dumped = dump(@model.class.hash_key, @model.hash_key)
|
|
@@ -68,7 +74,7 @@ module Dynamoid
|
|
|
68
74
|
end
|
|
69
75
|
|
|
70
76
|
# Should be called after incrementing `lock_version` attribute
|
|
71
|
-
def
|
|
77
|
+
def conditions_to_create_item
|
|
72
78
|
conditions = {}
|
|
73
79
|
|
|
74
80
|
# Add an 'exists' check to prevent overwriting existing records with new ones
|
|
@@ -77,14 +83,6 @@ module Dynamoid
|
|
|
77
83
|
conditions[:unless_exists] << @model.range_key
|
|
78
84
|
end
|
|
79
85
|
|
|
80
|
-
# Add an optimistic locking check if the lock_version column exists
|
|
81
|
-
# Uses the original lock_version value from Dirty API
|
|
82
|
-
# in case user changed 'lock_version' manually
|
|
83
|
-
if @model.class.attributes[:lock_version] && @model.changes[:lock_version][0]
|
|
84
|
-
conditions[:if] ||= {}
|
|
85
|
-
conditions[:if][:lock_version] = @model.changes[:lock_version][0]
|
|
86
|
-
end
|
|
87
|
-
|
|
88
86
|
conditions
|
|
89
87
|
end
|
|
90
88
|
|
|
@@ -103,9 +101,18 @@ module Dynamoid
|
|
|
103
101
|
# Add an optimistic locking check if the lock_version column exists
|
|
104
102
|
# Uses the original lock_version value from Dirty API
|
|
105
103
|
# in case user changed 'lock_version' manually
|
|
106
|
-
if @model.class.attributes[:lock_version]
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
if @model.class.attributes[:lock_version]
|
|
105
|
+
lock_version = if @model.changes[:lock_version]
|
|
106
|
+
@model.changes[:lock_version][0]
|
|
107
|
+
else
|
|
108
|
+
@model.lock_version
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# skip concurrency control when lock_version is nil
|
|
112
|
+
if lock_version
|
|
113
|
+
conditions[:if] ||= {}
|
|
114
|
+
conditions[:if][:lock_version] = lock_version
|
|
115
|
+
end
|
|
109
116
|
end
|
|
110
117
|
|
|
111
118
|
options[:conditions] = conditions
|
data/lib/dynamoid/persistence.rb
CHANGED
|
@@ -48,17 +48,19 @@ module Dynamoid
|
|
|
48
48
|
#
|
|
49
49
|
# For instance here
|
|
50
50
|
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
51
|
+
# ```
|
|
52
|
+
# class User
|
|
53
|
+
# include Dynamoid::Document
|
|
53
54
|
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
55
|
+
# table key: :uuid
|
|
56
|
+
# range :last_name
|
|
56
57
|
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
58
|
+
# field :first_name
|
|
59
|
+
# field :last_name
|
|
60
|
+
# end
|
|
60
61
|
#
|
|
61
|
-
#
|
|
62
|
+
# User.create_table
|
|
63
|
+
# ```
|
|
62
64
|
#
|
|
63
65
|
# +create_table+ method call will create a table +dynamoid_users+ with
|
|
64
66
|
# hash key +uuid+ and range key +name+, DynamoDB default billing mode and
|
|
@@ -186,9 +188,11 @@ module Dynamoid
|
|
|
186
188
|
# Instantiates a model and pass it into an optional block to set other
|
|
187
189
|
# attributes.
|
|
188
190
|
#
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
#
|
|
191
|
+
# ```
|
|
192
|
+
# User.create(first_name: 'Mark') do |u|
|
|
193
|
+
# u.age = 21
|
|
194
|
+
# end
|
|
195
|
+
# ```
|
|
192
196
|
#
|
|
193
197
|
# Validates model and runs callbacks.
|
|
194
198
|
#
|
|
@@ -227,9 +231,11 @@ module Dynamoid
|
|
|
227
231
|
# Instantiates a model and pass it into an optional block to set other
|
|
228
232
|
# attributes.
|
|
229
233
|
#
|
|
230
|
-
#
|
|
231
|
-
#
|
|
232
|
-
#
|
|
234
|
+
# ```
|
|
235
|
+
# User.create!(first_name: 'Mark') do |u|
|
|
236
|
+
# u.age = 21
|
|
237
|
+
# end
|
|
238
|
+
# ```
|
|
233
239
|
#
|
|
234
240
|
# Validates model and runs callbacks.
|
|
235
241
|
#
|
|
@@ -448,6 +454,10 @@ module Dynamoid
|
|
|
448
454
|
# User.inc('1', age: 2, touch: :viewed_at)
|
|
449
455
|
# User.inc('1', age: 2, touch: [:viewed_at, :accessed_at])
|
|
450
456
|
#
|
|
457
|
+
# Raises +Dynamoid::Errors::MissingHashKey+ if a partition key has value
|
|
458
|
+
# +nil+ and raises +Dynamoid::Errors::MissingRangeKey+ if a sort key is
|
|
459
|
+
# required but has value +nil+ or is missing.
|
|
460
|
+
#
|
|
451
461
|
# @param hash_key_value [Scalar value] hash key
|
|
452
462
|
# @param range_key_value [Scalar value] range key (optional)
|
|
453
463
|
# @param counters [Hash] value to increase by
|
|
@@ -850,23 +860,29 @@ module Dynamoid
|
|
|
850
860
|
# Operation +add+ just adds a value for numeric attributes and join
|
|
851
861
|
# collections if attribute is a set.
|
|
852
862
|
#
|
|
853
|
-
#
|
|
854
|
-
#
|
|
855
|
-
#
|
|
856
|
-
#
|
|
863
|
+
# ```
|
|
864
|
+
# user.update! do |t|
|
|
865
|
+
# t.add(age: 1, followers_count: 5)
|
|
866
|
+
# t.add(hobbies: ['skying', 'climbing'])
|
|
867
|
+
# end
|
|
868
|
+
# ```
|
|
857
869
|
#
|
|
858
870
|
# Operation +delete+ is applied to collection attribute types and
|
|
859
871
|
# substructs one collection from another.
|
|
860
872
|
#
|
|
861
|
-
#
|
|
862
|
-
#
|
|
863
|
-
#
|
|
873
|
+
# ```
|
|
874
|
+
# user.update! do |t|
|
|
875
|
+
# t.delete(hobbies: ['skying'])
|
|
876
|
+
# end
|
|
877
|
+
# ```
|
|
864
878
|
#
|
|
865
879
|
# Operation +set+ just changes an attribute value:
|
|
866
880
|
#
|
|
867
|
-
#
|
|
868
|
-
#
|
|
869
|
-
#
|
|
881
|
+
# ```
|
|
882
|
+
# user.update! do |t|
|
|
883
|
+
# t.set(age: 21)
|
|
884
|
+
# end
|
|
885
|
+
# ```
|
|
870
886
|
#
|
|
871
887
|
# All the operations work like +ADD+, +DELETE+ and +PUT+ actions supported
|
|
872
888
|
# by +AttributeUpdates+
|
|
@@ -879,18 +895,22 @@ module Dynamoid
|
|
|
879
895
|
#
|
|
880
896
|
# Can update a model conditionaly:
|
|
881
897
|
#
|
|
882
|
-
#
|
|
883
|
-
#
|
|
884
|
-
#
|
|
898
|
+
# ```
|
|
899
|
+
# user.update!(if: { age: 20 }) do |t|
|
|
900
|
+
# t.add(age: 1)
|
|
901
|
+
# end
|
|
902
|
+
# ```
|
|
885
903
|
#
|
|
886
904
|
# To check if some attribute (or attributes) isn't stored in a DynamoDB
|
|
887
905
|
# item (e.g. it wasn't set explicitly) there is another condition -
|
|
888
906
|
# +unless_exists+:
|
|
889
907
|
#
|
|
890
|
-
#
|
|
891
|
-
#
|
|
892
|
-
#
|
|
893
|
-
#
|
|
908
|
+
# ```
|
|
909
|
+
# user = User.create(name: 'Tylor')
|
|
910
|
+
# user.update!(unless_exists: [:age]) do |t|
|
|
911
|
+
# t.set(age: 18)
|
|
912
|
+
# end
|
|
913
|
+
# ```
|
|
894
914
|
#
|
|
895
915
|
# If a document doesn't meet conditions it raises
|
|
896
916
|
# +Dynamoid::Errors::StaleObjectError+ exception.
|
|
@@ -957,36 +977,46 @@ module Dynamoid
|
|
|
957
977
|
# Operation +add+ just adds a value for numeric attributes and join
|
|
958
978
|
# collections if attribute is a set.
|
|
959
979
|
#
|
|
960
|
-
#
|
|
961
|
-
#
|
|
962
|
-
#
|
|
963
|
-
#
|
|
980
|
+
# ```
|
|
981
|
+
# user.update do |t|
|
|
982
|
+
# t.add(age: 1, followers_count: 5)
|
|
983
|
+
# t.add(hobbies: ['skying', 'climbing'])
|
|
984
|
+
# end
|
|
985
|
+
# ```
|
|
964
986
|
#
|
|
965
987
|
# Operation +delete+ is applied to collection attribute types and
|
|
966
988
|
# substructs one collection from another.
|
|
967
989
|
#
|
|
968
|
-
#
|
|
969
|
-
#
|
|
970
|
-
#
|
|
990
|
+
# ```
|
|
991
|
+
# user.update do |t|
|
|
992
|
+
# t.delete(hobbies: ['skying'])
|
|
993
|
+
# end
|
|
994
|
+
# ```
|
|
971
995
|
#
|
|
972
996
|
# If it's applied to a scalar attribute then the item's attribute is
|
|
973
997
|
# removed at all:
|
|
974
998
|
#
|
|
975
|
-
#
|
|
976
|
-
#
|
|
977
|
-
#
|
|
999
|
+
# ```
|
|
1000
|
+
# user.update do |t|
|
|
1001
|
+
# t.delete(age: nil)
|
|
1002
|
+
# end
|
|
1003
|
+
# ```
|
|
978
1004
|
#
|
|
979
1005
|
# or even without useless value at all:
|
|
980
1006
|
#
|
|
981
|
-
#
|
|
982
|
-
#
|
|
983
|
-
#
|
|
1007
|
+
# ```
|
|
1008
|
+
# user.update do |t|
|
|
1009
|
+
# t.delete(:age)
|
|
1010
|
+
# end
|
|
1011
|
+
# ```
|
|
984
1012
|
#
|
|
985
1013
|
# Operation +set+ just changes an attribute value:
|
|
986
1014
|
#
|
|
987
|
-
#
|
|
988
|
-
#
|
|
989
|
-
#
|
|
1015
|
+
# ```
|
|
1016
|
+
# user.update do |t|
|
|
1017
|
+
# t.set(age: 21)
|
|
1018
|
+
# end
|
|
1019
|
+
# ```
|
|
990
1020
|
#
|
|
991
1021
|
# All the operations works like +ADD+, +DELETE+ and +PUT+ actions supported
|
|
992
1022
|
# by +AttributeUpdates+
|
|
@@ -995,18 +1025,22 @@ module Dynamoid
|
|
|
995
1025
|
#
|
|
996
1026
|
# Can update a model conditionaly:
|
|
997
1027
|
#
|
|
998
|
-
#
|
|
999
|
-
#
|
|
1000
|
-
#
|
|
1028
|
+
# ```
|
|
1029
|
+
# user.update(if: { age: 20 }) do |t|
|
|
1030
|
+
# t.add(age: 1)
|
|
1031
|
+
# end
|
|
1032
|
+
# ```
|
|
1001
1033
|
#
|
|
1002
1034
|
# To check if some attribute (or attributes) isn't stored in a DynamoDB
|
|
1003
1035
|
# item (e.g. it wasn't set explicitly) there is another condition -
|
|
1004
1036
|
# +unless_exists+:
|
|
1005
1037
|
#
|
|
1006
|
-
#
|
|
1007
|
-
#
|
|
1008
|
-
#
|
|
1009
|
-
#
|
|
1038
|
+
# ```
|
|
1039
|
+
# user = User.create(name: 'Tylor')
|
|
1040
|
+
# user.update(unless_exists: [:age]) do |t|
|
|
1041
|
+
# t.set(age: 18)
|
|
1042
|
+
# end
|
|
1043
|
+
# ```
|
|
1010
1044
|
#
|
|
1011
1045
|
# If a document doesn't meet conditions it just returns +false+. Otherwise it returns +true+.
|
|
1012
1046
|
#
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dynamoid
|
|
4
|
+
module Transactions
|
|
5
|
+
class Mutation
|
|
6
|
+
# @private
|
|
7
|
+
class Base
|
|
8
|
+
# Callback called at "initialization" or "registration" an action
|
|
9
|
+
# before changes are persisted. It's a proper place to validate
|
|
10
|
+
# a model or run callbacks
|
|
11
|
+
def on_registration
|
|
12
|
+
raise 'Not implemented'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Callback called after changes are persisted.
|
|
16
|
+
# It's a proper place to mark changes in a model as applied.
|
|
17
|
+
def on_commit
|
|
18
|
+
raise 'Not implemented'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Callback called when a transaction is rolled back.
|
|
22
|
+
# It's a proper place to undo changes made in after_... callbacks.
|
|
23
|
+
def on_rollback
|
|
24
|
+
raise 'Not implemented'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Whether some callback aborted or canceled an action
|
|
28
|
+
def aborted?
|
|
29
|
+
raise 'Not implemented'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Whether there are changes to persist, e.g. updating a model with no
|
|
33
|
+
# attribute changed is skipped.
|
|
34
|
+
def skipped?
|
|
35
|
+
raise 'Not implemented'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Value returned to a user as an action result
|
|
39
|
+
def observable_by_user_result
|
|
40
|
+
raise 'Not implemented'
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Coresponding part of a final request body
|
|
44
|
+
def action_requests
|
|
45
|
+
raise 'Not implemented'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.validate_attribute_names!(model_class, names)
|
|
49
|
+
names.each do |name|
|
|
50
|
+
unless model_class.attributes[name]
|
|
51
|
+
raise Dynamoid::Errors::UnknownAttribute.new(model_class, name)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def validate_attribute_names!(model_class, names)
|
|
59
|
+
self.class.validate_attribute_names!(model_class, names)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# copied from aws_sdk_v3
|
|
63
|
+
def sanitize_item(attributes)
|
|
64
|
+
config_value = Dynamoid.config.store_attribute_with_nil_value
|
|
65
|
+
store_attribute_with_nil_value = config_value.nil? ? false : !!config_value
|
|
66
|
+
|
|
67
|
+
attributes.reject do |_, v|
|
|
68
|
+
!store_attribute_with_nil_value && v.nil?
|
|
69
|
+
end.transform_values do |v|
|
|
70
|
+
v.is_a?(Hash) ? v.stringify_keys : v
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|