module Dynamoid::Persistence::ClassMethods

Public Instance Methods

create(attrs = {}, &block) click to toggle source

Create a model.

Initializes a new model and immediately saves it to DynamoDB.

User.create(first_name: 'Mark', last_name: 'Tyler')

Accepts both Hash and Array of Hashes and can create several models.

User.create([{ first_name: 'Alice' }, { first_name: 'Bob' }])

Creates a model and pass it into a block to set other attributes.

User.create(first_name: 'Mark') do |u|
  u.age = 21
end

Validates model and runs callbacks.

@param attrs [Hash|Array] Attributes of the models @param block [Proc] Block to process a document after initialization @return [Dynamoid::Document] The created document @since 0.2.0

# File lib/dynamoid/persistence.rb, line 189
def create(attrs = {}, &block)
  if attrs.is_a?(Array)
    attrs.map { |attr| create(attr, &block) }
  else
    build(attrs, &block).tap(&:save)
  end
end
create!(attrs = {}, &block) click to toggle source

Create a model.

Initializes a new object and immediately saves it to the Dynamoid. Raises an exception Dynamoid::Errors::DocumentNotValid if validation failed. Accepts both Hash and Array of Hashes and can create several models.

@param attrs [Hash|Array] Attributes with which to create the object. @param block [Proc] Block to process a document after initialization @return [Dynamoid::Document] The created document @since 0.2.0

# File lib/dynamoid/persistence.rb, line 208
def create!(attrs = {}, &block)
  if attrs.is_a?(Array)
    attrs.map { |attr| create!(attr, &block) }
  else
    build(attrs, &block).tap(&:save!)
  end
end
create_table(options = {}) click to toggle source

Create a table.

Uses a configuration specified in a model class (with the table method) e.g. table name, schema (hash and range keys), global and local secondary indexes, billing mode and write/read capacity.

For instance here

class User
  include Dynamoid::Document

  table key: :uuid
  range :last_name

  field :first_name
  field :last_name
end

User.create_table

create_table method call will create a table dynamoid_users with hash key uuid and range key name, DynamoDB default billing mode and Dynamoid default read/write capacity units (100/20).

All the configuration can be overridden with options argument.

User.create_table(table_name: 'users', read_capacity: 200, write_capacity: 40)

Dynamoid creates a table synchronously by default. DynamoDB table creation is an asynchronous operation and a client should wait until a table status changes to ACTIVE and a table becomes available. That’s why Dynamoid is polling a table status and returns results only when a table becomes available.

Polling is configured with Dynamoid::Config.sync_retry_max_times and Dynamoid::Config.sync_retry_wait_seconds configuration options. If table creation takes more time than configured waiting time then Dynamoid stops polling and returns true.

In order to return back asynchronous behaviour and not to wait until a table is created the +sync: false+ option should be specified.

User.create_table(sync: false)

Subsequent method calls for the same table will be ignored.

@param options [Hash]

@option options [Symbol] :table_name name of the table @option options [Symbol] :id hash key name of the table @option options [Symbol] :hash_key_type Dynamoid type of the hash key - :string, :integer or any other scalar type @option options [Hash] :range_key a Hash with range key name and type in format +{ <name> => <type> }+ e.g. +{ last_name: :string }+ @option options [String] :billing_mode billing mode of a table - either PROVISIONED (default) or PAY_PER_REQUEST (for On-Demand Mode) @option options [Integer] :read_capacity read capacity units for the table; does not work on existing tables and is ignored when billing mode is PAY_PER_REQUEST @option options [Integer] :write_capacity write capacity units for the table; does not work on existing tables and is ignored when billing mode is PAY_PER_REQUEST @option options [Hash] :local_secondary_indexes @option options [Hash] :global_secondary_indexes @option options [true|false] :sync specifies should the method call be synchronous and wait until a table is completely created

@return [true|false] Whether a table created successfully @since 0.4.0

