Connecting to a Redis database

Ohm uses a lightweight Redis client called Redic. To connect
to a Redis database, you will need to set an instance of Redic, with
an URL of the form redis://:<passwd>@<host>:<port>/<db>, through the
Ohm.redis= method, e.g.

This example shows some basic features, like attribute declarations and
querying. Keep reading to find out what you can do with models.

Attribute types

Ohm::Model provides 4 attribute types:

{Ohm::Model.attribute attribute},

{Ohm::Model.set set}

{Ohm::Model.list list}

{Ohm::Model.counter counter}

and 2 meta types:

{Ohm::Model.reference reference}

{Ohm::Model.collection collection}.

attribute

An attribute is just any value that can be stored as a string. In the
example above, we used this field to store the event's name. You can
use it to store numbers, but be aware that Redis will return a string
when you retrieve the value.

set

A set in Redis is an unordered list, with an external behavior similar
to that of Ruby arrays, but optimized for faster membership lookups.
It's used internally by Ohm to keep track of the instances of each model
and for generating and maintaining indexes.

list

A list is like an array in Ruby. It's perfectly suited for queues
and for keeping elements in order.

counter

A counter is like a regular attribute, but the direct manipulation
of the value is not allowed. You can retrieve, increase or decrease
the value, but you can not assign it. In the example above, we used a
counter attribute for tracking votes. As the incr and decr operations
are atomic, you can rest assured a vote won't be counted twice.

reference

It's a special kind of attribute that references another model.
Internally, Ohm will keep a pointer to the model (its ID), but you get
accessors that give you real instances. You can think of it as the model
containing the foreign key to another model.

collection

Provides an accessor to search for all models that reference the current model.

Persistence strategy

The attributes declared with attribute are only persisted after
calling save.

Operations on attributes of type list, set and counter are
possible only after the object is created (when it has an assigned
id). Any operation on these kinds of attributes is performed
immediately. This design yields better performance than buffering
the operations and waiting for a call to save.

For most use cases, this pattern doesn't represent a problem.
If you are saving the object, this will suffice:

if event.save
event.comments.add(Comment.create(body:"Wonderful event!"))
end

Working with Sets

Given the following model declaration:

classEvent < Ohm::Model
attribute :name
set :attendees, :Personend

You can add instances of Person to the set of attendees with the
add method:

event.attendees.add(Person.create(name:"Albert"))
# And now...
event.attendees.each do |person|
# ...do what you want with this person.end

Sorting

Since attendees is a {Ohm::Model::Set Set}, it exposes two sorting
methods: {Ohm::Model::Collection#sort sort} returns the elements
ordered by id, and {Ohm::Model::Collection#sort_by sort_by} receives
a parameter with an attribute name, which will determine the sorting
order. Both methods receive an options hash which is explained below:

:order

Order direction and strategy. You can pass in any of the following:

ASC

ASC ALPHA (or ALPHA ASC)

DESC

DESC ALPHA (or ALPHA DESC)

It defaults to ASC.

Important Note: Starting with Redis 2.6, ASC and DESC only
work with integers or floating point data types. If you need to sort
by an alphanumeric field, add the ALPHA keyword.

:limit

The offset and limit from which we should start with. Note that
this is 0-indexed. It defaults to 0.

Example:

limit: [0, 10] will get the first 10 entries starting from offset 0.

:by

Key or Hash key with which to sort by. An important distinction with
using {Ohm::Model::Collection#sort sort} and
{Ohm::Model::Collection#sort_by sort_by} is that sort_by automatically
converts the passed argument with the assumption that it is a hash key
and it's within the current model you are sorting.

Tip: Unless you absolutely know what you're doing, use sort
when you want to sort your models by their id, and use sort_by
otherwise.

:get

A key pattern to return, e.g. Post:*->title. As is the case with
the :by option, using {Ohm::Model::Collection#sort sort} and
{Ohm::Model::Collection#sort_by sort_by} has distinct differences in
that sort_by does much of the hand-coding for you.

Post.all.sort_by(:title, get::title)
# SORT Post:all BY Post:*->title GET Post:*->titlePost.all.sort(by::title, get::title)
# SORT Post:all BY title GET title

Associations

Ohm lets you declare references and collections to represent associations.

The only difference with the actual implementation is that the model
is memoized.

The net effect here is we can conveniently set and retrieve Post objects,
and also search comments using the post_id index.

Comment.find(post_id:1)

Collections explained

The reason a {Ohm::Model.reference reference} and a
{Ohm::Model.collection collection} go hand in hand, is that a collection is
just a macro that defines a finder for you, and we know that to find a model
by a field requires an {Ohm::Model.index index} to be defined for the field
you want to search.

The only "magic" happening is with the inference of the index that was used
in the other model. The following all produce the same effect:

# easiest, with the basic assumption that the index is `:post_id`
collection :comments, :Comment# we can explicitly declare this as follows too:
collection :comments, :Comment, :post# finally, we can use the default argument for the third parameter which# is `to_reference`.
collection :comments, :Comment, to_reference
# exploring `to_reference` reveals a very interesting and simple concept:Post.to_reference ==:post# => true

Indices

An {Ohm::Model.index index} is a set that's handled automatically by Ohm. For
any index declared, Ohm maintains different sets of objects IDs for quick
lookups.

In the Event example, the index on the name attribute will
allow for searches like Event.find(name: "some value").

Note that the methods {Ohm::Model::Set#find find} and
{Ohm::Model::Set#except except} need a corresponding index in order to work.

Finding records

You can find a collection of records with the find method:

# This returns a collection of users with the username "Albert"User.find(username:"Albert")

Filtering results

# Find all users from ArgentinaUser.find(country:"Argentina")
# Find all active users from ArgentinaUser.find(country:"Argentina", status:"active")
# Find all active users from Argentina and UruguayUser.find(status:"active").combine(country: ["Argentina", "Uruguay"])
# Find all users from Argentina, except those with a suspended account.User.find(country:"Argentina").except(status:"suspended")
# Find all users both from Argentina and UruguayUser.find(country:"Argentina").union(country:"Uruguay")

Note that calling these methods results in new sets being created
on the fly. This is important so that you can perform further operations
before reading the items to the client.

Ohm Extensions

Ohm is rather small and can be extended in many ways.

A lot of amazing contributions are available at Ohm Contrib
make sure to check them if you need to extend Ohm's functionality.

Upgrading

Ohm 2 breaks the compatibility with previous versions. If you're upgrading an
existing application, it's nice to have a good test coverage before going in.
To know about fixes and changes, please refer to the CHANGELOG file.