module AWS::Record

AWS::Record is an ORM built on top of AWS services.

Constants

Base

An ActiveRecord-like interface built ontop of Amazon SimpleDB.

class Book < AWS::Record::Model

  string_attr :title
  string_attr :author
  integer_attr :number_of_pages

  timestamps # adds a :created_at and :updated_at pair of timestamps

end

b = Book.new(:title => 'My Book', :author => 'Me', :pages => 1)
b.save

Attribute Macros

When extending AWS::Record::Model you should first consider what attributes your class should have. Unlike ActiveRecord, AWS::Record models are not backed by a database table/schema. You must choose what attributes (and what types) you need.

  • string_attr

  • boolean_attr

  • integer_attr

  • float_attr

  • datetime_attr

  • date_attr

Usage

Normally you just call these methods inside your model class definition:

class Book < AWS::Record::Model
  string_attr :title
  boolean_attr :has_been_read
  integer_attr :number_of_pages
  float_attr :weight_in_pounds
  datetime_attr :published_at
end

For each attribute macro a pair of setter/getter methods are added # to your class (and a few other useful methods).

b = Book.new
b.title = "My Book"
b.has_been_read = true
b.number_of_pages = 1000
b.weight_in_pounds = 1.1
b.published_at = Time.now
b.save

b.id #=> "0aa894ca-8223-4d34-831e-e5134b2bb71c"
b.attributes
#=> { 'title' => 'My Book', 'has_been_read' => true, ... }

Default Values

All attribute macros accept the :default_value option. This sets a value that is populated onto all new instnaces of the class.

class Book < AWS::Record::Model
  string_attr :author, :default_value => 'Me'
end

Book.new.author #=> 'Me'

Multi-Valued (Set) Attributes

AWS::Record permits storing multiple values with a single attribute.

class Book < AWS::Record::Model
  string_attr :tags, :set => true
end

b = Book.new
b.tags #=> #<Set: {}>

b.tags = ['fiction', 'fantasy']
b.tags #=> #<Set: {'fiction', 'fantasy'}>

These multi-valued attributes are treated as sets, not arrays. This means:

  • values are unordered

  • duplicate values are automatically omitted

Please consider these limitations when you choose to use the :set option with the attribute macros.

Validations

It's important to validate models before there are persisted to keep your data clean. AWS::Record supports most of the ActiveRecord style validators.

class Book < AWS::Record::Model
  string_attr :title
  validates_presence_of :title
end

b = Book.new
b.valid? #=> false
b.errors.full_messages #=> ['Title may not be blank']

Validations are checked before saving a record. If any of the validators adds an error, the the save will fail.

For more information about the available validation methods see {Validations}.

Finder Methods

You can find records by their ID. Each record gets a UUID when it is saved for the first time. You can use this ID to fetch the record at a latter time:

b = Book["0aa894ca-8223-4d34-831e-e5134b2bb71c"]

b = Book.find("0aa894ca-8223-4d34-831e-e5134b2bb71c")

If you try to find a record by ID that has no data an error will be raised.

All

You can enumerate all of your records using all.

Book.all.each do |book|
  puts book.id
end

Book.find(:all) do |book|
  puts book.id
end

Be careful when enumerating all. Depending on the number of records and number of attributes each record has, this can take a while, causing quite a few requests.

First

If you only want a single record, you should use first.

b = Book.first

Modifiers

Frequently you do not want ALL records or the very first record. You can pass options to find, all and first.

my_books = Book.find(:all, :where => 'owner = "Me"')

book = Book.first(:where => { :has_been_read => false })

You can pass as find options:

  • :where - Conditions that must be met to be returned

  • :order - The order to sort matched records by

  • :limit - The maximum number of records to return

Scopes

More useful than writing query fragments all over the place is to name your most common conditions for reuse.

class Book < AWS::Record::Model

  scope :mine, where(:owner => 'Me')

  scope :unread, where(:has_been_read => false)

  scope :by_popularity, order(:score, :desc)

  scope :top_10, by_popularity.limit(10)

