dynamoid 3.7.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +206 -238
- data/LICENSE.txt +2 -2
- data/README.md +43 -18
- data/lib/dynamoid/adapter.rb +5 -8
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/batch_get_item.rb +10 -1
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +20 -14
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +2 -1
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +45 -52
- data/lib/dynamoid/associations/single_association.rb +28 -9
- data/lib/dynamoid/criteria/chain.rb +10 -6
- data/lib/dynamoid/criteria/key_fields_detector.rb +1 -1
- data/lib/dynamoid/criteria.rb +6 -7
- data/lib/dynamoid/dirty.rb +6 -9
- data/lib/dynamoid/document.rb +4 -10
- data/lib/dynamoid/fields.rb +16 -17
- data/lib/dynamoid/indexes.rb +45 -38
- data/lib/dynamoid/loadable.rb +6 -1
- data/lib/dynamoid/persistence/update_fields.rb +1 -1
- data/lib/dynamoid/persistence/upsert.rb +1 -1
- data/lib/dynamoid/persistence.rb +61 -18
- data/lib/dynamoid/undumping.rb +18 -0
- data/lib/dynamoid/validations.rb +6 -0
- data/lib/dynamoid/version.rb +1 -1
- data/lib/dynamoid.rb +1 -0
- metadata +40 -37
data/lib/dynamoid/fields.rb
CHANGED
@@ -8,16 +8,6 @@ module Dynamoid
|
|
8
8
|
module Fields
|
9
9
|
extend ActiveSupport::Concern
|
10
10
|
|
11
|
-
# @private
|
12
|
-
# Types allowed in indexes:
|
13
|
-
PERMITTED_KEY_TYPES = %i[
|
14
|
-
number
|
15
|
-
integer
|
16
|
-
string
|
17
|
-
datetime
|
18
|
-
serialized
|
19
|
-
].freeze
|
20
|
-
|
21
11
|
# Initialize the attributes we know the class has, in addition to our magic attributes: id, created_at, and updated_at.
|
22
12
|
included do
|
23
13
|
class_attribute :attributes, instance_accessor: false
|
@@ -219,20 +209,26 @@ module Dynamoid
|
|
219
209
|
#
|
220
210
|
# @since 0.4.0
|
221
211
|
def table(options)
|
212
|
+
self.options = options
|
213
|
+
|
222
214
|
# a default 'id' column is created when Dynamoid::Document is included
|
223
215
|
unless attributes.key? hash_key
|
224
216
|
remove_field :id
|
225
217
|
field(hash_key)
|
226
218
|
end
|
227
219
|
|
220
|
+
# The created_at/updated_at fields are declared in the `included` callback first.
|
221
|
+
# At that moment the only known setting is `Dynamoid::Config.timestamps`.
|
222
|
+
# Now `options[:timestamps]` may override the global setting for a model.
|
223
|
+
# So we need to make decision again and declare the fields or rollback thier declaration.
|
224
|
+
#
|
225
|
+
# Do not replace with `#timestamps_enabled?`.
|
228
226
|
if options[:timestamps] && !Dynamoid::Config.timestamps
|
229
|
-
#
|
230
|
-
# are disabled globaly
|
227
|
+
# The fields weren't declared in `included` callback because they are disabled globaly
|
231
228
|
field :created_at, :datetime
|
232
229
|
field :updated_at, :datetime
|
233
230
|
elsif options[:timestamps] == false && Dynamoid::Config.timestamps
|
234
|
-
#
|
235
|
-
# disabled for a table
|
231
|
+
# The fields were declared in `included` callback but they are disabled for a table
|
236
232
|
remove_field :created_at
|
237
233
|
remove_field :updated_at
|
238
234
|
end
|
@@ -289,10 +285,12 @@ module Dynamoid
|
|
289
285
|
#
|
290
286
|
# @param name [Symbol] the name of the field
|
291
287
|
# @param value [Object] the value to assign to that field
|
288
|
+
# @return [Dynamoid::Document] self
|
292
289
|
#
|
293
290
|
# @since 0.2.0
|
294
291
|
def write_attribute(name, value)
|
295
292
|
name = name.to_sym
|
293
|
+
old_value = read_attribute(name)
|
296
294
|
|
297
295
|
unless attribute_is_present_on_model?(name)
|
298
296
|
raise Dynamoid::Errors::UnknownAttribute.new("Attribute #{name} is not part of the model")
|
@@ -302,12 +300,13 @@ module Dynamoid
|
|
302
300
|
association.reset
|
303
301
|
end
|
304
302
|
|
305
|
-
attribute_will_change!(name) # Dirty API
|
306
|
-
|
307
303
|
@attributes_before_type_cast[name] = value
|
308
304
|
|
309
305
|
value_casted = TypeCasting.cast_field(value, self.class.attributes[name])
|
306
|
+
attribute_will_change!(name) if old_value != value_casted # Dirty API
|
307
|
+
|
310
308
|
attributes[name] = value_casted
|
309
|
+
self
|
311
310
|
end
|
312
311
|
alias []= write_attribute
|
313
312
|
|
@@ -367,7 +366,7 @@ module Dynamoid
|
|
367
366
|
# @since 0.2.0
|
368
367
|
def set_updated_at
|
369
368
|
# @_touch_record=false means explicit disabling
|
370
|
-
if self.class.timestamps_enabled? && !updated_at_changed? && @_touch_record != false
|
369
|
+
if self.class.timestamps_enabled? && changed? && !updated_at_changed? && @_touch_record != false
|
371
370
|
self.updated_at = DateTime.now.in_time_zone(Time.zone)
|
372
371
|
end
|
373
372
|
end
|
data/lib/dynamoid/indexes.rb
CHANGED
@@ -4,6 +4,15 @@ module Dynamoid
|
|
4
4
|
module Indexes
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
|
+
# @private
|
8
|
+
# @see https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey
|
9
|
+
# Types allowed in indexes
|
10
|
+
PERMITTED_KEY_DYNAMODB_TYPES = %i[
|
11
|
+
string
|
12
|
+
binary
|
13
|
+
number
|
14
|
+
].freeze
|
15
|
+
|
7
16
|
included do
|
8
17
|
class_attribute :local_secondary_indexes, instance_accessor: false
|
9
18
|
class_attribute :global_secondary_indexes, instance_accessor: false
|
@@ -20,17 +29,17 @@ module Dynamoid
|
|
20
29
|
#
|
21
30
|
# field :category
|
22
31
|
#
|
23
|
-
#
|
32
|
+
# global_secondary_index hash_key: :category
|
24
33
|
# end
|
25
34
|
#
|
26
35
|
# The full example with all the options being specified:
|
27
36
|
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
37
|
+
# global_secondary_index hash_key: :category,
|
38
|
+
# range_key: :created_at,
|
39
|
+
# name: 'posts_category_created_at_index',
|
40
|
+
# projected_attributes: :all,
|
41
|
+
# read_capacity: 100,
|
42
|
+
# write_capacity: 20
|
34
43
|
#
|
35
44
|
# Global secondary index should be declared after fields for mentioned
|
36
45
|
# hash key and optional range key are declared (with method +field+)
|
@@ -86,14 +95,14 @@ module Dynamoid
|
|
86
95
|
# range :created_at, :datetime
|
87
96
|
# field :author_id
|
88
97
|
#
|
89
|
-
#
|
98
|
+
# local_secondary_index range_key: :author_id
|
90
99
|
# end
|
91
100
|
#
|
92
101
|
# The full example with all the options being specified:
|
93
102
|
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
103
|
+
# local_secondary_index range_key: :created_at,
|
104
|
+
# name: 'posts_created_at_index',
|
105
|
+
# projected_attributes: :all
|
97
106
|
#
|
98
107
|
# Local secondary index should be declared after fields for mentioned
|
99
108
|
# hash key and optional range key are declared (with method +field+) as
|
@@ -290,39 +299,37 @@ module Dynamoid
|
|
290
299
|
end
|
291
300
|
end
|
292
301
|
|
302
|
+
|
303
|
+
def validate_hash_key
|
304
|
+
validate_index_key(:hash_key, @hash_key)
|
305
|
+
end
|
306
|
+
|
293
307
|
def validate_range_key
|
294
|
-
|
295
|
-
range_field_attributes = @dynamoid_class.attributes[@range_key]
|
296
|
-
if range_field_attributes.present?
|
297
|
-
range_key_type = range_field_attributes[:type]
|
298
|
-
if Dynamoid::Fields::PERMITTED_KEY_TYPES.include?(range_key_type)
|
299
|
-
@range_key_schema = {
|
300
|
-
@range_key => PrimaryKeyTypeMapping.dynamodb_type(range_key_type, range_field_attributes)
|
301
|
-
}
|
302
|
-
else
|
303
|
-
errors.add(:range_key, 'Index :range_key is not a valid key type')
|
304
|
-
end
|
305
|
-
else
|
306
|
-
errors.add(:range_key, "No such field #{@range_key} defined on table")
|
307
|
-
end
|
308
|
-
end
|
308
|
+
validate_index_key(:range_key, @range_key)
|
309
309
|
end
|
310
310
|
|
311
|
-
def
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
311
|
+
def validate_index_key(key_param, key_val)
|
312
|
+
return if key_val.blank?
|
313
|
+
|
314
|
+
key_field_attributes = @dynamoid_class.attributes[key_val]
|
315
|
+
if key_field_attributes.blank?
|
316
|
+
errors.add(key_param, "No such field #{key_val} defined on table")
|
317
|
+
return
|
318
|
+
end
|
319
|
+
|
320
|
+
key_dynamodb_type = dynamodb_type(key_field_attributes[:type], key_field_attributes)
|
321
|
+
if PERMITTED_KEY_DYNAMODB_TYPES.include?(key_dynamodb_type)
|
322
|
+
self.send("#{key_param}_schema=", { key_val => key_dynamodb_type })
|
322
323
|
else
|
323
|
-
errors.add(
|
324
|
+
errors.add(key_param, "Index :#{key_param} is not a valid key type")
|
324
325
|
end
|
325
326
|
end
|
327
|
+
|
328
|
+
def dynamodb_type(field_type, options)
|
329
|
+
PrimaryKeyTypeMapping.dynamodb_type(field_type, options)
|
330
|
+
rescue Errors::UnsupportedKeyType
|
331
|
+
field_type
|
332
|
+
end
|
326
333
|
end
|
327
334
|
end
|
328
335
|
end
|
data/lib/dynamoid/loadable.rb
CHANGED
@@ -8,12 +8,14 @@ module Dynamoid
|
|
8
8
|
attrs.each do |key, value|
|
9
9
|
send("#{key}=", value) if respond_to?("#{key}=")
|
10
10
|
end
|
11
|
+
|
12
|
+
self
|
11
13
|
end
|
12
14
|
|
13
15
|
# Reload an object from the database -- if you suspect the object has changed in the data store and you need those
|
14
16
|
# changes to be reflected immediately, you would call this method. This is a consistent read.
|
15
17
|
#
|
16
|
-
# @return [Dynamoid::Document]
|
18
|
+
# @return [Dynamoid::Document] self
|
17
19
|
#
|
18
20
|
# @since 0.2.0
|
19
21
|
def reload
|
@@ -24,7 +26,10 @@ module Dynamoid
|
|
24
26
|
end
|
25
27
|
|
26
28
|
self.attributes = self.class.find(hash_key, **options).attributes
|
29
|
+
|
27
30
|
@associations.values.each(&:reset)
|
31
|
+
@new_record = false
|
32
|
+
|
28
33
|
self
|
29
34
|
end
|
30
35
|
end
|
data/lib/dynamoid/persistence.rb
CHANGED
@@ -112,8 +112,10 @@ module Dynamoid
|
|
112
112
|
|
113
113
|
if created_successfuly && self.options[:expires]
|
114
114
|
attribute = self.options[:expires][:field]
|
115
|
-
Dynamoid.adapter.update_time_to_live(table_name, attribute)
|
115
|
+
Dynamoid.adapter.update_time_to_live(options[:table_name], attribute)
|
116
116
|
end
|
117
|
+
|
118
|
+
self
|
117
119
|
end
|
118
120
|
|
119
121
|
# Deletes the table for the model.
|
@@ -122,8 +124,10 @@ module Dynamoid
|
|
122
124
|
# is deleted completely.
|
123
125
|
#
|
124
126
|
# Subsequent method calls for the same table will be ignored.
|
127
|
+
# @return [Model class] self
|
125
128
|
def delete_table
|
126
129
|
Dynamoid.adapter.delete_table(table_name)
|
130
|
+
self
|
127
131
|
end
|
128
132
|
|
129
133
|
# @private
|
@@ -365,6 +369,9 @@ module Dynamoid
|
|
365
369
|
#
|
366
370
|
# User.inc('1', 'Tylor', age: 2)
|
367
371
|
#
|
372
|
+
# It's an atomic operation it does not interfere with other write
|
373
|
+
# requests.
|
374
|
+
#
|
368
375
|
# Uses efficient low-level +UpdateItem+ operation and does only one HTTP
|
369
376
|
# request.
|
370
377
|
#
|
@@ -374,6 +381,7 @@ module Dynamoid
|
|
374
381
|
# @param hash_key_value [Scalar value] hash key
|
375
382
|
# @param range_key_value [Scalar value] range key (optional)
|
376
383
|
# @param counters [Hash] value to increase by
|
384
|
+
# @return [Model class] self
|
377
385
|
def inc(hash_key_value, range_key_value = nil, counters)
|
378
386
|
options = if range_key
|
379
387
|
value_casted = TypeCasting.cast_field(range_key_value, attributes[range_key])
|
@@ -391,6 +399,8 @@ module Dynamoid
|
|
391
399
|
t.add(k => value_dumped)
|
392
400
|
end
|
393
401
|
end
|
402
|
+
|
403
|
+
self
|
394
404
|
end
|
395
405
|
end
|
396
406
|
|
@@ -405,11 +415,13 @@ module Dynamoid
|
|
405
415
|
# user.touch(:last_login_at)
|
406
416
|
#
|
407
417
|
# @param name [Symbol] attribute name to update (optional)
|
418
|
+
# @return [Dynamoid::Document] self
|
408
419
|
def touch(name = nil)
|
409
420
|
now = DateTime.now
|
410
421
|
self.updated_at = now
|
411
422
|
attributes[name] = now if name
|
412
423
|
save
|
424
|
+
self
|
413
425
|
end
|
414
426
|
|
415
427
|
# Is this object persisted in DynamoDB?
|
@@ -479,13 +491,9 @@ module Dynamoid
|
|
479
491
|
|
480
492
|
@_touch_record = options[:touch]
|
481
493
|
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
Save.call(self)
|
486
|
-
end
|
487
|
-
end
|
488
|
-
else
|
494
|
+
create_or_update = new_record? ? :create : :update
|
495
|
+
|
496
|
+
run_callbacks(create_or_update) do
|
489
497
|
run_callbacks(:save) do
|
490
498
|
Save.call(self)
|
491
499
|
end
|
@@ -538,10 +546,13 @@ module Dynamoid
|
|
538
546
|
# @param attribute [Symbol] attribute name to update
|
539
547
|
# @param value [Object] the value to assign it
|
540
548
|
# @return [Dynamoid::Document] self
|
549
|
+
#
|
541
550
|
# @since 0.2.0
|
542
551
|
def update_attribute(attribute, value)
|
552
|
+
# final implementation is in the Dynamoid::Validation module
|
543
553
|
write_attribute(attribute, value)
|
544
554
|
save
|
555
|
+
self
|
545
556
|
end
|
546
557
|
|
547
558
|
# Update a model.
|
@@ -555,7 +566,7 @@ module Dynamoid
|
|
555
566
|
# collections if attribute is a collection (one of +array+, +set+ or
|
556
567
|
# +map+).
|
557
568
|
#
|
558
|
-
# user.update do |t|
|
569
|
+
# user.update! do |t|
|
559
570
|
# t.add(age: 1, followers_count: 5)
|
560
571
|
# t.add(hobbies: ['skying', 'climbing'])
|
561
572
|
# end
|
@@ -563,24 +574,28 @@ module Dynamoid
|
|
563
574
|
# Operation +delete+ is applied to collection attribute types and
|
564
575
|
# substructs one collection from another.
|
565
576
|
#
|
566
|
-
# user.update do |t|
|
577
|
+
# user.update! do |t|
|
567
578
|
# t.delete(hobbies: ['skying'])
|
568
579
|
# end
|
569
580
|
#
|
570
581
|
# Operation +set+ just changes an attribute value:
|
571
582
|
#
|
572
|
-
# user.update do |t|
|
583
|
+
# user.update! do |t|
|
573
584
|
# t.set(age: 21)
|
574
585
|
# end
|
575
586
|
#
|
576
|
-
# All the operations
|
587
|
+
# All the operations work like +ADD+, +DELETE+ and +PUT+ actions supported
|
577
588
|
# by +AttributeUpdates+
|
578
589
|
# {parameter}[https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LegacyConditionalParameters.AttributeUpdates.html]
|
579
590
|
# of +UpdateItem+ operation.
|
580
591
|
#
|
592
|
+
# It's an atomic operation. So adding or deleting elements in a collection
|
593
|
+
# or incrementing or decrementing a numeric field is atomic and does not
|
594
|
+
# interfere with other write requests.
|
595
|
+
#
|
581
596
|
# Can update a model conditionaly:
|
582
597
|
#
|
583
|
-
# user.update(if: { age: 20 }) do |t|
|
598
|
+
# user.update!(if: { age: 20 }) do |t|
|
584
599
|
# t.add(age: 1)
|
585
600
|
# end
|
586
601
|
#
|
@@ -593,15 +608,24 @@ module Dynamoid
|
|
593
608
|
# fail.
|
594
609
|
#
|
595
610
|
# @param conditions [Hash] Conditions on model attributes to make a conditional update (optional)
|
611
|
+
# @return [Dynamoid::Document] self
|
596
612
|
def update!(conditions = {})
|
597
613
|
run_callbacks(:update) do
|
598
|
-
options =
|
614
|
+
options = {}
|
615
|
+
if range_key
|
616
|
+
value = read_attribute(range_key)
|
617
|
+
attribute_options = self.class.attributes[range_key]
|
618
|
+
options[:range_key] = Dumping.dump_field(value, attribute_options)
|
619
|
+
end
|
599
620
|
|
600
621
|
begin
|
601
|
-
|
622
|
+
table_name = self.class.table_name
|
623
|
+
update_item_options = options.merge(conditions: conditions)
|
624
|
+
|
625
|
+
new_attrs = Dynamoid.adapter.update_item(table_name, hash_key, update_item_options) do |t|
|
602
626
|
t.add(lock_version: 1) if self.class.attributes[:lock_version]
|
603
627
|
|
604
|
-
if
|
628
|
+
if self.class.timestamps_enabled?
|
605
629
|
time_now = DateTime.now.in_time_zone(Time.zone)
|
606
630
|
time_now_dumped = Dumping.dump_field(time_now, self.class.attributes[:updated_at])
|
607
631
|
t.set(updated_at: time_now_dumped)
|
@@ -614,6 +638,8 @@ module Dynamoid
|
|
614
638
|
raise Dynamoid::Errors::StaleObjectError.new(self, 'update')
|
615
639
|
end
|
616
640
|
end
|
641
|
+
|
642
|
+
self
|
617
643
|
end
|
618
644
|
|
619
645
|
# Update a model.
|
@@ -639,6 +665,19 @@ module Dynamoid
|
|
639
665
|
# t.delete(hobbies: ['skying'])
|
640
666
|
# end
|
641
667
|
#
|
668
|
+
# If it's applied to a scalar attribute then the item's attribute is
|
669
|
+
# removed at all:
|
670
|
+
#
|
671
|
+
# user.update do |t|
|
672
|
+
# t.delete(age: nil)
|
673
|
+
# end
|
674
|
+
#
|
675
|
+
# or even without useless value at all:
|
676
|
+
#
|
677
|
+
# user.update do |t|
|
678
|
+
# t.delete(:age)
|
679
|
+
# end
|
680
|
+
#
|
642
681
|
# Operation +set+ just changes an attribute value:
|
643
682
|
#
|
644
683
|
# user.update do |t|
|
@@ -664,6 +703,7 @@ module Dynamoid
|
|
664
703
|
# fail.
|
665
704
|
#
|
666
705
|
# @param conditions [Hash] Conditions on model attributes to make a conditional update (optional)
|
706
|
+
# @return [true|false] - whether conditions are met and updating is successful
|
667
707
|
def update(conditions = {}, &block)
|
668
708
|
update!(conditions, &block)
|
669
709
|
true
|
@@ -748,9 +788,9 @@ module Dynamoid
|
|
748
788
|
# Supports optimistic locking with the +lock_version+ attribute and doesn't
|
749
789
|
# delete a model if it's already changed.
|
750
790
|
#
|
751
|
-
# Returns +
|
791
|
+
# Returns +self+ if deleted successfully and +false+ otherwise.
|
752
792
|
#
|
753
|
-
# @return [
|
793
|
+
# @return [Dynamoid::Document|false] whether deleted successfully
|
754
794
|
# @since 0.2.0
|
755
795
|
def destroy
|
756
796
|
ret = run_callbacks(:destroy) do
|
@@ -783,6 +823,7 @@ module Dynamoid
|
|
783
823
|
# Raises +Dynamoid::Errors::StaleObjectError+ exception if cannot delete a
|
784
824
|
# model.
|
785
825
|
#
|
826
|
+
# @return [Dynamoid::Document] self
|
786
827
|
# @since 0.2.0
|
787
828
|
def delete
|
788
829
|
options = range_key ? { range_key: Dumping.dump_field(read_attribute(range_key), self.class.attributes[range_key]) } : {}
|
@@ -806,6 +847,8 @@ module Dynamoid
|
|
806
847
|
self.class.associations.each do |name, options|
|
807
848
|
send(name).disassociate_source
|
808
849
|
end
|
850
|
+
|
851
|
+
self
|
809
852
|
rescue Dynamoid::Errors::ConditionalCheckFailedException
|
810
853
|
raise Dynamoid::Errors::StaleObjectError.new(self, 'delete')
|
811
854
|
end
|
data/lib/dynamoid/undumping.rb
CHANGED
@@ -236,9 +236,27 @@ module Dynamoid
|
|
236
236
|
end
|
237
237
|
|
238
238
|
class SerializedUndumper < Base
|
239
|
+
# We must use YAML.safe_load in Ruby 3.1 to handle serialized Set class
|
240
|
+
minimum_ruby_version = ->(version) { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new(version) }
|
241
|
+
# Once we drop support for Rubies older than 2.6 we can remove this conditional (with major version bump)!
|
242
|
+
# YAML_SAFE_LOAD = minimum_ruby_version.call("2.6")
|
243
|
+
# But we don't want to change behavior for Ruby <= 3.0 that has been using the gem, without a major version bump
|
244
|
+
YAML_SAFE_LOAD = minimum_ruby_version.call('3.1')
|
245
|
+
|
239
246
|
def process(value)
|
240
247
|
if @options[:serializer]
|
241
248
|
@options[:serializer].load(value)
|
249
|
+
elsif YAML_SAFE_LOAD
|
250
|
+
# The classes listed in permitted classes are added to the default set of "safe loadable" classes.
|
251
|
+
# TrueClass
|
252
|
+
# FalseClass
|
253
|
+
# NilClass
|
254
|
+
# Integer
|
255
|
+
# Float
|
256
|
+
# String
|
257
|
+
# Array
|
258
|
+
# Hash
|
259
|
+
YAML.safe_load(value, permitted_classes: [Symbol, Set, Date, Time, DateTime])
|
242
260
|
else
|
243
261
|
YAML.load(value)
|
244
262
|
end
|
data/lib/dynamoid/validations.rb
CHANGED
@@ -38,6 +38,12 @@ module Dynamoid
|
|
38
38
|
self
|
39
39
|
end
|
40
40
|
|
41
|
+
def update_attribute(attribute, value)
|
42
|
+
write_attribute(attribute, value)
|
43
|
+
save(validate: false)
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
41
47
|
module ClassMethods
|
42
48
|
# Override validates_presence_of to handle false values as present.
|
43
49
|
#
|
data/lib/dynamoid/version.rb
CHANGED