Module: Dynamoid::Persistence
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/dynamoid/persistence.rb,
lib/dynamoid/persistence/inc.rb,
lib/dynamoid/persistence/save.rb,
lib/dynamoid/persistence/import.rb,
lib/dynamoid/persistence/upsert.rb,
lib/dynamoid/persistence/update_fields.rb,
lib/dynamoid/persistence/update_validations.rb,
lib/dynamoid/persistence/item_updater_with_dumping.rb,
lib/dynamoid/persistence/item_updater_with_casting_and_dumping.rb
Overview
Persistence is responsible for dumping objects to and marshalling objects from the data store. It tries to reserialize values to be of the same type as when they were passed in, based on the fields in the class.
Defined Under Namespace
Modules: ClassMethods
Instance Attribute Summary collapse
-
#destroyed ⇒ Object
(also: #destroyed?)
Returns the value of attribute destroyed.
-
#new_record ⇒ Object
(also: #new_record?)
Returns the value of attribute new_record.
Instance Method Summary collapse
-
#decrement(attribute, by = 1) ⇒ Dynamoid::Document
Change numeric attribute value.
-
#decrement!(attribute, by = 1, touch: nil) ⇒ Dynamoid::Document
Change numeric attribute value and save a model.
-
#delete ⇒ Dynamoid::Document
Delete a model.
-
#destroy ⇒ Dynamoid::Document|false
Delete a model.
-
#destroy! ⇒ Object
Delete a model.
-
#increment(attribute, by = 1) ⇒ Dynamoid::Document
Change numeric attribute value.
-
#increment!(attribute, by = 1, touch: nil) ⇒ Dynamoid::Document
Change numeric attribute value and save a model.
-
#persisted? ⇒ true|false
Is this object persisted in DynamoDB?.
-
#save(options = {}) ⇒ true|false
Create new model or persist changes.
-
#save!(options = {}) ⇒ true|false
Create new model or persist changes.
-
#touch(*names, time: nil) ⇒ Dynamoid::Document
Update document timestamps.
-
#update(conditions = {}, &block) ⇒ true|false
Update a model.
-
#update!(conditions = {}) ⇒ Dynamoid::Document
Update a model.
-
#update_attribute(attribute, value) ⇒ Dynamoid::Document
Update a single attribute, saving the object afterwards.
-
#update_attribute!(attribute, value) ⇒ Dynamoid::Document
Update a single attribute, saving the object afterwards.
-
#update_attributes(attributes) ⇒ true|false
Update multiple attributes at once, saving the object once the updates are complete.
-
#update_attributes!(attributes) ⇒ Object
Update multiple attributes at once, saving the object once the updates are complete.
Instance Attribute Details
#destroyed ⇒ Object Also known as: destroyed?
Returns the value of attribute destroyed.
22 23 24 |
# File 'lib/dynamoid/persistence.rb', line 22 def destroyed @destroyed end |
#new_record ⇒ Object Also known as: new_record?
Returns the value of attribute new_record.
22 23 24 |
# File 'lib/dynamoid/persistence.rb', line 22 def new_record @new_record end |
Instance Method Details
#decrement(attribute, by = 1) ⇒ Dynamoid::Document
Change numeric attribute value.
Initializes attribute to zero if nil and subtracts the specified value
(by default is 1). Only makes sense for number-based attributes.
user.decrement(:followers_count)
user.decrement(:followers_count, 2)
1125 1126 1127 |
# File 'lib/dynamoid/persistence.rb', line 1125 def decrement(attribute, by = 1) increment(attribute, -by) end |
#decrement!(attribute, by = 1, touch: nil) ⇒ Dynamoid::Document
Change numeric attribute value and save a model.
Initializes attribute to zero if nil and subtracts the specified value
(by default is 1). Only makes sense for number-based attributes.
user.decrement!(:followers_count)
user.decrement!(:followers_count, 2)
Only attribute is saved. The model itself is not saved. So any other
modified attributes will still be dirty. Validations and callbacks are
skipped.
When :touch option is passed the timestamp columns are updating. If
attribute names are passed, they are updated along with updated_at
attribute:
user.decrement!(:followers_count, touch: true)
user.decrement!(:followers_count, touch: :viewed_at)
user.decrement!(:followers_count, touch: [:viewed_at, :accessed_at])
1153 1154 1155 |
# File 'lib/dynamoid/persistence.rb', line 1153 def decrement!(attribute, by = 1, touch: nil) increment!(attribute, -by, touch: touch) end |
#delete ⇒ Dynamoid::Document
Delete a model.
Supports optimistic locking with the lock_version attribute and doesn't
delete a model if it's already changed.
Raises Dynamoid::Errors::StaleObjectError exception if cannot delete a
model.
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and raises Dynamoid::Errors::MissingRangeKey if a sort key is
required but has value nil.
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 |
# File 'lib/dynamoid/persistence.rb', line 1212 def delete raise Dynamoid::Errors::MissingHashKey if hash_key.nil? raise Dynamoid::Errors::MissingRangeKey if self.class.range_key? && range_value.nil? = range_key ? { range_key: Dumping.dump_field(read_attribute(range_key), self.class.attributes[range_key]) } : {} partition_key_dumped = Dumping.dump_field(hash_key, self.class.attributes[self.class.hash_key]) # Add an optimistic locking check if the lock_version column exists if self.class.attributes[:lock_version] conditions = { if: {} } conditions[:if][:lock_version] = if changes[:lock_version].nil? lock_version else changes[:lock_version][0] end [:conditions] = conditions end @destroyed = true Dynamoid.adapter.delete(self.class.table_name, partition_key_dumped, ) self.class.associations.each_key do |name| send(name).disassociate_source end self rescue Dynamoid::Errors::ConditionalCheckFailedException @destroyed = false raise Dynamoid::Errors::StaleObjectError.new(self, 'delete') end |
#destroy ⇒ Dynamoid::Document|false
Delete a model.
Runs callbacks.
Supports optimistic locking with the lock_version attribute and doesn't
delete a model if it's already changed.
Returns self if deleted successfully and false otherwise.
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and raises Dynamoid::Errors::MissingRangeKey if a sort key is
required but has value nil.
1172 1173 1174 1175 1176 1177 1178 1179 |
# File 'lib/dynamoid/persistence.rb', line 1172 def destroy run_callbacks(:destroy) do delete @destroyed = true end @destroyed ? self : false end |
#destroy! ⇒ Object
Delete a model.
Runs callbacks.
Supports optimistic locking with the lock_version attribute and doesn't
delete a model if it's already changed.
Raises Dynamoid::Errors::RecordNotDestroyed exception if model deleting
failed.
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and raises Dynamoid::Errors::MissingRangeKey if a sort key is
required but has value nil.
1194 1195 1196 |
# File 'lib/dynamoid/persistence.rb', line 1194 def destroy! destroy || (raise Dynamoid::Errors::RecordNotDestroyed, self) end |
#increment(attribute, by = 1) ⇒ Dynamoid::Document
Change numeric attribute value.
Initializes attribute to zero if nil and adds the specified value (by
default is 1). Only makes sense for number-based attributes.
user.increment(:followers_count)
user.increment(:followers_count, 2)
1072 1073 1074 1075 1076 |
# File 'lib/dynamoid/persistence.rb', line 1072 def increment(attribute, by = 1) self[attribute] ||= 0 self[attribute] += by self end |
#increment!(attribute, by = 1, touch: nil) ⇒ Dynamoid::Document
Change numeric attribute value and save a model.
Initializes attribute to zero if nil and adds the specified value (by
default is 1). Only makes sense for number-based attributes.
user.increment!(:followers_count)
user.increment!(:followers_count, 2)
Only attribute is saved. The model itself is not saved. So any other
modified attributes will still be dirty. Validations and callbacks are
skipped.
When :touch option is passed the timestamp columns are updating. If
attribute names are passed, they are updated along with updated_at
attribute:
user.increment!(:followers_count, touch: true)
user.increment!(:followers_count, touch: :viewed_at)
user.increment!(:followers_count, touch: [:viewed_at, :accessed_at])
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 |
# File 'lib/dynamoid/persistence.rb', line 1102 def increment!(attribute, by = 1, touch: nil) increment(attribute, by) change = read_attribute(attribute) - (attribute_was(attribute) || 0) run_callbacks :touch do self.class.inc(hash_key, range_value, attribute => change, touch: touch) clear_attribute_changes(attribute) end self end |
#persisted? ⇒ true|false
Is this object persisted in DynamoDB?
user = User.new
user.persisted? # => false
user.save
user.persisted? # => true
611 612 613 |
# File 'lib/dynamoid/persistence.rb', line 611 def persisted? !(new_record? || @destroyed) end |
#save(options = {}) ⇒ true|false
Create new model or persist changes.
Run the validation and callbacks. Returns true if saving is successful
and false otherwise.
user = User.new
user.save # => true
user.age = 26
user.save # => true
Validation can be skipped with validate: false option:
user = User.new(age: -1)
user.save(validate: false) # => true
save by default sets timestamps attributes - created_at and
updated_at when creates new model and updates updated_at attribute
when updates already existing one.
Changing updated_at attribute at updating a model can be skipped with
touch: false option:
user.save(touch: false)
If a model is new and hash key (+id+ by default) is not assigned yet it was assigned implicitly with random UUID value.
If lock_version attribute is declared it will be incremented. If it's
blank then it will be initialized with 1.
save method call raises Dynamoid::Errors::RecordNotUnique exception
if primary key (hash key + optional range key) already exists in a
table.
save method call raises Dynamoid::Errors::StaleObjectError exception
if there is lock_version attribute and the document in a table was
already changed concurrently and lock_version was consequently
increased.
Raises Dynamoid::Errors::MissingHashKey if a model is already persisted
and a partition key has value nil and raises
Dynamoid::Errors::MissingRangeKey if a sort key is required but has
value nil.
When a table is not created yet the first save method call will create
a table. It's useful in test environment to avoid explicit table
creation.
669 670 671 672 673 674 675 676 677 678 679 680 681 |
# File 'lib/dynamoid/persistence.rb', line 669 def save( = {}) if Dynamoid.config.create_table_on_save self.class.create_table(sync: true) end create_or_update = new_record? ? :create : :update run_callbacks(:save) do run_callbacks(create_or_update) do Save.call(self, touch: [:touch]) end end end |
#save!(options = {}) ⇒ true|false
Create new model or persist changes.
Run the validation and callbacks. Raises
Dynamoid::Errors::DocumentNotValid is validation fails.
user = User.create
user.age = 26
user.save! # => user
Validation can be skipped with validate: false option:
user = User.new(age: -1)
user.save!(validate: false) # => user
If any of the before_* callbacks throws :abort the saving is
cancelled and save! raises Dynamoid::Errors::RecordNotSaved.
save! by default sets timestamps attributes - created_at and
updated_at when creates new model and updates updated_at attribute
when updates already existing one.
Changing updated_at attribute at updating a model can be skipped with
touch: false option:
user.save!(touch: false)
If a model is new and hash key (+id+ by default) is not assigned yet it was assigned implicitly with random UUID value.
If lock_version attribute is declared it will be incremented. If it's
blank then it will be initialized with 1.
save! method call raises Dynamoid::Errors::RecordNotUnique exception
if primary key (hash key + optional range key) already exists in a
table.
save! method call raises Dynamoid::Errors::StaleObjectError exception
if there is lock_version attribute and the document in a table was
already changed concurrently and lock_version was consequently
increased.
save! method call raises Dynamoid::Errors::RecordNotSaved exception
if some callback aborted execution.
Raises Dynamoid::Errors::MissingHashKey if a model is already persisted
and a partition key has value nil and raises
Dynamoid::Errors::MissingRangeKey if a sort key is required but has
value nil.
When a table is not created yet the first save! method call will create
a table. It's useful in test environment to avoid explicit table
creation.
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 |
# File 'lib/dynamoid/persistence.rb', line 741 def save!( = {}) # validation is handled in the Validation module if Dynamoid.config.create_table_on_save self.class.create_table(sync: true) end create_or_update = new_record? ? :create : :update aborted = true run_callbacks(:save) do run_callbacks(create_or_update) do aborted = false Save.call(self, touch: [:touch]) end end if aborted raise Dynamoid::Errors::RecordNotSaved, self end self end |
#touch(*names, time: nil) ⇒ Dynamoid::Document
Update document timestamps.
Set updated_at attribute to current DateTime.
post.touch
Can update other fields in addition with the same timestamp if their names passed as arguments.
user.touch(:last_login_at, :viewed_at)
Some specific value can be used to save:
user.touch(time: 1.hour.ago)
No validation is performed and only after_touch callback is called.
The method must be used on a persisted object, otherwise
Dynamoid::Errors::Error will be thrown.
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 |
# File 'lib/dynamoid/persistence.rb', line 578 def touch(*names, time: nil) if new_record? raise Dynamoid::Errors::Error, 'cannot touch on a new or destroyed record object' end time_to_assign = time || DateTime.now self.updated_at = time_to_assign names.each do |name| attributes[name] = time_to_assign end attribute_names = names.map(&:to_sym) + [:updated_at] attributes_with_values = attributes.slice(*attribute_names) run_callbacks :touch do self.class.update_fields(hash_key, range_value, attributes_with_values) clear_attribute_changes(attribute_names.map(&:to_s)) end self end |
#update(conditions = {}, &block) ⇒ true|false
Update a model.
Doesn't run validation. Runs only update callbacks. Reloads all attribute values.
Accepts mandatory block in order to specify operations which will modify
attributes. Supports following operations: add, delete and set.
Operation add just adds a value for numeric attributes and join
collections if attribute is a set.
user.update do |t|
t.add(age: 1, followers_count: 5)
t.add(hobbies: ['skying', 'climbing'])
end
Operation delete is applied to collection attribute types and
substructs one collection from another.
user.update do |t|
t.delete(hobbies: ['skying'])
end
If it's applied to a scalar attribute then the item's attribute is removed at all:
user.update do |t|
t.delete(age: nil)
end
or even without useless value at all:
user.update do |t|
t.delete(:age)
end
Operation set just changes an attribute value:
user.update do |t|
t.set(age: 21)
end
All the operations works like ADD, DELETE and PUT actions supported
by AttributeUpdates
parameter
of UpdateItem operation.
Can update a model conditionaly:
user.update(if: { age: 20 }) do |t|
t.add(age: 1)
end
To check if some attribute (or attributes) isn't stored in a DynamoDB
item (e.g. it wasn't set explicitly) there is another condition -
unless_exists:
user = User.create(name: 'Tylor')
user.update(unless_exists: [:age]) do |t|
t.set(age: 18)
end
If a document doesn't meet conditions it just returns false. Otherwise it returns true.
It will increment the lock_version attribute if a table has the column,
but will not check it. Thus, a concurrent save call will never cause an
update! to fail, but an update! may cause a concurrent save to
fail.
1054 1055 1056 1057 1058 1059 |
# File 'lib/dynamoid/persistence.rb', line 1054 def update(conditions = {}, &block) update!(conditions, &block) true rescue Dynamoid::Errors::StaleObjectError false end |
#update!(conditions = {}) ⇒ Dynamoid::Document
Update a model.
Doesn't run validation. Runs only update callbacks. Reloads all attribute values.
Accepts mandatory block in order to specify operations which will modify
attributes. Supports following operations: add, delete and set.
Operation add just adds a value for numeric attributes and join
collections if attribute is a set.
user.update! do |t|
t.add(age: 1, followers_count: 5)
t.add(hobbies: ['skying', 'climbing'])
end
Operation delete is applied to collection attribute types and
substructs one collection from another.
user.update! do |t|
t.delete(hobbies: ['skying'])
end
Operation set just changes an attribute value:
user.update! do |t|
t.set(age: 21)
end
All the operations work like ADD, DELETE and PUT actions supported
by AttributeUpdates
parameter
of UpdateItem operation.
It's atomic operations. So adding or deleting elements in a collection or incrementing or decrementing a numeric field is atomic and does not interfere with other write requests.
Can update a model conditionaly:
user.update!(if: { age: 20 }) do |t|
t.add(age: 1)
end
To check if some attribute (or attributes) isn't stored in a DynamoDB
item (e.g. it wasn't set explicitly) there is another condition -
unless_exists:
user = User.create(name: 'Tylor')
user.update!(unless_exists: [:age]) do |t|
t.set(age: 18)
end
If a document doesn't meet conditions it raises
Dynamoid::Errors::StaleObjectError exception.
It will increment the lock_version attribute if a table has the column,
but will not check it. Thus, a concurrent save call will never cause an
update! to fail, but an update! may cause a concurrent save to
fail.
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 |
# File 'lib/dynamoid/persistence.rb', line 925 def update!(conditions = {}) run_callbacks(:update) do = {} if range_key value = read_attribute(range_key) = self.class.attributes[range_key] [:range_key] = Dumping.dump_field(value, ) end begin table_name = self.class.table_name partition_key_dumped = Dumping.dump_field(hash_key, self.class.attributes[self.class.hash_key]) conditions = conditions.dup conditions[:if] ||= {} conditions[:if][self.class.hash_key] = partition_key_dumped if self.class.range_key sort_key_dumped = Dumping.dump_field(range_value, self.class.attributes[self.class.range_key]) conditions[:if][self.class.range_key] = sort_key_dumped end = .merge(conditions: conditions) new_attrs = Dynamoid.adapter.update_item(table_name, partition_key_dumped, ) do |t| item_updater = ItemUpdaterWithDumping.new(self.class, t) item_updater.add(lock_version: 1) if self.class.attributes[:lock_version] if self.class. item_updater.set(updated_at: DateTime.now.in_time_zone(Time.zone)) end yield t end load(Undumping.undump_attributes(new_attrs, self.class.attributes)) rescue Dynamoid::Errors::ConditionalCheckFailedException # exception may be raised either because of failed user provided conditions # or because of conditions on partition and sort keys. We cannot # distinguish these two cases. raise Dynamoid::Errors::StaleObjectError.new(self, 'update') end end self end |
#update_attribute(attribute, value) ⇒ Dynamoid::Document
Update a single attribute, saving the object afterwards.
user.update_attribute(:last_name, 'Tylor')
Validation is skipped.
Raises a Dynamoid::Errors::UnknownAttribute exception if any of the
attributes is not on the model
827 828 829 |
# File 'lib/dynamoid/persistence.rb', line 827 def update_attribute(attribute, value) # final implementation is in the Dynamoid::Validation module end |
#update_attribute!(attribute, value) ⇒ Dynamoid::Document
Update a single attribute, saving the object afterwards.
user.update_attribute!(:last_name, 'Tylor')
Validation is skipped.
If any of the before_* callbacks throws :abort the updating is
cancelled and update_attribute! raises
Dynamoid::Errors::RecordNotSaved.
Raises a Dynamoid::Errors::UnknownAttribute exception if any of the
attributes is not on the model
849 850 851 |
# File 'lib/dynamoid/persistence.rb', line 849 def update_attribute!(attribute, value) # final implementation is in the Dynamoid::Validation module end |
#update_attributes(attributes) ⇒ true|false
Update multiple attributes at once, saving the object once the updates are complete.
Returns true if saving is successful and false
otherwise.
user.update_attributes(age: 27, last_name: 'Tylor')
Raises a Dynamoid::Errors::UnknownAttribute exception if any of the
attributes is not on the model
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and raises Dynamoid::Errors::MissingRangeKey if a sort key is
required but has value nil.
783 784 785 786 |
# File 'lib/dynamoid/persistence.rb', line 783 def update_attributes(attributes) attributes.each { |attribute, value| write_attribute(attribute, value) } save end |
#update_attributes!(attributes) ⇒ Object
Update multiple attributes at once, saving the object once the updates are complete.
user.update_attributes!(age: 27, last_name: 'Tylor')
Raises a Dynamoid::Errors::DocumentNotValid exception if some vaidation
fails.
If any of the before_* callbacks throws :abort the updating is
cancelled and update_attributes! raises
Dynamoid::Errors::RecordNotSaved.
Raises a Dynamoid::Errors::UnknownAttribute exception if any of the
attributes is not on the model
Raises Dynamoid::Errors::MissingHashKey if a partition key has value
nil and raises Dynamoid::Errors::MissingRangeKey if a sort key is
required but has value nil.
808 809 810 811 |
# File 'lib/dynamoid/persistence.rb', line 808 def update_attributes!(attributes) attributes.each { |attribute, value| write_attribute(attribute, value) } save! end |