end

# The following expression returns 10 books that belong
# to me, that are unread sorted by popularity.
next_good_reads = Book.mine.unread.top_10

There are 3 standard scope methods:

  • where

  • order

  • limit

Conditions (where)

Where accepts aruments in a number of forms:

  1. As an sql-like fragment. If you need to escape values this form is not suggested.

    Book.where('title = "My Book"')
  2. An sql-like fragment, with placeholders. This escapes quoted arguments properly to avoid injection.

    Book.where('title = ?', 'My Book')
  3. A hash of key-value pairs. This is the simplest form, but also the least flexible. You can not use this form if you need more complex expressions that use or.

    Book.where(:title => 'My Book')
    

Order

This orders the records as returned by AWS. Default ordering is ascending. Pass the value :desc as a second argument to sort in reverse ordering.

Book.order(:title)        # alphabetical ordering 
Book.order(:title, :desc) # reverse alphabetical ordering

You may only order by a single attribute. If you call order twice in the chain, the last call gets presedence:

Book.order(:title).order(:price)

In this example the books will be ordered by :price and the order(:title) is lost.

Limit

Just call limit with an integer argument. This sets the maximum number of records to retrieve:

Book.limit(2)

Delayed Execution

It should be noted that all finds are lazy (except first). This means the value returned is not an array of records, rather a handle to a {Scope} object that will return records when you enumerate over them.

This allows you to build an expression without making unecessary requests. In the following example no request is made until the call to each_with_index.

all_books = Books.all
ten_books = all_books.limit(10)

ten_books.each_with_index do |book,n|
  puts "#{n + 1} : #{book.title}"
end

Public Class Methods

as_array(value) click to toggle source

A utility method for casting values into an array.

  • nil is returned as an empty array, []

  • Arrays are returned unmodified

  • Everything else is returned as the sole element of an array

@param [Object] value @return [Array] The value cast into an array @private

# File lib/aws/record.rb, line 91
def self.as_array value
  case value
  when nil   then []
  when Set   then value.to_a
  when Array then value
  else [value]
  end
end
as_set(value) click to toggle source

A utility method for casting values into

  • Sets are returned unmodified

  • everything else is passed through #{as_array} and then into a new Set

@param [Object] value @return [Set] The value cast into a Set. @private

# File lib/aws/record.rb, line 108
def self.as_set value
  case value
  when Set then value
  else Set.new(as_array(value))
  end
end
domain_prefix() click to toggle source

@return [String,nil] The string that is prepended to all domain names.

# File lib/aws/record.rb, line 51
def self.domain_prefix
  @domain_prefix
end
domain_prefix=(prefix) click to toggle source

Sets a prefix to be applied to all SimpleDB domains associated with AWS::Record::Base classes.

AWS::Record.domain_prefix = 'production_'

class Product < AWS::Record::Base
  set_shard_name 'products'
end

p = Product.new
p.shard #=> 'products'
p.save # the product is persisted to the 'production-products' domain

@param [String] prefix A prefix to append to all domains. This is useful

for grouping domains used by one application with a single prefix.
# File lib/aws/record.rb, line 46
def self.domain_prefix= prefix
  @domain_prefix = prefix
end
table_prefix() click to toggle source

@return [String,nil] The string that is prepended to all table names.

# File lib/aws/record.rb, line 78
def self.table_prefix
  @table_prefix
end
table_prefix=(prefix) click to toggle source

Sets a prefix to be applied to all DynamoDB tables associated with {AWS::Record::HashModel} and {AWS::Record::ListModel} classes.

AWS::Record.table_prefix = 'production_'

class Product < AWS::Record::HashModel
  set_shard_name 'products'
end

p = Product.new
p.shard #=> 'products'
p.save # the product is persisted to the 'production-products' table

@param [String] prefix A prefix to append to all tables. This is

useful for grouping tables used by one application with a
single prefix.
# File lib/aws/record.rb, line 73
def self.table_prefix= prefix
  @table_prefix = prefix
end