# File lib/dynamoid/persistence.rb, line 95
def create_table(options = {})
  range_key_hash = if range_key
                     { range_key => PrimaryKeyTypeMapping.dynamodb_type(attributes[range_key][:type], attributes[range_key]) }
                   end

  options = {
    id: hash_key,
    table_name: table_name,
    billing_mode: capacity_mode,
    write_capacity: write_capacity,
    read_capacity: read_capacity,
    range_key: range_key_hash,
    hash_key_type: PrimaryKeyTypeMapping.dynamodb_type(attributes[hash_key][:type], attributes[hash_key]),
    local_secondary_indexes: local_secondary_indexes.values,
    global_secondary_indexes: global_secondary_indexes.values
  }.merge(options)

  created_successfuly = Dynamoid.adapter.create_table(options[:table_name], options[:id], options)

  if created_successfuly && self.options[:expires]
    attribute = self.options[:expires][:field]
    Dynamoid.adapter.update_time_to_live(options[:table_name], attribute)
  end

  self
end
delete_table() click to toggle source

Deletes the table for the model.

Dynamoid deletes a table asynchronously and doesn’t wait until a table is deleted completely.

Subsequent method calls for the same table will be ignored. @return [Model class] self

# File lib/dynamoid/persistence.rb, line 129
def delete_table
  Dynamoid.adapter.delete_table(table_name)
  self
end
from_database(attrs = {}) click to toggle source

@private

# File lib/dynamoid/persistence.rb, line 135
def from_database(attrs = {})
  klass = choose_right_class(attrs)
  attrs_undumped = Undumping.undump_attributes(attrs, klass.attributes)
  klass.new(attrs_undumped).tap { |r| r.new_record = false }
end
import(array_of_attributes) click to toggle source

Create several models at once.

users = User.import([{ name: 'a' }, { name: 'b' }])

import is a relatively low-level method and bypasses some mechanisms like callbacks and validation.

It sets timestamp fields created_at and updated_at if they are blank. It sets a hash key field as well if it’s blank. It expects that the hash key field is string and sets a random UUID value if the field value is blank. All the field values are type casted to the declared types.

It works efficiently and uses the ‘BatchWriteItem` operation. In order to cope with throttling it uses a backoff strategy if it’s specified with ‘Dynamoid::Config.backoff` configuration option.

Because of the nature of DynamoDB and its limits only 25 models can be saved at once. So multiple HTTP requests can be sent to DynamoDB.

@param array_of_attributes [Array<Hash>] @return [Array] Created models

# File lib/dynamoid/persistence.rb, line 163
def import(array_of_attributes)
  Import.call(self, array_of_attributes)
end
inc(hash_key_value, range_key_value = nil, counters) click to toggle source

Increase a numeric field by specified value.

User.inc('1', age: 2)

Can update several fields at once.

User.inc('1', age: 2, version: 1)

If range key is declared for a model it should be passed as well:

User.inc('1', 'Tylor', age: 2)

It’s an atomic operation it does not interfere with other write requests.

Uses efficient low-level UpdateItem operation and does only one HTTP request.

Doesn’t run validations and callbacks. Doesn’t update created_at and updated_at as well.

