class AWS::Record::Model
end
puts “#{n + 1} : #{book.title}”
ten_books.each_with_index do |book,n|
ten_books = all_books.limit(10)
all_books = Books.all
each_with_index.
In the following example no request is made until the call to
This allows you to build an expression without making unecessary requests.
to a {Scope} object that will return records when you enumerate over them.
means the value returned is not an array of records, rather a handle
It should be noted that all finds are lazy (except ‘first`). This
### Delayed Execution
Book.limit(2)
number of records to retrieve:
Just call `limit` with an integer argument. This sets the maximum
### Limit
is lost.
In this example the books will be ordered by :price and the order(:title)
Book.order(:title).order(:price)
chain, the last call gets presedence:
You may only order by a single attribute. If you call order twice in the
Book.order(:title, :desc) # reverse alphabetical ordering
Book.order(:title) # alphabetical ordering
Pass the value :desc as a second argument to sort in reverse ordering.
This orders the records as returned by AWS. Default ordering is ascending.
### Order
Book.where(:title => ’My Book’)
expressions that use or.
least flexible. You can not use this form if you need more complex
3. A hash of key-value pairs. This is the simplest form, but also the
Book.where(‘title = ?’, ‘My Book’)
arguments properly to avoid injection.
2. An sql-like fragment, with placeholders. This escapes quoted
Book.where(‘title = “My Book”’)
not suggested.
1. As an sql-like fragment. If you need to escape values this form is
Where accepts aruments in a number of forms:
### Conditions (where)
* ‘limit`
* `order`
* `where`
There are 3 standard scope methods:
next_good_reads = Book.mine.unread.top_10
# to me, that are unread sorted by popularity.
# The following expression returns 10 books that belong
end
scope :top_10, by_popularity.limit(10)
scope :by_popularity, order(:score, :desc)
scope :unread, where(:has_been_read => false)
scope :mine, where(:owner => ’Me’)
class Book < AWS::Record::Model
name your most common conditions for reuse.
More useful than writing query fragments all over the place is to
# Scopes
* ‘:limit` - The maximum number of records to return
* `:order` - The order to sort matched records by
* `:where` - Conditions that must be met to be returned
You can pass as find options:
book = Book.first(:where => { :has_been_read => false })
my_books = Book.find(:all, :where => ’owner = “Me”‘)
can pass options to `find`, `all` and `first`.
Frequently you do not want ALL records or the very first record. You
### Modifiers
b = Book.first
If you only want a single record, you should use `first`.
### First
causing quite a few requests.
and number of attributes each record has, this can take a while,
Be careful when enumerating all. Depending on the number of records
end
puts book.id
Book.find(:all) do |book|
end
puts book.id
Book.all.each do |book|
You can enumerate all of your records using `all`.
### All
be raised.
If you try to find a record by ID that has no data an error will
b = Book.find(“0aa894ca-8223-4d34-831e-e5134b2bb71c”)
b = Book[“0aa894ca-8223-4d34-831e-e5134b2bb71c”]
at a latter time:
is saved for the first time. You can use this ID to fetch the record
You can find records by their ID. Each record gets a UUID when it
# Finder Methods
{Validations}.
For more information about the available validation methods see
adds an error, the the save will fail.
Validations are checked before saving a record. If any of the validators
b.errors.full_messages #=> [’Title may not be blank’]
b.valid? #=> false
b = Book.new
end
validates_presence_of :title
string_attr :title
class Book < AWS::Record::Model
validators.
your data clean. AWS::Record supports most of the ActiveRecord style
It’s important to validate models before there are persisted to keep
# Validations
option with the attribute macros.
Please consider these limitations when you choose to use the ‘:set`
* duplicate values are automatically omitted
* values are unordered
means:
These multi-valued attributes are treated as sets, not arrays. This
b.tags #=> #<Set: {’fiction’, ‘fantasy’}>
b.tags = [‘fiction’, ‘fantasy’]
b.tags #=> #<Set: {}>
b = Book.new
end
string_attr :tags, :set => true
class Book < AWS::Record::Model
AWS::Record permits storing multiple values with a single attribute.
### Multi-Valued (Set) Attributes
Book.new.author #=> ‘Me’
end
string_attr :author, :default_value => ‘Me’
class Book < AWS::Record::Model
a value that is populated onto all new instnaces of the class.
All attribute macros accept the ‘:default_value` option. This sets
### Default Values
#=> { ’title’ => ‘My Book’, ‘has_been_read’ => true, … }
b.attributes
b.id #=> “0aa894ca-8223-4d34-831e-e5134b2bb71c”
b.save
b.published_at = Time.now
b.weight_in_pounds = 1.1
b.number_of_pages = 1000
b.has_been_read = true
b.title = “My Book”
b = Book.new
to your class (and a few other useful methods).
For each attribute macro a pair of setter/getter methods are added #
end
datetime_attr :published_at
float_attr :weight_in_pounds
integer_attr :number_of_pages
boolean_attr :has_been_read
string_attr :title
class Book < AWS::Record::Model
Normally you just call these methods inside your model class definition:
### Usage
* ‘date_attr`
* `datetime_attr`
* `float_attr`
* `integer_attr`
* `boolean_attr`
* `string_attr`
attributes (and what types) you need.
models are not backed by a database table/schema. You must choose what
attributes your class should have. Unlike ActiveRecord, AWS::Record
When extending AWS::Record::Model you should first consider what
# Attribute Macros
b.save
b = Book.new(:title => ’My Book’, :author => ‘Me’, :pages => 1)
end
timestamps # adds a :created_at and :updated_at pair of timestamps
integer_attr :number_of_pages
string_attr :author
string_attr :title
class Book < AWS::Record::Model
An ActiveRecord-like interface built ontop of Amazon SimpleDB.
def all options = {}
-
(Scope)- Returns an enumerable scope object.
def all options = {} new_scope.find(:all, options) end
def attributes
-
(Hash)- A hash with attribute names as hash keys (strings) and
def attributes attributes = super attributes['id'] = id if persisted? attributes end
def boolean_attr name, options = {}
-
name(Symbol) -- The name of the attribute.
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
def count(options = {})
(**options)-
:limit(Integer) -- The max number of records to count. -
:where(Mixed) -- Conditions that determine what
Parameters:
-
options(Hash) -- ({}) Options for counting
def count(options = {}) new_scope.count(options) end
def create_domain shard_name = nil
-
(SimpleDB::Domain)-
Parameters:
-
shard_name(optional, String) -- Defaults to the class name.
def create_domain shard_name = nil sdb.domains.create(sdb_domain_name(shard_name)) end
def create_storage
def create_storage to_add = serialize_attributes sdb_item.attributes.add(to_add.merge(opt_lock_conditions)) end
def date_attr name, options = {}
(**options)-
:set(Boolean) -- When true this attribute
Parameters:
-
options(Hash) -- -
name(Symbol) -- The name of the attribute.
Other tags:
- Example: A standard date attribute -
def date_attr name, options = {} add_attribute(Record::Attributes::DateAttr.new(name, options)) end
def datetime_attr name, options = {}
(**options)-
:set(Boolean) -- When true this attribute
Parameters:
-
options(Hash) -- -
name(Symbol) -- The name of the attribute.
Other tags:
- Example: A standard datetime attribute -
def datetime_attr name, options = {} add_attribute(Record::Attributes::DateTimeAttr.new(name, options)) end
def delete_storage
def delete_storage sdb_item.delete(opt_lock_conditions) @_deleted = true end
def deserialize_item_data item_data
def deserialize_item_data item_data marked_for_deletion = item_data['_delete_'] || [] data = {} item_data.each_pair do |attr_name,values| attribute = self.class.attributes[attr_name] next unless attribute next if marked_for_deletion.include?(attr_name) if attribute.set? data[attr_name] = values.map{|v| attribute.deserialize(v) } else data[attr_name] = attribute.deserialize(values.first) end end data end
def each &block
def each &block all.each(&block) end
def find *args
(**options)-
:limit(Integer) -- The max number of records to fetch. -
:sort(String, Array) -- The order records should be -
:where(Mixed) -- Conditions that determine what
Parameters:
-
options(Hash) -- -
mode(:all, :first) -- (:all) When finding `:all` matching records -
id() -- The record to find, raises an exception if the record is
Overloads:
-
find(mode, options = {}) -
find(id)
def find *args new_scope.find(*args) end
def find_by_id id, options = {}
-
(Record::HashModel)- Returns the record with the given id.
Raises:
-
(RecordNotFound)- Raises a record not found exception if there
Options Hash:
(**options)-
:shard(String) -- Specifies what shard (i.e. domain)
Parameters:
-
options(Hash) -- -
id(String) -- The id of the record to load.
def find_by_id id, options = {} domain = sdb_domain(options[:shard] || options[:domain]) data = domain.items[id].data.attributes raise RecordNotFound, "no data found for id: #{id}" if data.empty? obj = self.new(:shard => domain) obj.send(:hydrate, id, data) obj end
def first options = {}
-
(Object, nil)- Returns the first record found. If there were
def first options = {} new_scope.first(options) end
def float_attr name, options = {}
(**options)-
:set(Boolean) -- When true this attribute
Parameters:
-
options(Hash) -- -
name(Symbol) -- The name of the attribute.
def float_attr name, options = {} add_attribute(Attributes::FloatAttr.new(name, options)) end
def hydrate(id, data)
def hydrate(id, data) @_id = id super end
def id
-
(String)- Returns the id string (uuid) for this record. Retuns
def id @_id end
def integer_attr name, options = {}
(**options)-
:set(Boolean) -- When true this attribute
Parameters:
-
options(Hash) -- -
name(Symbol) -- The name of the attribute.
def integer_attr name, options = {} add_attribute(Attributes::IntegerAttr.new(name, options)) end
def limit limit
People.where(:age => 40).limit(10).each {|person| ... }
Limit can be chained with other scope modifiers:
People.limit(10).each {|person| ... }
matching the where conditions will be returned from a find.
The maximum number of records to return. By default, all records
def limit limit new_scope.limit(limit) end
def order *args
-
direction(:asc, :desc) -- (:asc) The direction to sort. -
attribute(String, Symbol) -- The attribute to sort by.
Overloads:
-
order(attribute, direction = :asc)
def order *args new_scope.order(*args) end
def populate_id
- Api: - private
def populate_id @_id = UUIDTools::UUID.random_create.to_s.downcase end
def sdb
def sdb AWS::SimpleDB.new end
def sdb_domain shard_name = nil
- Api: - private
Returns:
-
(AWS::SimpleDB::Domain)-
def sdb_domain shard_name = nil sdb.domains[sdb_domain_name(shard_name)] end
def sdb_domain
def sdb_domain self.class.sdb_domain(shard) end
def sdb_domain_name shard_name = nil
def sdb_domain_name shard_name = nil "#{AWS::Record.domain_prefix}#{self.shard_name(shard_name)}" end
def sdb_item
def sdb_item sdb_domain.items[id] end
def shard shard_name
-
(Scope)- Returns a scope for restricting the domain of subsequent
Parameters:
-
shard_name(String) --
def shard shard_name new_scope.shard(shard_name) end
def sortable_float_attr name, options = {}
(**options)-
:set(Boolean) -- When true this attribute -
:range(Range) -- The range of numbers this attribute
Parameters:
-
options(Hash) -- -
name(Symbol) -- The name of the attribute.
Other tags:
- Note: - If you change the `:range` after some values have been persisted
def sortable_float_attr name, options = {} add_attribute(Attributes::SortableFloatAttr.new(name, options)) end
def sortable_integer_attr name, options = {}
(**options)-
:set(Boolean) -- When true this attribute -
:range(Range) -- A numeric range the represents the
Parameters:
-
options(Hash) -- -
name(Symbol) -- The name of the attribute.
def sortable_integer_attr name, options = {} add_attribute(Attributes::SortableIntegerAttr.new(name, options)) end
def string_attr name, options = {}
(**options)-
:set(Boolean) -- When true this attribute
Parameters:
-
options(Hash) -- -
name(Symbol) -- The name of the attribute.
Other tags:
- Example: A string attribute with `:set` set to true -
Example: A standard string attribute -
def string_attr name, options = {} add_attribute(Record::Attributes::StringAttr.new(name, options)) end
def timestamps
recipe.updated_at #=>
recipe.created_at #=>
recipe.save
recipe = Recipe.new
end
timestamps
class Recipe < AWS::Record::Model
@example
`:created_at` and `:updated_at`.
A convenience method for adding the standard two datetime attributes
def timestamps c = datetime_attr :created_at u = datetime_attr :updated_at [c, u] end
def update_storage
def update_storage to_update = {} to_delete = [] # serialized_attributes will raise error if the entire record is blank attribute_values = serialize_attributes changed.each do |attr_name| if values = attribute_values[attr_name] to_update[attr_name] = values else to_delete << attr_name end end to_update.merge!(opt_lock_conditions) if to_delete.empty? sdb_item.attributes.replace(to_update) else sdb_item.attributes.replace(to_update.merge('_delete_' => to_delete)) sdb_item.attributes.delete(to_delete + ['_delete_']) end end
def where *args
-
where(sql_fragment[, quote_params, ...]) -
where(conditions_hash)
Parameters:
-
conditions_hash(Hash) -- A hash of attributes to values. Each
def where *args new_scope.where(*args) end