module Aws::Record::ItemOperations

Public Class Methods

included(sub_class) click to toggle source

@api private

# File lib/aws-record/record/item_operations.rb, line 19
def self.included(sub_class)
  sub_class.extend(ItemOperationsClassMethods)
end

Public Instance Methods

assign_attributes(opts) click to toggle source

Assigns the attributes provided onto the model.

@example Usage Example

class MyModel
  include Aws::Record
  integer_attr :uuid,   hash_key: true
  string_attr  :name, range_key: true
  integer_attr :age
  float_attr   :height
end

model = MyModel.new(id: 4, name: "John", age: 4, height: 70.5)
model.age    # => 4
model.height # => 70.5
model.save
model.dirty? # => false

model.assign_attributes(age: 5, height: 150.75)
model.age    # => 5
model.height # => 150.75
model.dirty? # => true

@param [Hash] opts

# File lib/aws-record/record/item_operations.rb, line 109
def assign_attributes(opts)
  opts.each do |field, new_value|
    field = field.to_sym
    setter = "#{field}="
    raise ArgumentError.new "Invalid field: #{field} for model" unless respond_to?(setter)
    public_send(setter, new_value)
  end
end
delete!() click to toggle source

Deletes the item instance that matches the key values of this item instance in Amazon DynamoDB. Uses the {docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#delete_item-instance_method Aws::DynamoDB::Client#delete_item} API.

# File lib/aws-record/record/item_operations.rb, line 190
def delete!
  dynamodb_client.delete_item(
    table_name: self.class.table_name,
    key: key_values
  )
  self.instance_variable_get("@data").destroyed = true
end
save(opts = {}) click to toggle source

Saves this instance of an item to Amazon DynamoDB. If this item is “new” as defined by having new or altered key attributes, will attempt a conditional {docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#put_item-instance_method Aws::DynamoDB::Client#put_item} call, which will not overwrite an existing item. If the item only has altered non-key attributes, will perform an {docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#update_item-instance_method Aws::DynamoDB::Client#update_item} call. Uses this item instance's attributes in order to build the request on your behalf.

You can use the :force option to perform a simple put/overwrite without conditional validation or update logic.

@param [Hash] opts @option opts [Boolean] :force if true, will save as a put operation and

overwrite any existing item on the remote end. Otherwise, and by
default, will either perform a conditional put or an update call.

@return false if the record is invalid as defined by an attempt to call

+valid?+ on this item, if that method exists. Otherwise, returns client
call return value.
# File lib/aws-record/record/item_operations.rb, line 76
def save(opts = {})
  if _invalid_record?(opts)
    false
  else
    _perform_save(opts)
  end
end
save!(opts = {}) click to toggle source

Saves this instance of an item to Amazon DynamoDB. If this item is “new” as defined by having new or altered key attributes, will attempt a conditional {docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#put_item-instance_method Aws::DynamoDB::Client#put_item} call, which will not overwrite an existing item. If the item only has altered non-key attributes, will perform an {docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#update_item-instance_method Aws::DynamoDB::Client#update_item} call. Uses this item instance's attributes in order to build the request on your behalf.

You can use the :force option to perform a simple put/overwrite without conditional validation or update logic.

@param [Hash] opts @option opts [Boolean] :force if true, will save as a put operation and

overwrite any existing item on the remote end. Otherwise, and by
default, will either perform a conditional put or an update call.

@raise [Aws::Record::Errors::KeyMissing] if a required key attribute

does not have a value within this item instance.

@raise [Aws::Record::Errors::ConditionalWriteFailed] if a conditional

put fails because the item exists on the remote end.

@raise [Aws::Record::Errors::ValidationError] if the item responds to

+:valid?+ and that call returned false. In such a case, checking root
cause is dependent on the validation library you are using.
# File lib/aws-record/record/item_operations.rb, line 47
def save!(opts = {})
  ret = save(opts)
  if ret
    ret
  else
    raise Errors::ValidationError.new("Validation hook returned false!")
  end
end
update(new_params, opts = {}) click to toggle source

Mass assigns the attributes to the model and then performs a save

You can use the :force option to perform a simple put/overwrite without conditional validation or update logic.

Note that aws-record allows you to change your model's key values, but this will be interpreted as persisting a new item to your DynamoDB table

@example Usage Example

class MyModel
  include Aws::Record
  integer_attr :uuid,   hash_key: true
  string_attr  :name, range_key: true
  integer_attr :age
  float_attr   :height
end

model = MyModel.new(id: 4, name: "John", age: 4, height: 70.5)
model.age    # => 4
model.height # => 70.5
model.save
model.dirty? # => false

model.update(age: 5, height: 150.75)
model.age    # => 5
model.height # => 150.75
model.dirty? # => false

@param [Hash] new_param, contains the new parameters for the model

@param [Hash] opts @option opts [Boolean] :force if true, will save as a put operation and

overwrite any existing item on the remote end. Otherwise, and by
default, will either perform a conditional put or an update call.

@return false if the record is invalid as defined by an attempt to call

+valid?+ on this item, if that method exists. Otherwise, returns client
call return value.
# File lib/aws-record/record/item_operations.rb, line 157
def update(new_params, opts = {})
  assign_attributes(new_params)
  save(opts)
end
update!(new_params, opts = {}) click to toggle source

Updates model attributes and validates new values

You can use the :force option to perform a simple put/overwrite without conditional validation or update logic.

Note that aws-record allows you to change your model's key values, but this will be interpreted as persisting a new item to your DynamoDB table

@param [Hash] new_param, contains the new parameters for the model

@param [Hash] opts @option opts [Boolean] :force if true, will save as a put operation and

overwrite any existing item on the remote end. Otherwise, and by
default, will either perform a conditional put or an update call.

@return The update mode if the update is successful

@raise [Aws::Record::Errors::ValidationError] if any new values

violate the models validations.
# File lib/aws-record/record/item_operations.rb, line 181
def update!(new_params, opts = {})
  assign_attributes(new_params)
  save!(opts)
end

Private Instance Methods

_build_item_for_save() click to toggle source
# File lib/aws-record/record/item_operations.rb, line 262
def _build_item_for_save
  validate_key_values
  @data.populate_default_values
  @data.build_save_hash
end
_dirty_changes_for_update() click to toggle source
# File lib/aws-record/record/item_operations.rb, line 320
def _dirty_changes_for_update
  attributes = self.class.attributes
  ret = dirty.inject({}) do |acc, attr_name|
    acc[attr_name] = @data.raw_value(attr_name)
    acc
  end
  ret
end
_invalid_record?(opts) click to toggle source
# File lib/aws-record/record/item_operations.rb, line 199
def _invalid_record?(opts)
  if self.respond_to?(:valid?)
    if !self.valid?
      true
    else
      false
    end
  else
    false
  end
end
_perform_save(opts) click to toggle source
# File lib/aws-record/record/item_operations.rb, line 211
def _perform_save(opts)
  force = opts[:force]
  expect_new = expect_new_item?
  if force
    dynamodb_client.put_item(
      table_name: self.class.table_name,
      item: _build_item_for_save
    )
  elsif expect_new
    put_opts = {
      table_name: self.class.table_name,
      item: _build_item_for_save
    }.merge(prevent_overwrite_expression)
    begin
      dynamodb_client.put_item(put_opts)
    rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
      raise Errors::ConditionalWriteFailed.new(
        "Conditional #put_item call failed! Check that conditional write"\
          " conditions are met, or include the :force option to clobber"\
          " the remote item."
      )
    end
  else
    update_pairs = _dirty_changes_for_update
    update_tuple = self.class.send(
      :_build_update_expression,
      update_pairs
    )
    if update_tuple
      uex, exp_attr_names, exp_attr_values = update_tuple
      request_opts = {
        table_name: self.class.table_name,
        key: key_values,
        update_expression: uex,
        expression_attribute_names: exp_attr_names,
      }
      request_opts[:expression_attribute_values] = exp_attr_values unless exp_attr_values.empty?
      dynamodb_client.update_item(request_opts)
    else
      dynamodb_client.update_item(
        table_name: self.class.table_name,
        key: key_values
      )
    end
  end
  data = self.instance_variable_get("@data")
  data.destroyed = false
  data.new_record = false
  true
end
expect_new_item?() click to toggle source
# File lib/aws-record/record/item_operations.rb, line 295
def expect_new_item?
  # Algorithm: Are keys dirty? If so, we treat as new.
  self.class.keys.any? do |_, attr_name|
    attribute_dirty?(attr_name)
  end
end
key_values() click to toggle source
# File lib/aws-record/record/item_operations.rb, line 268
def key_values
  validate_key_values
  attributes = self.class.attributes
  self.class.keys.inject({}) do |acc, (_, attr_name)|
    db_name = attributes.storage_name_for(attr_name)
    acc[db_name] = attributes.attribute_for(attr_name).
      serialize(@data.raw_value(attr_name))
    acc
  end
end
missing_key_values() click to toggle source
# File lib/aws-record/record/item_operations.rb, line 288
def missing_key_values
  self.class.keys.inject([]) do |acc, key|
    acc << key.last if @data.raw_value(key.last).nil?
    acc
  end
end
prevent_overwrite_expression() click to toggle source
# File lib/aws-record/record/item_operations.rb, line 302
def prevent_overwrite_expression
  conditions = []
  expression_attribute_names = {}
  keys = self.class.instance_variable_get("@keys")
  # Hash Key
  conditions << "attribute_not_exists(#H)"
  expression_attribute_names["#H"] = keys.hash_key_attribute.database_name
  # Range Key
  if self.class.range_key
    conditions << "attribute_not_exists(#R)"
    expression_attribute_names["#R"] = keys.range_key_attribute.database_name
  end
  {
    condition_expression: conditions.join(" and "),
    expression_attribute_names: expression_attribute_names
  }
end
validate_key_values() click to toggle source
# File lib/aws-record/record/item_operations.rb, line 279
def validate_key_values
  missing = missing_key_values
  unless missing.empty?
    raise Errors::KeyMissing.new(
      "Missing required keys: #{missing.join(', ')}"
    )
  end
end