Class: AWS::Record::Base

Inherits:
Object
  • Object
show all
Extended by:
AttributeMacros, FinderMethods, Validations
Includes:
DirtyTracking
Defined in:
lib/aws/record/base.rb,
lib/aws/record/errors.rb

Overview

An ActiveRecord-like interface built ontop of AWS.

class Book < AWS::Record::Base

  string_attr :title
  string_attr :author
  integer :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::Base 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

For more information about the various attribute macros available, and what options they accept, see AttributeMacros.

Usage

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

class Book < AWS::Record::Base
  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::Base
  string_attr :author, :deafult_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::Base
  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::Base
  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::Base

  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

Instance Method Summary collapse

Methods included from Validations

validates_acceptance_of, validates_confirmation_of, validates_count_of, validates_each, validates_exclusion_of, validates_format_of, validates_inclusion_of, validates_length_of, validates_numericality_of, validates_presence_of

Methods included from AttributeMacros

boolean_attr, datetime_attr, float_attr, integer_attr, sortable_float_attr, sortable_integer_attr, string_attr, timestamps

Methods included from FinderMethods

all, count, create_domain, domain_name, find, first, limit, optimistic_locking, order, scope, sdb_domain, set_domain_name, where

Methods included from DirtyTracking

#changed, #changed?, #changes

Constructor Details

#initialize(attributes = {}) ⇒ Base

Constructs a new record for this class/domain.

Parameters:

  • attributes (Hash) (defaults to: {})

    A set of attribute values to seed this record with. The attributes are bulk assigned.



299
300
301
302
303
# File 'lib/aws/record/base.rb', line 299

def initialize attributes = {}
  @_data = {}
  assign_default_values
  bulk_assign(attributes)
end

Instance Method Details

#attributesHash

Returns A hash with attribute names as hash keys (strings) and attribute values (of mixed types) as hash values.

Returns:

  • (Hash)

    A hash with attribute names as hash keys (strings) and attribute values (of mixed types) as hash values.



315
316
317
318
319
320
321
322
# File 'lib/aws/record/base.rb', line 315

def attributes
  attributes = IndifferentHash.new
  attributes['id'] = id if persisted?
  self.class.attributes.keys.inject(attributes) do |hash,attr_name|
    hash[attr_name] = __send__(attr_name)
    hash
  end
end

#deleteObject

Deletes the record.



396
397
398
399
400
401
402
403
404
405
406
# File 'lib/aws/record/base.rb', line 396

def delete
  if persisted?
    if deleted?
      raise 'unable to delete, this object has already been deleted'
    else
      delete_item
    end
  else
    raise 'unable to delete, this object has not been saved yet'
  end
end

#deleted?Boolean

Returns true if this instance object has been deleted.

Returns:

  • (Boolean)

    Returns true if this instance object has been deleted.



409
410
411
# File 'lib/aws/record/base.rb', line 409

def deleted?
  persisted? ? !!@_deleted : false
end

#errorsObject



21
22
23
# File 'lib/aws/record/errors.rb', line 21

def errors
  @errors ||= Errors.new
end

#idString

The id for each record is auto-generated. The default strategy generates uuid strings.

Returns:

  • (String)

    Returns the id string (uuid) for this record. Retuns nil if this is a new record that has not been persisted yet.



309
310
311
# File 'lib/aws/record/base.rb', line 309

def id
  @_id
end

#new_record?Boolean

Returns true if this record has not been persisted to SimpleDB.

Returns:

  • (Boolean)

    Returns true if this record has not been persisted to SimpleDB.



339
340
341
# File 'lib/aws/record/base.rb', line 339

def new_record?
  !persisted?
end

#persisted?Boolean

Persistence indicates if the record has been saved previously or not.

Examples:

@recipe = Recipe.new(:name => 'Buttermilk Pancackes')
@recipe.persisted? #=> false
@recipe.save!
@recipe.persisted? #=> true

Returns:

  • (Boolean)

    Returns true if this record has been persisted.



333
334
335
# File 'lib/aws/record/base.rb', line 333

def persisted?
  !!@_persisted
end

#saveBoolean

Creates new records, updates existing records.

Returns:

  • (Boolean)

    Returns true if the record saved without errors, false otherwise.



352
353
354
355
356
357
358
359
360
# File 'lib/aws/record/base.rb', line 352

def save
  if valid?
    persisted? ? update : create
    clear_changes!
    true
  else
    false
  end
end

#save!true

Creates new records, updates exsting records. If there is a validation error then an exception is raised.

Returns:

  • (true)

    Returns true after a successful save.

Raises:

  • (InvalidRecordError)

    Raised when the record has validation errors and can not be saved.



367
368
369
370
# File 'lib/aws/record/base.rb', line 367

def save!
  raise InvalidRecordError.new(self) unless save
  true
end

#update_attributes(attribute_hash) ⇒ Boolean

Bulk assigns the attributes and then saves the record.

Parameters:

  • attribute_hash (Hash)

    A hash of attribute names (keys) and attribute values to assign to this record.

Returns:

  • (Boolean)

    Returns true if the record saved without errors, false otherwise.



376
377
378
379
# File 'lib/aws/record/base.rb', line 376

def update_attributes attribute_hash
  bulk_assign(attribute_hash)
  save
end

#update_attributes!(attribute_hash) ⇒ true

Bulk assigns the attributes and then saves the record. Raises an exception (AWS::Record::InvalidRecordError) if the record is not valid.

Parameters:

  • attribute_hash (Hash)

    A hash of attribute names (keys) and attribute values to assign to this record.

Returns:

  • (true)


386
387
388
389
390
391
392
# File 'lib/aws/record/base.rb', line 386

def update_attributes! attribute_hash
  if update_attributes(attribute_hash)
    true
  else
    raise InvalidRecordError.new(self)
  end
end

#valid?Boolean

Returns true if this record has no validation errors.

Returns:

  • (Boolean)

    Returns true if this record has no validation errors.



344
345
346
347
# File 'lib/aws/record/base.rb', line 344

def valid?
  validate
  errors.empty?
end