class AWS::Record::HashModel

Public Class Methods

inherited(sub_class) click to toggle source
# File lib/aws/record/hash_model.rb, line 24
def self.inherited(sub_class)
  sub_class.string_attr :id,
    :hash_key => true,
    :default_hash_key_attribute => true
end

Public Instance Methods

all(options = {}) click to toggle source

Returns an enumerable scope object represents all records.

Book.all.each do |book|
  # ...
end

This method is equivalent to `find(:all)`, and therefore you can also pass aditional options.

Book.all(:where => { :author' => 'me' }).each do |my_book|
  # ...
end

@return [Scope] Returns an enumerable scope object.

# File lib/aws/record/hash_model/finder_methods.rb, line 109
def all options = {}
  new_scope.find(:all, options)
end
binary_attr(name, options = {}) click to toggle source

Adds a DynamoDB binary attribute to this class. A binary attribute acts the same as a string attribute, except

@param [Symbol] name The name of the attribute.

@param [Hash] options

@option options [Boolean] :set (false) When true this attribute

can have multiple values.

@note This should not be used for large objects.

# File lib/aws/record/hash_model/attributes.rb, line 169
def binary_attr name, options = {}
end
boolean_attr(name, options = {}) click to toggle source

Adds a boolean attribute to this class.

@example

class Book < AWS::Record::HashModel
  boolean_attr :read
end

b = Book.new
b.read? # => false
b.read = true
b.read? # => true

listing = Listing.new(:score => '123.456'
listing.score # => 123.456

@param [Symbol] name The name of the attribute.

# File lib/aws/record/hash_model/attributes.rb, line 98
def boolean_attr name, options = {}

  attr = add_attribute(Attributes::BooleanAttr.new(name, options))

  # add the boolean question mark method
  define_method("#{attr.name}?") do
    !!__send__(attr.name)
  end

end
count(options = {}) click to toggle source

Counts records Amazon DynamoDB.

class Product < AWS::Record::HashModel
end

# returns the count of records in the 'Product' table
Product.count

You can specify the table via shard

# returns the count of records in the 'products-1' table
Product.shard('products-1').count

You can also specify the shard as an option to count.

Product.count(:shard => 'table-name')

Chaining count with limit has no effect on the count.

Product.limit(10).count # same as Product.count, limit ignored

@param [Hash] options

@option [String] :shard Which shard to count records in.

@return [Integer] The count of records in the table.

# File lib/aws/record/hash_model/finder_methods.rb, line 145
def count options = {}
  new_scope.count(options)
end
create_table(read_capacity_units, write_capacity_units, options = {}) click to toggle source

Creates the DynamoDB table that is configured for this class.

class Product < AWS::Record::HashModel
end

# create the table 'Product' with 10 read/write capacity units
Product.create_table 10, 10

If you shard you data across multiple tables, you can specify the shard name:

# create two tables, with the given names
Product.create_table 500, 10, :shard_name => 'products-1'
Product.create_table 500, 10, :shard_name => 'products-2'

If you share a single AWS account with multiple applications, you can provide a table prefix to group tables and to avoid name collisions:

AWS::Record.table_prefix = 'myapp-'

# creates the table 'myapp-Product'
Product.create_table 250, 50

# creates the table 'myapp-products-1'
Product.create_table 250, 50, :shard_name => 'products-1'

@param [Integer] read_capacity_units

See {DynamoDB::TableCollection#create} for more information.

@param [Integer] write_capacity_units

See {DynamoDB::TableCollection#create} for more information.

@param [Hash] options

@option options [String] :shard_name Defaults to the class name. The

shard name will be prefixed with {AWS::Record.table_prefix},
and that becomes the table name.

@return [DynamoDB::Table]

# File lib/aws/record/hash_model.rb, line 78
def create_table read_capacity_units, write_capacity_units, options = {}

  table_name = dynamo_db_table_name(options[:shard_name])

  create_opts = {}
  create_opts[:hash_key] = { hash_key => :string }

  dynamo_db.tables.create(
    table_name,
    read_capacity_units,
    write_capacity_units,
    create_opts)

end
date_attr(name, options = {}) click to toggle source

Adds a date attribute to this class.

@example A standard date attribute

class Person < AWS::Record::HashModel
  date_attr :birthdate
end

baby = Person.new
baby.birthdate = Time.now
baby.birthdate #=> <Date: ....>

@param [Symbol] name The name of the attribute.

@param [Hash] options

@option options [Boolean] :set (false) When true this attribute

can have multiple dates.
# File lib/aws/record/hash_model/attributes.rb, line 153
def date_attr name, options = {}
  add_attribute(Attributes::DateAttr.new(name, options))
end
datetime_attr(name, options = {}) click to toggle source

Adds a datetime attribute to this class.

@example A standard datetime attribute

class Recipe < AWS::Record::HashModel
  datetime_attr :invented
end

recipe = Recipe.new(:invented => Time.now)
recipe.invented #=> <DateTime ...>

If you add a #datetime_attr for `:created_at` and/or `:updated_at` those will be automanaged.

@param [Symbol] name The name of the attribute.

@param [Hash] options

@option options [Boolean] :set (false) When true this attribute

can have multiple date times.
# File lib/aws/record/hash_model/attributes.rb, line 130
def datetime_attr name, options = {}
  add_attribute(Attributes::DateTimeAttr.new(name, options))
end
dynamo_db_table(shard_name = nil) click to toggle source

@return [DynamoDB::Table] @api private

# File lib/aws/record/hash_model.rb, line 95
def dynamo_db_table shard_name = nil
  table = dynamo_db.tables[dynamo_db_table_name(shard_name)]
  table.hash_key = [hash_key, :string]
  table
end
each(&block) click to toggle source

Yields once for each record.

# File lib/aws/record/hash_model/finder_methods.rb, line 114
def each &block
  all.each(&block)
end
find(*args) click to toggle source

Finds records in Amazon DynamoDB and returns them as objects of the current class.

Finding `:all` returns an enumerable scope object

People.find(:all, :limit => 10).each do |person|
  puts person.name
end

Finding `:first` returns a single record (or nil)

boss = People.find(:first)

Find accepts a hash of find modifiers (`:shard` and `:limit`). You can also choose to omit these modifiers and chain them on the scope object returned. In the following example only one request is made to SimpleDB (when each is called)

people = People.find(:all, :limit => 10)

people = people.limit(10).find(:all)

@overload find(id)

@param id The record to find, raises an exception if the record is
  not found.

@overload find(mode, options = {})

@param [:all,:first] mode (:all) When finding `:all` matching records
  and array is returned of records.  When finding `:first` then
  `nil` or a single record will be returned.
@param [Hash] options
@option options [Integer] :shard The shard name of the Amazon
  DynamoDB table to search.
@option options [Integer] :limit The max number of records to fetch.
# File lib/aws/record/hash_model/finder_methods.rb, line 76
def find *args
  new_scope.find(*args)
end
find_by_id(id, options = {}) click to toggle source

@param [String] id The id of the record to load. @param [Hash] options @option options [String] :shard Specifies what shard (i.e. table)

should be searched.

@raise [RecordNotFound] Raises a record not found exception if there

was no data found for the given id.

@return [Record::HashModel] Returns the record with the given id.

# File lib/aws/record/hash_model/finder_methods.rb, line 26
def find_by_id id, options = {}

  table = dynamo_db_table(options[:shard])

  data = table.items[id].attributes.to_h

  raise RecordNotFound, "no data found for id: #{id}" if data.empty?

  obj = self.new(:shard => table)
  obj.send(:hydrate, id, data)
  obj

end
first(options = {}) click to toggle source

@return [Object,nil] Returns the first record found. If there were

no records found, nil is returned.
# File lib/aws/record/hash_model/finder_methods.rb, line 152
def first options = {}
  new_scope.first(options)
end
float_attr(name, options = {}) click to toggle source

Adds a float attribute to this class.

class Listing < AWS::Record::HashModel
  float_attr :score
end

listing = Listing.new(:score => '123.456')
listing.score # => 123.456

@param [Symbol] name The name of the attribute. @param [Hash] options @option options [Boolean] :set (false) When true this attribute

can have multiple values.
# File lib/aws/record/hash_model/attributes.rb, line 77
def float_attr name, options = {}
  add_attribute(Attributes::FloatAttr.new(name, options))
end
hash_key() click to toggle source

@return [String,nil]

# File lib/aws/record/hash_model.rb, line 31
def hash_key
  self[self.class.hash_key]
end
hash_key_attribute() click to toggle source

@api private

# File lib/aws/record/hash_model.rb, line 107
def hash_key_attribute
  @hash_key_attribute
end
integer_attr(name, options = {}) click to toggle source

Adds an integer attribute to this class.

class Recipe < AWS::Record::HashModel
  integer_attr :servings
end

recipe = Recipe.new(:servings => '10')
recipe.servings #=> 10

@param [Symbol] name The name of the attribute. @param [Hash] options @option options [Boolean] :set (false) When true this attribute

can have multiple values.
# File lib/aws/record/hash_model/attributes.rb, line 60
def integer_attr name, options = {}
  add_attribute(Attributes::IntegerAttr.new(name, options))
end
limit(limit) click to toggle source

The maximum number of records to return. By default, all records matching the where conditions will be returned from a find.

People.limit(10).each {|person| ... }

Limit can be chained with other scope modifiers:

People.where(:age => 40).limit(10).each {|person| ... }
# File lib/aws/record/hash_model/finder_methods.rb, line 165
def limit limit
  new_scope.limit(limit)
end
shard(shard_name) click to toggle source

Returns a chainable scope object that restricts further scopes to a particular table.

Book.shard('books-2').each do |book|
  # ...
end

@param [String] shard_name @return [Scope] Returns a scope for restricting the table searched.

# File lib/aws/record/hash_model/finder_methods.rb, line 89
def shard shard_name
  new_scope.shard(shard_name)
end
string_attr(name, options = {}) click to toggle source

Adds a string attribute to this class.

@example A standard string attribute

class Recipe < AWS::Record::HashModel
  string_attr :name
end

recipe = Recipe.new(:name => "Buttermilk Pancakes")
recipe.name #=> 'Buttermilk Pancakes'

@example A string attribute with `:set` set to true

class Recipe < AWS::Record::HashModel
  string_attr :tags, :set => true
end

recipe = Recipe.new(:tags => %w(popular dessert))
recipe.tags #=> #<Set: {"popular", "desert"}>

@param [Symbol] name The name of the attribute. @param [Hash] options @option options [Boolean] :set (false) When true this attribute

can have multiple values.
# File lib/aws/record/hash_model/attributes.rb, line 43
def string_attr name, options = {}
  add_attribute(Attributes::StringAttr.new(name, options))
end
timestamps() click to toggle source

A convenience method for adding the standard two datetime attributes `:created_at` and `:updated_at`.

@example

class Recipe < AWS::Record::HashModel
  timestamps
end

recipe = Recipe.new
recipe.save
recipe.created_at #=> <DateTime ...>
recipe.updated_at #=> <DateTime ...>
# File lib/aws/record/hash_model/attributes.rb, line 186
def timestamps
  c = datetime_attr :created_at
  u = datetime_attr :updated_at
  [c, u]
end

Private Instance Methods

add_attribute(attribute) click to toggle source
Calls superclass method
# File lib/aws/record/hash_model.rb, line 131
def add_attribute(attribute)
  set_hash_key_attribute(attribute) if attribute.options[:hash_key]
  super(attribute)
end
create_storage() click to toggle source
# File lib/aws/record/hash_model.rb, line 163
def create_storage
  dynamo_db_table.items.create(serialize_attributes, opt_lock_conditions)
end
delete_storage() click to toggle source
# File lib/aws/record/hash_model.rb, line 185
def delete_storage
  dynamo_db_item.delete(opt_lock_conditions)
end
deserialize_item_data(data) click to toggle source
# File lib/aws/record/hash_model.rb, line 190
def deserialize_item_data data
  data.inject({}) do |hash,(attr_name,value)|
    if attribute = self.class.attributes[attr_name]
      hash[attr_name] = value.is_a?(Set) ?
        value.map{|v| attribute.deserialize(v) } :
        attribute.deserialize(value)
    end
    hash
  end
end
dynamo_db() click to toggle source
# File lib/aws/record/hash_model.rb, line 127
def dynamo_db
  AWS::DynamoDB.new
end
dynamo_db_item() click to toggle source

@return [DynamoDB::Item] Returns a reference to the item as stored in

simple db.

@api private

# File lib/aws/record/hash_model.rb, line 151
def dynamo_db_item
  dynamo_db_table.items[hash_key]
end
dynamo_db_table_name(shard_name = nil) click to toggle source
# File lib/aws/record/hash_model.rb, line 123
def dynamo_db_table_name shard_name = nil
  "#{Record.table_prefix}#{self.shard_name(shard_name)}"
end
populate_id() click to toggle source
# File lib/aws/record/hash_model.rb, line 140
def populate_id
  hash_key = self.class.hash_key_attribute
  if hash_key.options[:default_hash_key_attribute]
    self[hash_key.name] = SecureRandom.uuid
  end
end
set_hash_key_attribute(attribute) click to toggle source
# File lib/aws/record/hash_model.rb, line 113
def set_hash_key_attribute(attribute)
  if
    hash_key_attribute and
    hash_key_attribute.options[:default_hash_key_attribute]
  then
    remove_attribute(hash_key_attribute)
  end
  @hash_key_attribute = attribute
end
update_storage() click to toggle source
# File lib/aws/record/hash_model.rb, line 168
def update_storage
  # Only enumerating dirty (i.e. changed) attributes.  Empty
  # (nil and empty set) values are deleted, the others are replaced.
  dynamo_db_item.attributes.update(opt_lock_conditions) do |u|
    changed.each do |attr_name|
      attribute = self.class.attribute_for(attr_name)
      value = serialize_attribute(attribute, @_data[attr_name])
      if value.nil? or value == []
        u.delete(attr_name)
      else
        u.set(attr_name => value)
      end
    end
  end
end