module Dynamoid::Persistence

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.

Attributes

new_record[RW]
new_record?[RW]

Public Instance Methods

delete() click to toggle source

Delete this object from the datastore.

@since 0.2.0

# File lib/dynamoid/persistence.rb, line 228
def delete
  options = range_key ? {:range_key => dump_field(self.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?
        self.lock_version
      else
        changes[:lock_version][0]
      end
    options[:conditions] = conditions
  end
  Dynamoid.adapter.delete(self.class.table_name, self.hash_key, options)
rescue Dynamoid::Errors::ConditionalCheckFailedException
  raise Dynamoid::Errors::StaleObjectError.new(self, 'delete')
end
destroy() click to toggle source

Delete this object, but only after running callbacks for it.

@since 0.2.0

# File lib/dynamoid/persistence.rb, line 218
def destroy
  run_callbacks(:destroy) do
    self.delete
  end
  self
end
dump() click to toggle source

Dump this object’s attributes into hash form, fit to be persisted into the datastore.

@since 0.2.0

# File lib/dynamoid/persistence.rb, line 250
def dump
  Hash.new.tap do |hash|
    self.class.attributes.each do |attribute, options|
      hash[attribute] = dump_field(self.read_attribute(attribute), options)
    end
  end
end
persisted?() click to toggle source

Is this object persisted in the datastore? Required for some ActiveModel integration stuff.

@since 0.2.0

# File lib/dynamoid/persistence.rb, line 162
def persisted?
  !new_record?
end
save(options = {}) click to toggle source

Run the callbacks and then persist this object in the datastore.

@since 0.2.0

# File lib/dynamoid/persistence.rb, line 169
def save(options = {})
  self.class.create_table

  if new_record?
    conditions = { :unless_exists => [self.class.hash_key]}
    conditions[:unless_exists] << range_key if(range_key)

    run_callbacks(:create) { persist(conditions) }
  else
    persist
  end

  self
end
touch(name = nil) click to toggle source

Set updated_at and any passed in field to current DateTime. Useful for things like last_login_at, etc.

# File lib/dynamoid/persistence.rb, line 152
def touch(name = nil)
  now = DateTime.now
  self.updated_at = now
  attributes[name] = now if name
  save
end
update(conditions = {}, &block) click to toggle source
# File lib/dynamoid/persistence.rb, line 208
def update(conditions = {}, &block)
  update!(conditions, &block)
  true
rescue Dynamoid::Errors::StaleObjectError
  false
end
update!(conditions = {}) { |t| ... } click to toggle source

update!() will increment the lock_version if the table has the column, but will not check it. Thus, a concurrent save will never cause an update! to fail, but an update! may cause a concurrent save to fail.

# File lib/dynamoid/persistence.rb, line 189
def update!(conditions = {}, &block)
  run_callbacks(:update) do
    options = range_key ? {:range_key => dump_field(self.read_attribute(range_key), self.class.attributes[range_key])} : {}

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

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

Private Instance Methods

dump_field(value, options) click to toggle source

Determine how to dump this field. Given a value, it’ll determine how to turn it into a value that can be persisted into the datastore.

@since 0.2.0

# File lib/dynamoid/persistence.rb, line 264
def dump_field(value, options)
  if (field_class = options[:type]).is_a?(Class)
    if value.respond_to?(:dynamoid_dump)
      value.dynamoid_dump
    elsif field_class.respond_to?(:dynamoid_dump)
      field_class.dynamoid_dump(value)
    else
      raise ArgumentError, "Neither #{field_class} nor #{value} support serialization for Dynamoid."
    end
  else
    case options[:type]
      when :string
        !value.nil? ? value.to_s : nil
      when :integer
        !value.nil? ? Integer(value) : nil
      when :number
        !value.nil? ? value : nil
      when :set
        !value.nil? ? Set.new(value) : nil
      when :array
        !value.nil? ? value : nil
      when :datetime
        !value.nil? ? value.to_time.to_f : nil
      when :serialized
        options[:serializer] ? options[:serializer].dump(value) : value.to_yaml
      when :boolean
        !value.nil? ? value.to_s[0] : nil
      else
        raise ArgumentError, "Unknown type #{options[:type]}"
    end
  end
end
persist(conditions = nil) click to toggle source

Persist the object into the datastore. Assign it an id first if it doesn’t have one.

@since 0.2.0

# File lib/dynamoid/persistence.rb, line 300
def persist(conditions = nil)
  run_callbacks(:save) do
    self.hash_key = SecureRandom.uuid if self.hash_key.nil? || self.hash_key.blank?

    # Add an exists check to prevent overwriting existing records with new ones
    if(new_record?)
      conditions ||= {}
      (conditions[:unless_exists] ||= []) << self.class.hash_key
    end

    # Add an optimistic locking check if the lock_version column exists
    if(self.class.attributes[:lock_version])
      conditions ||= {}
      self.lock_version = (lock_version || 0) + 1
      #Uses the original lock_version value from ActiveModel::Dirty in case user changed lock_version manually
      (conditions[:if] ||= {})[:lock_version] = changes[:lock_version][0] if(changes[:lock_version][0])
    end

    begin
      Dynamoid.adapter.write(self.class.table_name, self.dump, conditions)
      @new_record = false
      true
    rescue Dynamoid::Errors::ConditionalCheckFailedException => e
      if new_record?
        raise Dynamoid::Errors::RecordNotUnique.new(e, self)
      else
        raise Dynamoid::Errors::StaleObjectError.new(self, 'persist')
      end
    end
  end
end