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, :deafult_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 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 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 sdb
def sdb AWS::SimpleDB.new end
def sdb_domain shard_name = nil
- 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