When ‘:touch` option is passed the timestamp columns are updating. If attribute names are passed, they are updated along with updated_at attribute:

User.inc('1', age: 2, touch: true)
User.inc('1', age: 2, touch: :viewed_at)
User.inc('1', age: 2, touch: [:viewed_at, :accessed_at])

@param hash_key_value [Scalar value] hash key @param range_key_value [Scalar value] range key (optional) @param counters [Hash] value to increase by @option counters [true | Symbol | Array] :touch to update update_at attribute and optionally the specified ones @return [Model class] self

# File lib/dynamoid/persistence.rb, line 395
def inc(hash_key_value, range_key_value = nil, counters)
  Inc.call(self, hash_key_value, range_key_value, counters)
  self
end
table_name() click to toggle source
# File lib/dynamoid/persistence.rb, line 28
def table_name
  table_base_name = options[:name] || base_class.name.split('::').last.downcase.pluralize

  @table_name ||= [Dynamoid::Config.namespace.to_s, table_base_name].reject(&:empty?).join('_')
end
update(hash_key, range_key_value = nil, attrs) click to toggle source

Update document with provided attributes.

Instantiates document and saves changes. Runs validations and callbacks. Don’t save changes if validation fails.

User.update('1', age: 26)

If range key is declared for a model it should be passed as well:

User.update('1', 'Tylor', age: 26)

@param hash_key [Scalar value] hash key @param range_key_value [Scalar value] range key (optional) @param attrs [Hash] @return [Dynamoid::Document] Updated document

# File lib/dynamoid/persistence.rb, line 231
def update(hash_key, range_key_value = nil, attrs)
  model = find(hash_key, range_key: range_key_value, consistent_read: true)
  model.update_attributes(attrs)
  model
end
update!(hash_key, range_key_value = nil, attrs) click to toggle source

Update document with provided attributes.

Instantiates document and saves changes. Runs validations and callbacks.

User.update!('1', age: 26)

If range key is declared for a model it should be passed as well:

User.update('1', 'Tylor', age: 26)

Raises Dynamoid::Errors::DocumentNotValid exception if validation fails.

@param hash_key [Scalar value] hash key @param range_key_value [Scalar value] range key (optional) @param attrs [Hash] @return [Dynamoid::Document] Updated document

# File lib/dynamoid/persistence.rb, line 254
def update!(hash_key, range_key_value = nil, attrs)
  model = find(hash_key, range_key: range_key_value, consistent_read: true)
  model.update_attributes!(attrs)
  model
end
update_fields(hash_key_value, range_key_value = nil, attrs = {}, conditions = {}) click to toggle source

Update document.

Doesn’t run validations and callbacks.

User.update_fields('1', age: 26)

If range key is declared for a model it should be passed as well:

User.update_fields('1', 'Tylor', age: 26)

Can make a conditional update so a document will be updated only if it meets the specified conditions. Conditions can be specified as a Hash with :if key:

User.update_fields('1', { age: 26 }, if: { version: 1 })

Here User model has an integer version field and the document will be updated only if the version attribute currently has value 1.

If a document with specified hash and range keys doesn’t exist or conditions were specified and failed the method call returns nil.

update_fields uses the UpdateItem operation so it saves changes and loads an updated document back with one HTTP request.

Raises a Dynamoid::Errors::UnknownAttribute exception if any of the attributes is not on the model

@param hash_key_value [Scalar value] hash key @param range_key_value [Scalar value] range key (optional) @param attrs [Hash] @param conditions [Hash] (optional) @return [Dynamoid::Document|nil] Updated document

# File lib/dynamoid/persistence.rb, line 293
def update_fields(hash_key_value, range_key_value = nil, attrs = {}, conditions = {})
  optional_params = [range_key_value, attrs, conditions].compact
  if optional_params.first.is_a?(Hash)
    range_key_value = nil
    attrs, conditions = optional_params[0..1]
  else
    range_key_value = optional_params.first
    attrs, conditions = optional_params[1..2]
  end

  UpdateFields.call(self,
                    partition_key: hash_key_value,
                    sort_key: range_key_value,
                    attributes: attrs,
                    conditions: conditions)
end
upsert(hash_key_value, range_key_value = nil, attrs = {}, conditions = {}) click to toggle source

Update an existing document or create a new one.

If a document with specified hash and range keys doesn’t exist it creates a new document with specified attributes. Doesn’t run validations and callbacks.

User.upsert('1', age: 26)

If range key is declared for a model it should be passed as well:

User.upsert('1', 'Tylor', age: 26)

Can make a conditional update so a document will be updated only if it meets the specified conditions. Conditions can be specified as a Hash with :if key:

User.upsert('1', { age: 26 }, if: { version: 1 })

Here User model has an integer version field and the document will be updated only if the version attribute currently has value 1.

If conditions were specified and failed the method call returns nil.

upsert uses the UpdateItem operation so it saves changes and loads an updated document back with one HTTP request.

Raises a Dynamoid::Errors::UnknownAttribute exception if any of the attributes is not on the model

@param hash_key_value [Scalar value] hash key @param range_key_value [Scalar value] range key (optional) @param attrs [Hash] @param conditions [Hash] (optional) @return [Dynamoid::Document|nil] Updated document

# File lib/dynamoid/persistence.rb, line 344
def upsert(hash_key_value, range_key_value = nil, attrs = {}, conditions = {})
  optional_params = [range_key_value, attrs, conditions].compact
  if optional_params.first.is_a?(Hash)
    range_key_value = nil
    attrs, conditions = optional_params[0..1]
  else
    range_key_value = optional_params.first
    attrs, conditions = optional_params[1..2]
  end

  Upsert.call(self,
              partition_key: hash_key_value,
              sort_key: range_key_value,
              attributes: attrs,
              conditions: conditions)
end