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
data/lib/dynamoid/dirty.rb
CHANGED
@@ -26,20 +26,18 @@ module Dynamoid
|
|
26
26
|
module ClassMethods
|
27
27
|
def update_fields(*)
|
28
28
|
super.tap do |model|
|
29
|
-
model.
|
29
|
+
model.clear_changes_information if model
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
def upsert(*)
|
34
34
|
super.tap do |model|
|
35
|
-
model.
|
35
|
+
model.clear_changes_information if model
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
39
|
def from_database(*)
|
40
|
-
super.tap
|
41
|
-
m.send(:clear_changes_information)
|
42
|
-
end
|
40
|
+
super.tap(&:clear_changes_information)
|
43
41
|
end
|
44
42
|
end
|
45
43
|
|
@@ -110,7 +108,7 @@ module Dynamoid
|
|
110
108
|
#
|
111
109
|
# @return [ActiveSupport::HashWithIndifferentAccess]
|
112
110
|
def changes
|
113
|
-
ActiveSupport::HashWithIndifferentAccess[changed.map { |
|
111
|
+
ActiveSupport::HashWithIndifferentAccess[changed.map { |name| [name, attribute_change(name)] }]
|
114
112
|
end
|
115
113
|
|
116
114
|
# Returns a hash of attributes that were changed before the model was saved.
|
@@ -137,6 +135,25 @@ module Dynamoid
|
|
137
135
|
@changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
|
138
136
|
end
|
139
137
|
|
138
|
+
# Clear all dirty data: current changes and previous changes.
|
139
|
+
def clear_changes_information
|
140
|
+
@previously_changed = ActiveSupport::HashWithIndifferentAccess.new
|
141
|
+
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
142
|
+
end
|
143
|
+
|
144
|
+
# Clears dirty data and moves +changes+ to +previous_changes+.
|
145
|
+
def changes_applied
|
146
|
+
@previously_changed = changes
|
147
|
+
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
148
|
+
end
|
149
|
+
|
150
|
+
# Remove changes information for the provided attributes.
|
151
|
+
#
|
152
|
+
# @param attributes [Array[String]] - a list of attributes to clear changes for
|
153
|
+
def clear_attribute_changes(names)
|
154
|
+
attributes_changed_by_setter.except!(*names)
|
155
|
+
end
|
156
|
+
|
140
157
|
# Handle <tt>*_changed?</tt> for +method_missing+.
|
141
158
|
#
|
142
159
|
# person.attribute_changed?(:name) # => true
|
@@ -145,14 +162,14 @@ module Dynamoid
|
|
145
162
|
# person.attribute_changed?(:name, from: 'Alice', to: 'Bod')
|
146
163
|
#
|
147
164
|
# @private
|
148
|
-
# @param
|
165
|
+
# @param name [Symbol] attribute name
|
149
166
|
# @param options [Hash] conditions on +from+ and +to+ value (optional)
|
150
167
|
# @option options [Symbol] :from previous attribute value
|
151
168
|
# @option options [Symbol] :to current attribute value
|
152
|
-
def attribute_changed?(
|
153
|
-
result = changes_include?(
|
154
|
-
result &&= options[:to] ==
|
155
|
-
result &&= options[:from] == changed_attributes[
|
169
|
+
def attribute_changed?(name, options = {})
|
170
|
+
result = changes_include?(name)
|
171
|
+
result &&= options[:to] == read_attribute(name) if options.key?(:to)
|
172
|
+
result &&= options[:from] == changed_attributes[name] if options.key?(:from)
|
156
173
|
result
|
157
174
|
end
|
158
175
|
|
@@ -163,16 +180,16 @@ module Dynamoid
|
|
163
180
|
# person.attribute_was(:name) # => "Alice"
|
164
181
|
#
|
165
182
|
# @private
|
166
|
-
# @param
|
167
|
-
def attribute_was(
|
168
|
-
attribute_changed?(
|
183
|
+
# @param name [Symbol] attribute name
|
184
|
+
def attribute_was(name)
|
185
|
+
attribute_changed?(name) ? changed_attributes[name] : read_attribute(name)
|
169
186
|
end
|
170
187
|
|
171
188
|
# Restore all previous data of the provided attributes.
|
172
189
|
#
|
173
190
|
# @param attributes [Array[Symbol]] a list of attribute names
|
174
|
-
def restore_attributes(
|
175
|
-
|
191
|
+
def restore_attributes(names = changed)
|
192
|
+
names.each { |name| restore_attribute! name }
|
176
193
|
end
|
177
194
|
|
178
195
|
# Handles <tt>*_previously_changed?</tt> for +method_missing+.
|
@@ -183,10 +200,10 @@ module Dynamoid
|
|
183
200
|
# person.attribute_changed?(:name) # => true
|
184
201
|
#
|
185
202
|
# @private
|
186
|
-
# @param
|
203
|
+
# @param name [Symbol] attribute name
|
187
204
|
# @return [true|false]
|
188
|
-
def attribute_previously_changed?(
|
189
|
-
previous_changes_include?(
|
205
|
+
def attribute_previously_changed?(name)
|
206
|
+
previous_changes_include?(name)
|
190
207
|
end
|
191
208
|
|
192
209
|
# Handles <tt>*_previous_change</tt> for +method_missing+.
|
@@ -197,61 +214,49 @@ module Dynamoid
|
|
197
214
|
# person.attribute_previously_changed(:name) # => ["Alice", "Bob"]
|
198
215
|
#
|
199
216
|
# @private
|
200
|
-
# @param
|
217
|
+
# @param name [Symbol]
|
201
218
|
# @return [Array]
|
202
|
-
def attribute_previous_change(
|
203
|
-
previous_changes[
|
219
|
+
def attribute_previous_change(name)
|
220
|
+
previous_changes[name] if attribute_previously_changed?(name)
|
204
221
|
end
|
205
222
|
|
206
223
|
private
|
207
224
|
|
208
|
-
def changes_include?(
|
209
|
-
attributes_changed_by_setter.include?(
|
225
|
+
def changes_include?(name)
|
226
|
+
attributes_changed_by_setter.include?(name)
|
210
227
|
end
|
211
228
|
alias attribute_changed_by_setter? changes_include?
|
212
229
|
|
213
|
-
# Removes current changes and makes them accessible through +previous_changes+.
|
214
|
-
def changes_applied # :doc:
|
215
|
-
@previously_changed = changes
|
216
|
-
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
217
|
-
end
|
218
|
-
|
219
|
-
# Clear all dirty data: current changes and previous changes.
|
220
|
-
def clear_changes_information # :doc:
|
221
|
-
@previously_changed = ActiveSupport::HashWithIndifferentAccess.new
|
222
|
-
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
223
|
-
end
|
224
|
-
|
225
230
|
# Handle <tt>*_change</tt> for +method_missing+.
|
226
|
-
def attribute_change(
|
227
|
-
[changed_attributes[
|
231
|
+
def attribute_change(name)
|
232
|
+
[changed_attributes[name], read_attribute(name)] if attribute_changed?(name)
|
228
233
|
end
|
229
234
|
|
230
235
|
# Handle <tt>*_will_change!</tt> for +method_missing+.
|
231
|
-
def attribute_will_change!(
|
232
|
-
return if attribute_changed?(
|
236
|
+
def attribute_will_change!(name)
|
237
|
+
return if attribute_changed?(name)
|
233
238
|
|
234
239
|
begin
|
235
|
-
value =
|
236
|
-
value = value.
|
240
|
+
value = read_attribute(name)
|
241
|
+
value = value.clone if value.duplicable?
|
237
242
|
rescue TypeError, NoMethodError
|
238
243
|
end
|
239
244
|
|
240
|
-
set_attribute_was(
|
245
|
+
set_attribute_was(name, value)
|
241
246
|
end
|
242
247
|
|
243
248
|
# Handle <tt>restore_*!</tt> for +method_missing+.
|
244
|
-
def restore_attribute!(
|
245
|
-
if attribute_changed?(
|
246
|
-
|
247
|
-
clear_attribute_changes([
|
249
|
+
def restore_attribute!(name)
|
250
|
+
if attribute_changed?(name)
|
251
|
+
write_attribute(name, changed_attributes[name])
|
252
|
+
clear_attribute_changes([name])
|
248
253
|
end
|
249
254
|
end
|
250
255
|
|
251
|
-
# Returns +true+ if
|
256
|
+
# Returns +true+ if name were changed before the model was saved,
|
252
257
|
# +false+ otherwise.
|
253
|
-
def previous_changes_include?(
|
254
|
-
previous_changes.include?(
|
258
|
+
def previous_changes_include?(name)
|
259
|
+
previous_changes.include?(name)
|
255
260
|
end
|
256
261
|
|
257
262
|
# This is necessary because `changed_attributes` might be overridden in
|
@@ -259,13 +264,8 @@ module Dynamoid
|
|
259
264
|
alias attributes_changed_by_setter changed_attributes
|
260
265
|
|
261
266
|
# Force an attribute to have a particular "before" value
|
262
|
-
def set_attribute_was(
|
263
|
-
attributes_changed_by_setter[
|
264
|
-
end
|
265
|
-
|
266
|
-
# Remove changes information for the provided attributes.
|
267
|
-
def clear_attribute_changes(attributes)
|
268
|
-
attributes_changed_by_setter.except!(*attributes)
|
267
|
+
def set_attribute_was(name, old_value)
|
268
|
+
attributes_changed_by_setter[name] = old_value
|
269
269
|
end
|
270
270
|
end
|
271
271
|
end
|
data/lib/dynamoid/document.rb
CHANGED
@@ -160,6 +160,22 @@ module Dynamoid
|
|
160
160
|
end
|
161
161
|
end
|
162
162
|
|
163
|
+
attr_accessor :abstract_class
|
164
|
+
|
165
|
+
def abstract_class?
|
166
|
+
defined?(@abstract_class) && @abstract_class == true
|
167
|
+
end
|
168
|
+
|
169
|
+
def sti_name
|
170
|
+
name
|
171
|
+
end
|
172
|
+
|
173
|
+
def sti_class_for(type_name)
|
174
|
+
type_name.constantize
|
175
|
+
rescue NameError
|
176
|
+
raise Errors::SubclassNotFound, "STI subclass does not found. Subclass: '#{type_name}'"
|
177
|
+
end
|
178
|
+
|
163
179
|
# @private
|
164
180
|
def deep_subclasses
|
165
181
|
subclasses + subclasses.map(&:deep_subclasses).flatten
|
@@ -167,7 +183,7 @@ module Dynamoid
|
|
167
183
|
|
168
184
|
# @private
|
169
185
|
def choose_right_class(attrs)
|
170
|
-
attrs[inheritance_field] ? attrs[inheritance_field]
|
186
|
+
attrs[inheritance_field] ? sti_class_for(attrs[inheritance_field]) : self
|
171
187
|
end
|
172
188
|
end
|
173
189
|
|
@@ -206,7 +222,7 @@ module Dynamoid
|
|
206
222
|
load(attrs_with_defaults.merge(attrs_virtual))
|
207
223
|
|
208
224
|
if block
|
209
|
-
|
225
|
+
yield(self)
|
210
226
|
end
|
211
227
|
end
|
212
228
|
end
|
@@ -244,7 +260,7 @@ module Dynamoid
|
|
244
260
|
#
|
245
261
|
# @return [Integer]
|
246
262
|
def hash
|
247
|
-
hash_key
|
263
|
+
[hash_key, range_value].hash
|
248
264
|
end
|
249
265
|
|
250
266
|
# Return a model's hash key value.
|
@@ -278,6 +294,26 @@ module Dynamoid
|
|
278
294
|
end
|
279
295
|
end
|
280
296
|
|
297
|
+
def inspect
|
298
|
+
# attributes order is:
|
299
|
+
# - partition key
|
300
|
+
# - sort key
|
301
|
+
# - user defined attributes
|
302
|
+
# - timestamps - created_at/updated_at
|
303
|
+
names = [self.class.hash_key]
|
304
|
+
names << self.class.range_key if self.class.range_key
|
305
|
+
names += self.class.attributes.keys - names - %i[created_at updated_at]
|
306
|
+
names << :created_at if self.class.attributes.key?(:created_at)
|
307
|
+
names << :updated_at if self.class.attributes.key?(:updated_at)
|
308
|
+
|
309
|
+
inspection = names.map do |name|
|
310
|
+
value = read_attribute(name)
|
311
|
+
"#{name}: #{value.inspect}"
|
312
|
+
end.join(', ')
|
313
|
+
|
314
|
+
"#<#{self.class.name} #{inspection}>"
|
315
|
+
end
|
316
|
+
|
281
317
|
private
|
282
318
|
|
283
319
|
def dumped_range_value
|
data/lib/dynamoid/dumping.rb
CHANGED
@@ -210,7 +210,7 @@ module Dynamoid
|
|
210
210
|
# datetime -> integer/string
|
211
211
|
class DateTimeDumper < Base
|
212
212
|
def process(value)
|
213
|
-
|
213
|
+
value.nil? ? nil : format_datetime(value, @options)
|
214
214
|
end
|
215
215
|
|
216
216
|
private
|
@@ -237,7 +237,7 @@ module Dynamoid
|
|
237
237
|
# date -> integer/string
|
238
238
|
class DateDumper < Base
|
239
239
|
def process(value)
|
240
|
-
|
240
|
+
value.nil? ? nil : format_date(value, @options)
|
241
241
|
end
|
242
242
|
|
243
243
|
private
|
data/lib/dynamoid/errors.rb
CHANGED
@@ -48,7 +48,7 @@ module Dynamoid
|
|
48
48
|
|
49
49
|
@source.generated_methods.module_eval do
|
50
50
|
define_method(name) { read_attribute(name) }
|
51
|
-
define_method("#{name}?") do
|
51
|
+
define_method(:"#{name}?") do
|
52
52
|
value = read_attribute(name)
|
53
53
|
case value
|
54
54
|
when true then true
|
@@ -57,8 +57,8 @@ module Dynamoid
|
|
57
57
|
!value.nil?
|
58
58
|
end
|
59
59
|
end
|
60
|
-
define_method("#{name}=") { |value| write_attribute(name, value) }
|
61
|
-
define_method("#{name}_before_type_cast") { read_attribute_before_type_cast(name) }
|
60
|
+
define_method(:"#{name}=") { |value| write_attribute(name, value) }
|
61
|
+
define_method(:"#{name}_before_type_cast") { read_attribute_before_type_cast(name) }
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
@@ -70,9 +70,9 @@ module Dynamoid
|
|
70
70
|
|
71
71
|
@source.generated_methods.module_eval do
|
72
72
|
alias_method alias_name, name
|
73
|
-
alias_method "#{alias_name}=", "#{name}="
|
74
|
-
alias_method "#{alias_name}?", "#{name}?"
|
75
|
-
alias_method "#{alias_name}_before_type_cast", "#{name}_before_type_cast"
|
73
|
+
alias_method :"#{alias_name}=", :"#{name}="
|
74
|
+
alias_method :"#{alias_name}?", :"#{name}?"
|
75
|
+
alias_method :"#{alias_name}_before_type_cast", :"#{name}_before_type_cast"
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
data/lib/dynamoid/fields.rb
CHANGED
@@ -164,7 +164,7 @@ module Dynamoid
|
|
164
164
|
#
|
165
165
|
# @param name [Symbol] a range key attribute name
|
166
166
|
# @param type [Symbol] a range key type (optional)
|
167
|
-
# @param options [
|
167
|
+
# @param options [Hash] type options (optional)
|
168
168
|
def range(name, type = :string, options = {})
|
169
169
|
field(name, type, options)
|
170
170
|
self.range_key = name
|
@@ -263,10 +263,8 @@ module Dynamoid
|
|
263
263
|
|
264
264
|
# @private
|
265
265
|
def generated_methods
|
266
|
-
@generated_methods ||=
|
267
|
-
|
268
|
-
include(mod)
|
269
|
-
end
|
266
|
+
@generated_methods ||= Module.new.tap do |mod|
|
267
|
+
include(mod)
|
270
268
|
end
|
271
269
|
end
|
272
270
|
end
|
@@ -293,7 +291,7 @@ module Dynamoid
|
|
293
291
|
old_value = read_attribute(name)
|
294
292
|
|
295
293
|
unless attribute_is_present_on_model?(name)
|
296
|
-
raise Dynamoid::Errors::UnknownAttribute
|
294
|
+
raise Dynamoid::Errors::UnknownAttribute, "Attribute #{name} is not part of the model"
|
297
295
|
end
|
298
296
|
|
299
297
|
if association = @associations[name]
|
@@ -354,23 +352,6 @@ module Dynamoid
|
|
354
352
|
|
355
353
|
private
|
356
354
|
|
357
|
-
# Automatically called during the created callback to set the created_at time.
|
358
|
-
#
|
359
|
-
# @since 0.2.0
|
360
|
-
def set_created_at
|
361
|
-
self.created_at ||= DateTime.now.in_time_zone(Time.zone) if self.class.timestamps_enabled?
|
362
|
-
end
|
363
|
-
|
364
|
-
# Automatically called during the save callback to set the updated_at time.
|
365
|
-
#
|
366
|
-
# @since 0.2.0
|
367
|
-
def set_updated_at
|
368
|
-
# @_touch_record=false means explicit disabling
|
369
|
-
if self.class.timestamps_enabled? && changed? && !updated_at_changed? && @_touch_record != false
|
370
|
-
self.updated_at = DateTime.now.in_time_zone(Time.zone)
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
355
|
def set_expires_field
|
375
356
|
options = self.class.options[:expires]
|
376
357
|
|
@@ -379,23 +360,24 @@ module Dynamoid
|
|
379
360
|
seconds = options[:after]
|
380
361
|
|
381
362
|
if self[name].blank?
|
382
|
-
send("#{name}=", Time.now.to_i + seconds)
|
363
|
+
send(:"#{name}=", Time.now.to_i + seconds)
|
383
364
|
end
|
384
365
|
end
|
385
366
|
end
|
386
367
|
|
387
368
|
def set_inheritance_field
|
388
369
|
# actually it does only following logic:
|
389
|
-
# self.type ||= self.class.
|
370
|
+
# self.type ||= self.class.sti_name if self.class.attributes[:type]
|
371
|
+
return if self.class.abstract_class?
|
390
372
|
|
391
373
|
type = self.class.inheritance_field
|
392
374
|
if self.class.attributes[type] && send(type).nil?
|
393
|
-
send("#{type}=", self.class.
|
375
|
+
send(:"#{type}=", self.class.sti_name)
|
394
376
|
end
|
395
377
|
end
|
396
378
|
|
397
379
|
def attribute_is_present_on_model?(attribute_name)
|
398
|
-
setter = "#{attribute_name}="
|
380
|
+
setter = :"#{attribute_name}="
|
399
381
|
respond_to?(setter)
|
400
382
|
end
|
401
383
|
end
|
data/lib/dynamoid/finders.rb
CHANGED
@@ -6,17 +6,6 @@ module Dynamoid
|
|
6
6
|
module Finders
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
|
-
# @private
|
10
|
-
RANGE_MAP = {
|
11
|
-
'gt' => :range_greater_than,
|
12
|
-
'lt' => :range_less_than,
|
13
|
-
'gte' => :range_gte,
|
14
|
-
'lte' => :range_lte,
|
15
|
-
'begins_with' => :range_begins_with,
|
16
|
-
'between' => :range_between,
|
17
|
-
'eq' => :range_eq
|
18
|
-
}.freeze
|
19
|
-
|
20
9
|
module ClassMethods
|
21
10
|
# Find one or many objects, specified by one id or an array of ids.
|
22
11
|
#
|
@@ -118,7 +107,7 @@ module Dynamoid
|
|
118
107
|
|
119
108
|
# @private
|
120
109
|
def _find_all(ids, options = {})
|
121
|
-
raise Errors::MissingRangeKey if range_key && ids.any? { |
|
110
|
+
raise Errors::MissingRangeKey if range_key && ids.any? { |_pk, sk| sk.nil? }
|
122
111
|
|
123
112
|
if range_key
|
124
113
|
ids = ids.map do |pk, sk|
|
@@ -151,7 +140,9 @@ module Dynamoid
|
|
151
140
|
end
|
152
141
|
|
153
142
|
if items.size == ids.size || !options[:raise_error]
|
154
|
-
items ? items.map { |i| from_database(i) } : []
|
143
|
+
models = items ? items.map { |i| from_database(i) } : []
|
144
|
+
models.each { |m| m.run_callbacks :find }
|
145
|
+
models
|
155
146
|
else
|
156
147
|
ids_list = range_key ? ids.map { |pk, sk| "(#{pk},#{sk})" } : ids.map(&:to_s)
|
157
148
|
message = "Couldn't find all #{name.pluralize} with primary keys [#{ids_list.join(', ')}] "
|
@@ -173,7 +164,9 @@ module Dynamoid
|
|
173
164
|
end
|
174
165
|
|
175
166
|
if item = Dynamoid.adapter.read(table_name, id, options.slice(:range_key, :consistent_read))
|
176
|
-
from_database(item)
|
167
|
+
model = from_database(item)
|
168
|
+
model.run_callbacks :find
|
169
|
+
model
|
177
170
|
elsif options[:raise_error]
|
178
171
|
primary_key = range_key ? "(#{id},#{options[:range_key]})" : id
|
179
172
|
message = "Couldn't find #{name} with primary key #{primary_key}"
|
@@ -249,7 +242,6 @@ module Dynamoid
|
|
249
242
|
range = options[:range] || {}
|
250
243
|
hash_key_field, hash_key_value = hash.first
|
251
244
|
range_key_field, range_key_value = range.first
|
252
|
-
range_op_mapped = nil
|
253
245
|
|
254
246
|
if range_key_field
|
255
247
|
range_key_field = range_key_field.to_s
|
@@ -257,27 +249,30 @@ module Dynamoid
|
|
257
249
|
if range_key_field.include?('.')
|
258
250
|
range_key_field, range_key_op = range_key_field.split('.', 2)
|
259
251
|
end
|
260
|
-
range_op_mapped = RANGE_MAP.fetch(range_key_op)
|
261
252
|
end
|
262
253
|
|
263
254
|
# Find the index
|
264
255
|
index = find_index(hash_key_field, range_key_field)
|
265
256
|
raise Dynamoid::Errors::MissingIndex, "attempted to find #{[hash_key_field, range_key_field]}" if index.nil?
|
266
257
|
|
267
|
-
#
|
268
|
-
|
269
|
-
|
270
|
-
hash_value: hash_key_value,
|
271
|
-
index_name: index.name
|
272
|
-
}
|
258
|
+
# Query
|
259
|
+
query_key_conditions = {}
|
260
|
+
query_key_conditions[hash_key_field.to_sym] = [[:eq, hash_key_value]]
|
273
261
|
if range_key_field
|
274
|
-
|
275
|
-
opts[range_op_mapped] = range_key_value
|
276
|
-
end
|
277
|
-
dynamo_options = opts.merge(options.reject { |key, _| key == :range })
|
278
|
-
Dynamoid.adapter.query(table_name, dynamo_options).flat_map { |i| i }.map do |item|
|
279
|
-
from_database(item)
|
262
|
+
query_key_conditions[range_key_field.to_sym] = [[range_key_op.to_sym, range_key_value]]
|
280
263
|
end
|
264
|
+
|
265
|
+
query_non_key_conditions = options
|
266
|
+
.except(*Dynamoid::AdapterPlugin::AwsSdkV3::Query::OPTIONS_KEYS)
|
267
|
+
.except(:range)
|
268
|
+
.symbolize_keys
|
269
|
+
|
270
|
+
query_options = options.slice(*Dynamoid::AdapterPlugin::AwsSdkV3::Query::OPTIONS_KEYS)
|
271
|
+
query_options[:index_name] = index.name
|
272
|
+
|
273
|
+
Dynamoid.adapter.query(table_name, query_key_conditions, query_non_key_conditions, query_options)
|
274
|
+
.flat_map { |i| i }
|
275
|
+
.map { |item| from_database(item) }
|
281
276
|
end
|
282
277
|
|
283
278
|
# Find using exciting method_missing finders attributes. Uses criteria
|
@@ -294,7 +289,8 @@ module Dynamoid
|
|
294
289
|
# @private
|
295
290
|
# @since 0.2.0
|
296
291
|
def method_missing(method, *args)
|
297
|
-
|
292
|
+
# Cannot use Symbol#start_with? because it was introduced in Ruby 2.7, but we support Ruby >= 2.3
|
293
|
+
if method.to_s.start_with?('find')
|
298
294
|
ActiveSupport::Deprecation.warn("[Dynamoid] .#{method} is deprecated! Call .where instead of")
|
299
295
|
|
300
296
|
finder = method.to_s.split('_by_').first
|
@@ -303,7 +299,7 @@ module Dynamoid
|
|
303
299
|
chain = Dynamoid::Criteria::Chain.new(self)
|
304
300
|
chain = chain.where({}.tap { |h| attributes.each_with_index { |attr, index| h[attr.to_sym] = args[index] } })
|
305
301
|
|
306
|
-
if finder
|
302
|
+
if finder.include?('all')
|
307
303
|
chain.all
|
308
304
|
else
|
309
305
|
chain.first
|
data/lib/dynamoid/indexes.rb
CHANGED
@@ -130,13 +130,13 @@ module Dynamoid
|
|
130
130
|
index_range_key = options[:range_key]
|
131
131
|
|
132
132
|
unless index_range_key.present?
|
133
|
-
raise Dynamoid::Errors::InvalidIndex, 'A local secondary index '\
|
134
|
-
|
133
|
+
raise Dynamoid::Errors::InvalidIndex, 'A local secondary index ' \
|
134
|
+
'requires a :range_key to be specified'
|
135
135
|
end
|
136
136
|
|
137
137
|
if primary_range_key.present? && index_range_key == primary_range_key
|
138
|
-
raise Dynamoid::Errors::InvalidIndex, 'A local secondary index'\
|
139
|
-
|
138
|
+
raise Dynamoid::Errors::InvalidIndex, 'A local secondary index ' \
|
139
|
+
'must use a different :range_key than the primary key'
|
140
140
|
end
|
141
141
|
|
142
142
|
index_opts = options.merge(
|
@@ -159,8 +159,7 @@ module Dynamoid
|
|
159
159
|
# @param range [scalar] the range key used to declare an index (optional)
|
160
160
|
# @return [Dynamoid::Indexes::Index, nil] index object or nil if it isn't found
|
161
161
|
def find_index(hash, range = nil)
|
162
|
-
|
163
|
-
index
|
162
|
+
indexes[index_key(hash, range)]
|
164
163
|
end
|
165
164
|
|
166
165
|
# Returns an index by its name
|
@@ -169,10 +168,9 @@ module Dynamoid
|
|
169
168
|
# @return [Dynamoid::Indexes::Index, nil] index object or nil if it isn't found
|
170
169
|
def find_index_by_name(name)
|
171
170
|
string_name = name.to_s
|
172
|
-
indexes.each_value.detect{ |i| i.name.to_s == string_name }
|
171
|
+
indexes.each_value.detect { |i| i.name.to_s == string_name }
|
173
172
|
end
|
174
173
|
|
175
|
-
|
176
174
|
# Returns true iff the provided hash[,range] key combo is a local
|
177
175
|
# secondary index.
|
178
176
|
#
|
@@ -299,7 +297,6 @@ module Dynamoid
|
|
299
297
|
end
|
300
298
|
end
|
301
299
|
|
302
|
-
|
303
300
|
def validate_hash_key
|
304
301
|
validate_index_key(:hash_key, @hash_key)
|
305
302
|
end
|
@@ -319,7 +316,7 @@ module Dynamoid
|
|
319
316
|
|
320
317
|
key_dynamodb_type = dynamodb_type(key_field_attributes[:type], key_field_attributes)
|
321
318
|
if PERMITTED_KEY_DYNAMODB_TYPES.include?(key_dynamodb_type)
|
322
|
-
|
319
|
+
send(:"#{key_param}_schema=", { key_val => key_dynamodb_type })
|
323
320
|
else
|
324
321
|
errors.add(key_param, "Index :#{key_param} is not a valid key type")
|
325
322
|
end
|
data/lib/dynamoid/loadable.rb
CHANGED
@@ -6,7 +6,7 @@ module Dynamoid
|
|
6
6
|
|
7
7
|
def load(attrs)
|
8
8
|
attrs.each do |key, value|
|
9
|
-
send("#{key}=", value) if respond_to?("#{key}=")
|
9
|
+
send(:"#{key}=", value) if respond_to?(:"#{key}=")
|
10
10
|
end
|
11
11
|
|
12
12
|
self
|
@@ -27,7 +27,7 @@ module Dynamoid
|
|
27
27
|
|
28
28
|
self.attributes = self.class.find(hash_key, **options).attributes
|
29
29
|
|
30
|
-
@associations.
|
30
|
+
@associations.each_value(&:reset)
|
31
31
|
@new_record = false
|
32
32
|
|
33
33
|
self
|
@@ -1,10 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dynamoid
|
2
4
|
module Log
|
5
|
+
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Log/Formatter.html
|
6
|
+
# https://docs.aws.amazon.com/sdk-for-ruby/v2/api/Seahorse/Client/Response.html
|
7
|
+
# https://aws.amazon.com/ru/blogs/developer/logging-requests/
|
3
8
|
module Formatter
|
4
|
-
|
5
|
-
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Log/Formatter.html
|
6
|
-
# https://docs.aws.amazon.com/sdk-for-ruby/v2/api/Seahorse/Client/Response.html
|
7
|
-
# https://aws.amazon.com/ru/blogs/developer/logging-requests/
|
8
9
|
class Debug
|
9
10
|
def format(response)
|
10
11
|
bold = "\x1b[1m"
|
@@ -21,6 +22,20 @@ module Dynamoid
|
|
21
22
|
].join("\n")
|
22
23
|
end
|
23
24
|
end
|
25
|
+
|
26
|
+
class Compact
|
27
|
+
def format(response)
|
28
|
+
bold = "\x1b[1m"
|
29
|
+
reset = "\x1b[0m"
|
30
|
+
|
31
|
+
[
|
32
|
+
response.context.operation.name,
|
33
|
+
bold,
|
34
|
+
response.context.http_request.body.string,
|
35
|
+
reset
|
36
|
+
].join(' ')
|
37
|
+
end
|
38
|
+
end
|
24
39
|
end
|
25
40
|
end
|
26
41
|
end
|