Class: AWS::Record::Base

Inherits:
Object
  • Object
show all
Extended by:
AttributeMacros, FinderMethods, OptimisticLocking, Scopes, 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

Class Method Summary collapse

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, find, first, limit, order, where

Methods included from OptimisticLocking

optimistic_locking

Methods included from Scopes

scope

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.



304
305
306
307
308
# File 'lib/aws/record/base.rb', line 304

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

Class Method Details

.attributesHash<String,Attribute>

Returns a hash of all of the configured attributes for this class.

Returns:

  • (Hash<String,Attribute>)

    Returns a hash of all of the configured attributes for this class.



422
423
424
# File 'lib/aws/record/base.rb', line 422

def attributes
  @attributes ||= {}
end

.create_domainObject

Creates the SimpleDB domain that is configured for this class.



440
441
442
# File 'lib/aws/record/base.rb', line 440

def create_domain
  AWS::SimpleDB.new.domains.create(domain_name)
end

.domain_nameString

Returns the full prefixed domain name for this class.

Returns:

  • (String)

    Returns the full prefixed domain name for this class.



434
435
436
437
# File 'lib/aws/record/base.rb', line 434

def domain_name
  @_domain_name ||= self.to_s
  "#{Record.domain_prefix}#{@_domain_name}"
end

.set_domain_name(name) ⇒ Object

Allows you to override the default domain name for this record.

The defualt domain name is the class name.

Parameters:

  • The (String)

    domain name that should be used for this class.



429
430
431
# File 'lib/aws/record/base.rb', line 429

def set_domain_name name
  @_domain_name = name
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.



320
321
322
323
324
325
326
327
# File 'lib/aws/record/base.rb', line 320

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.



401
402
403
404
405
406
407
408
409
410
411
# File 'lib/aws/record/base.rb', line 401

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.



414
415
416
# File 'lib/aws/record/base.rb', line 414

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.



314
315
316
# File 'lib/aws/record/base.rb', line 314

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.



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

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.



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

def persisted?
  !!@_persisted
end

#saveBoolean

Creates new records, updates existing records.

Returns:

  • (Boolean)

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



357
358
359
360
361
362
363
364
365
# File 'lib/aws/record/base.rb', line 357

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.



372
373
374
375
# File 'lib/aws/record/base.rb', line 372

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.



381
382
383
384
# File 'lib/aws/record/base.rb', line 381

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)


391
392
393
394
395
396
397
# File 'lib/aws/record/base.rb', line 391

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.



349
350
351
352
# File 'lib/aws/record/base.rb', line 349

def valid?
  validate
  errors.empty?
end