module OceanDynamo::Persistence

Public Class Methods

included(base) click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 4
def self.included(base)
  base.extend(ClassMethods)
end
new(attrs={}) click to toggle source

Instance variables and methods

Calls superclass method
# File lib/ocean-dynamo/persistence.rb, line 99
def initialize(attrs={})
  @destroyed = false
  @new_record = true
  super
end

Public Instance Methods

_setup_from_dynamo(arg) click to toggle source

Deserialises and assigns all defined attributes. Skips undeclared attributes. Unlike its predecessor, this version never reads anything from DynamoDB, it just processes the results from such reads. Thus, the implementation of consistent reads is up to the caller of this method.

# File lib/ocean-dynamo/persistence.rb, line 275
def _setup_from_dynamo(arg)
  case arg
  when Aws::DynamoDB::Types::GetItemOutput
    raw_attrs = arg.item
  when Hash
    raw_attrs = arg
  else
    raise ArgumentError, "arg must be an Aws::DynamoDB::Types::GetItemOutput or a Hash (was #{arg.class})"
  end
  dynamo_deserialize_attributes(raw_attrs)
  @new_record = false
  self
end
create(options={}) click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 174
def create(options={})
  return false if options[:validate] != false && !valid?(:create)
  run_callbacks :commit do
    run_callbacks :save do
      run_callbacks :create do
        # Default the correct hash key to a UUID
        if self.class.has_belongs_to?
          write_attribute(table_range_key, SecureRandom.uuid) if range_key.blank?
        else
          write_attribute(table_hash_key, SecureRandom.uuid) if hash_key.blank?
        end

        set_timestamps
        dynamo_persist
        true
      end
    end
  end
end
create_or_update(options={}) click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 168
def create_or_update(options={})
  result = new_record? ? create(options) : update(options)
  result != false
end
delete() click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 223
def delete
  if persisted?
    dynamo_delete(lock: lock_attribute)
  end
  freeze
  @destroyed = true
end
destroy() click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 209
def destroy
  run_callbacks :commit do
    run_callbacks :destroy do
      delete
    end
  end
end
destroy!() click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 218
def destroy!
  destroy || raise(RecordNotDestroyed)
end
destroyed?() click to toggle source

def dynamo_schema(*)

super

end

# File lib/ocean-dynamo/persistence.rb, line 111
def destroyed?
  @destroyed
end
new_record?() click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 116
def new_record?
  @new_record
end
persisted?() click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 121
def persisted?
  !(new_record? || destroyed?)
end
reload(**keywords) click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 232
def reload(**keywords)
  new_instance = self.class.find(hash_key, range_key, **keywords)
  assign_attributes(new_instance.attributes)
  self
end
save(options={}) click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 133
def save(options={})
  if perform_validations(options)
    begin
      create_or_update
    rescue RecordInvalid
      false
    end
  else
    false
  end
end
save!(options={}) click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 146
def save!(options={})
  if perform_validations(options)
    options[:validate] = false
    create_or_update(options) || raise(RecordNotSaved)
  else
    raise RecordInvalid.new(self)
  end
end
touch(name=nil) click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 239
def touch(name=nil)
  raise DynamoError, "can not touch on a new record object" unless persisted?
  _late_connect?
  run_callbacks :touch do
    begin
      timestamps = set_timestamps(name)
      return self if timestamps.blank?
      update_expression = []
      expression_attribute_values = {}
      timestamps.each_with_index do |ts, i|
        nomen = ":ts#{i}"
        expression_attribute_values[nomen] = serialize_attribute(ts, read_attribute(ts))
        update_expression << "#{ts} = #{nomen}"
      end
      update_expression = "SET " + update_expression.join(", ")
      options = {
          key: serialized_key_attributes,
          update_expression: update_expression
      }.merge(_handle_locking)
      options[:expression_attribute_values] = (options[:expression_attribute_values] || {}).merge(expression_attribute_values)
      dynamo_table.update_item(options)
    rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException
      write_attribute(lock_attribute, read_attribute(lock_attribute)-1) unless frozen?
      raise OceanDynamo::StaleObjectError.new(self)
    end
    self
  end
end
update(options={}) click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 195
def update(options={})
  return false if options[:validate] != false && !valid?(:update)
  run_callbacks :commit do
    run_callbacks :save do
      run_callbacks :update do
        set_timestamps
        dynamo_persist(lock: lock_attribute)
        true
      end
    end
  end
end
update_attributes(attrs={}, options={}) click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 156
def update_attributes(attrs={}, options={})
  assign_attributes(attrs, options)
  save
end
update_attributes!(attrs={}, options={}) click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 162
def update_attributes!(attrs={}, options={})
  assign_attributes(attrs, options)
  save!
end
valid?(context = nil) click to toggle source
Calls superclass method
# File lib/ocean-dynamo/persistence.rb, line 126
def valid?(context = nil)
  context ||= (new_record? ? :create : :update)
  output = super(context)
  errors.empty? && output
end

Protected Instance Methods

deserialize_attribute(value, metadata, type: metadata[:type]) click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 395
def deserialize_attribute(value, metadata, type: metadata[:type])
  case type
  when :reference
    return value
  when :string
    return "" if value == nil
    value.is_a?(Set) ? value.to_a : value
  when :integer
    return nil if value == nil
    value.is_a?(Set) || value.is_a?(Array) ? value.collect(&:to_i) : value.to_i
  when :float
    return nil if value == nil
    value.is_a?(Set) || value.is_a?(Array) ? value.collect(&:to_f) : value.to_f
  when :boolean
    case value
    when "true"
      true
    when "false"
      false
    else
      nil
    end
  when :datetime
    return nil if value == nil
    Time.zone.at(value.to_i)
  when :serialized
    return nil if value == nil
    JSON.parse(value)
  else
    raise UnsupportedType.new(type.to_s)
  end
end
serialize_attribute(attribute, value, metadata=fields[attribute], target_class: metadata['target_class'], type: metadata['type']) click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 359
def serialize_attribute(attribute, value, metadata=fields[attribute],
                        target_class: metadata['target_class'],         # Remove?
                        type: metadata['type'])
  return nil if value == nil
  case type
  when :reference
    value
  when :string
    return nil if ["", []].include?(value)
    value
  when :integer
    value == [] ? nil : value
  when :float
    value == [] ? nil : value
  when :boolean
    value ? "true" : "false"
  when :datetime
    value.to_i
  when :serialized
    value.to_json
  else
    raise UnsupportedType.new(type.to_s)
  end
end
serialized_attributes() click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 329
def serialized_attributes
  result = Hash.new
  fields.each do |attribute, metadata|
    serialized = serialize_attribute(attribute, read_attribute(attribute), metadata)
    result[attribute] = serialized unless serialized == nil
  end
  result
end
serialized_key_attributes() click to toggle source
# File lib/ocean-dynamo/persistence.rb, line 339
def serialized_key_attributes
  result = Hash.new
  # First the hash key
  attribute = table_hash_key
  metadata = fields[attribute]
  serialized = serialize_attribute(attribute, read_attribute(attribute), metadata)
  raise "Hash key may not be null" if serialized == nil
  result[attribute] = serialized
  # Then the range key, if any
  if table_range_key
    attribute = table_range_key
    metadata = fields[attribute]
    serialized = serialize_attribute(attribute, read_attribute(attribute), metadata)
    raise "Range key may not be null" if serialized == nil
    result[attribute] = serialized
  end
  result
end