Module: Dynamoid::Persistence

Extended by:
ActiveSupport::Concern
Included in:
Components
Defined in:
lib/dynamoid/persistence.rb,
lib/dynamoid/persistence/save.rb,
lib/dynamoid/persistence/import.rb,
lib/dynamoid/persistence/upsert.rb,
lib/dynamoid/persistence/update_fields.rb

Overview

# Persistence is responsible for dumping objects to and marshalling objects from the datastore. 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 Classes: Import, Save, UpdateFields, Upsert

Constant Summary collapse

UNIX_EPOCH_DATE =
Date.new(1970, 1, 1).freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#new_recordObject Also known as: new_record?

Returns the value of attribute new_record.



19
20
21
# File 'lib/dynamoid/persistence.rb', line 19

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)

Parameters:

  • attribute (Symbol)

    attribute name

  • by (Numeric) (defaults to: 1)

    value to subtract (optional)

Returns:



704
705
706
707
708
# File 'lib/dynamoid/persistence.rb', line 704

def decrement(attribute, by = 1)
  self[attribute] ||= 0
  self[attribute] -= by
  self
end

#decrement!(attribute, by = 1) ⇒ true|false

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)

Returns true if a model was saved and false otherwise.

Parameters:

  • attribute (Symbol)

    attribute name

  • by (Numeric) (defaults to: 1)

    value to subtract (optional)

Returns:

  • (true|false)

    whether saved model successfully



723
724
725
726
# File 'lib/dynamoid/persistence.rb', line 723

def decrement!(attribute, by = 1)
  decrement(attribute, by)
  save
end

#deleteObject

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.

Since:

  • 0.2.0



771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
# File 'lib/dynamoid/persistence.rb', line 771

def delete
  options = range_key ? { range_key: Dumping.dump_field(read_attribute(range_key), self.class.attributes[range_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
    options[:conditions] = conditions
  end

  @destroyed = true

  Dynamoid.adapter.delete(self.class.table_name, hash_key, options)
rescue Dynamoid::Errors::ConditionalCheckFailedException
  raise Dynamoid::Errors::StaleObjectError.new(self, 'delete')
end

#destroytrue|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 true if deleted successfully and false otherwise.

Returns:

  • (true|false)

    whether deleted successfully

Since:

  • 0.2.0



739
740
741
742
743
744
745
746
747
# File 'lib/dynamoid/persistence.rb', line 739

def destroy
  ret = run_callbacks(:destroy) do
    delete
  end

  @destroyed = true

  ret == false ? false : self
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.



758
759
760
# File 'lib/dynamoid/persistence.rb', line 758

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)

Parameters:

  • attribute (Symbol)

    attribute name

  • by (Numeric) (defaults to: 1)

    value to add (optional)

Returns:



669
670
671
672
673
# File 'lib/dynamoid/persistence.rb', line 669

def increment(attribute, by = 1)
  self[attribute] ||= 0
  self[attribute] += by
  self
end

#increment!(attribute, by = 1) ⇒ true|false

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)

Returns true if a model was saved and false otherwise.

Parameters:

  • attribute (Symbol)

    attribute name

  • by (Numeric) (defaults to: 1)

    value to add (optional)

Returns:

  • (true|false)

    whether saved model successfully



688
689
690
691
# File 'lib/dynamoid/persistence.rb', line 688

def increment!(attribute, by = 1)
  increment(attribute, by)
  save
end

#persisted?true|false

Is this object persisted in DynamoDB?

user = User.new
user.persisted? # => false

user.save
user.persisted? # => true

Returns:

  • (true|false)

Since:

  • 0.2.0



418
419
420
# File 'lib/dynamoid/persistence.rb', line 418

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 update 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.

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.

Parameters:

  • options (Hash) (defaults to: {})

    (optional)

Options Hash (options):

  • :validate (true|false)

    validate a model or not - true by default (optional)

  • :touch (true|false)

    update tiemstamps fields or not - true by default (optional)

Returns:

  • (true|false)

    Whether saving successful or not

Since:

  • 0.2.0



470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/dynamoid/persistence.rb', line 470

def save(options = {})
  self.class.create_table(sync: true)

  @_touch_record = options[:touch]

  if new_record?
    run_callbacks(:create) do
      run_callbacks(:save) do
        Save.call(self)
      end
    end
  else
    run_callbacks(:save) do
      Save.call(self)
    end
  end
end

#touch(name = nil) ⇒ Object

Update document timestamps.

Set updated_at attribute to current DateTime.

post.touch

Can update another field in addition with the same timestamp if it’s name passed as argument.

user.touch(:last_login_at)

Parameters:

  • name (Symbol) (defaults to: nil)

    attribute name to update (optional)



401
402
403
404
405
406
# File 'lib/dynamoid/persistence.rb', line 401

def touch(name = nil)
  now = DateTime.now
  self.updated_at = now
  attributes[name] = now if name
  save
end

#update(conditions = {}, &block) ⇒ Object

Update a model.

Runs validation and 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 collection (one of array, set or map).

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 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

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.

Parameters:

  • conditions (Hash) (defaults to: {})

    Conditions on model attributes to make a conditional update (optional)



651
652
653
654
655
656
# File 'lib/dynamoid/persistence.rb', line 651

def update(conditions = {}, &block)
  update!(conditions, &block)
  true
rescue Dynamoid::Errors::StaleObjectError
  false
end

#update!(conditions = {}) ⇒ Object

Update a model.

Runs validation and 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 collection (one of array, set or map).

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 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

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.

Parameters:

  • conditions (Hash) (defaults to: {})

    Conditions on model attributes to make a conditional update (optional)



580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
# File 'lib/dynamoid/persistence.rb', line 580

def update!(conditions = {})
  run_callbacks(:update) do
    options = range_key ? { range_key: Dumping.dump_field(read_attribute(range_key), self.class.attributes[range_key]) } : {}

    begin
      new_attrs = Dynamoid.adapter.update_item(self.class.table_name, hash_key, options.merge(conditions: conditions)) do |t|
        t.add(lock_version: 1) if self.class.attributes[:lock_version]

        if Dynamoid::Config.timestamps
          time_now = DateTime.now.in_time_zone(Time.zone)
          time_now_dumped = Dumping.dump_field(time_now, self.class.attributes[:updated_at])
          t.set(updated_at: time_now_dumped)
        end

        yield t
      end
      load(Undumping.undump_attributes(new_attrs, self.class.attributes))
    rescue Dynamoid::Errors::ConditionalCheckFailedException
      raise Dynamoid::Errors::StaleObjectError.new(self, 'update')
    end
  end
end

#update_attribute(attribute, value) ⇒ Dynamoid::Document

Update a single attribute, saving the object afterwards.

Returns true if saving is successful and false otherwise.

user.update_attribute(:last_name, 'Tylor')

Parameters:

  • attribute (Symbol)

    attribute name to update

  • value (Object)

    the value to assign it

Returns:

Since:

  • 0.2.0



526
527
528
529
# File 'lib/dynamoid/persistence.rb', line 526

def update_attribute(attribute, value)
  write_attribute(attribute, value)
  save
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')

Parameters:

  • attributes (Hash)

    a hash of attributes to update

Returns:

  • (true|false)

    Whether updating successful or not

Since:

  • 0.2.0



497
498
499
500
# File 'lib/dynamoid/persistence.rb', line 497

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.

Parameters:

  • attributes (Hash)

    a hash of attributes to update



511
512
513
514
# File 'lib/dynamoid/persistence.rb', line 511

def update_attributes!(attributes)
  attributes.each { |attribute, value| write_attribute(attribute, value) }
  save!
end