SkillAgentSearch skills...

Ohm

Object-Hash Mapping for Redis

Install / Use

/learn @soveran/Ohm
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Ohm ॐ

Object-hash mapping library for Redis.

Description

Ohm is a library for storing objects in [Redis][redis], a persistent key-value database. It has very good performance.

Community

Join the mailing list: http://groups.google.com/group/ohm-ruby

Meet us on IRC: #ohm on freenode.net

Related projects

These are libraries in other languages that were inspired by Ohm.

  • Ohm for Crystal, created by soveran
  • JOhm for Java, created by xetorthio
  • Lohm for Lua, created by slact
  • ohm.lua for Lua, created by amakawa
  • Nohm for Node.js, created by maritz
  • Redisco for Python, created by iamteem
  • redis3m for C++, created by luca3m
  • Ohmoc for Objective-C, created by seppo0010
  • Sohm for Lua, compatible with Twemproxy

Articles and Presentations

Getting started

Install [Redis][redis]. On most platforms it's as easy as grabbing the sources, running make and then putting the redis-server binary in the PATH.

Once you have it installed, you can execute redis-server and it will run on localhost:6379 by default. Check the redis.conf file that comes with the sources if you want to change some settings.

If you don't have Ohm, try this:

$ [sudo] gem install ohm

Or you can grab the code from [http://github.com/soveran/ohm][ohm].

Now, in an irb session you can test the Redis adapter directly:

>> require "ohm"
=> true
>> Ohm.redis.call "SET", "Foo", "Bar"
=> "OK"
>> Ohm.redis.call "GET", "Foo"
=> "Bar"

Connecting to a Redis database

Ohm uses a lightweight Redis client called [Redic][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.

require "ohm"

Ohm.redis = Redic.new("redis://127.0.0.1:6379")

Ohm.redis.call "SET", "Foo", "Bar"

Ohm.redis.call "GET", "Foo"
# => "Bar"

Ohm defaults to a Redic connection to "redis://127.0.0.1:6379". The example above could be rewritten as:

require "ohm"

Ohm.redis.call "SET", "Foo", "Bar"

Ohm.redis.call "GET", "Foo"
# => "Bar"

All Ohm models inherit the same connection settings from Ohm.redis. For cases where certain models need to connect to different databases, they simple have to override that, i.e.

require "ohm"

Ohm.redis = Redic.new(ENV["REDIS_URL1"])

class User < Ohm::Model
end

User.redis = Redic.new(ENV["REDIS_URL2"])

Models

Ohm's purpose in life is to map objects to a key value datastore. It doesn't need migrations or external schema definitions. Take a look at the example below:

Example

class Event < Ohm::Model
  attribute :name
  reference :venue, :Venue
  set :participants, :Person
  counter :votes

  index :name
end

class Venue < Ohm::Model
  attribute :name
  collection :events, :Event
end

class Person < Ohm::Model
  attribute :name
end

All models have the id attribute built in, you don't need to declare it.

This is how you interact with IDs:

event = Event.create :name => "Ohm Worldwide Conference 2031"
event.id
# => 1

# Find an event by id
event == Event[1]
# => true

# Update an event
event.update :name => "Ohm Worldwide Conference 2032"
# => #<Event:0x007fb4c35e2458 @attributes={:name=>"Ohm Worldwide Conference"}, @_memo={}, @id="1">

# Trying to find a non existent event
Event[2]
# => nil

# Finding all the events
Event.all.to_a
# => [<Event:1 name='Ohm Worldwide Conference 2032'>]

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,
  • Ohm::Model.set
  • Ohm::Model.list
  • Ohm::Model.counter

and 2 meta types:

  • Ohm::Model.reference
  • Ohm::Model.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 increment and decrement 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.

Tracked keys

Besides the provided attribute types, it is possible to instruct Ohm to track arbitrary keys and tie them to the object's lifecycle.

For example:

class Log < Ohm::Model
  track :text
  
  def append(msg)
    redis.call("APPEND", key[:text], msg)
  end
  
  def tail(n = 100)
    redis.call("GETRANGE", key[:text], -(n), -1)
  end
end

log = Log.create
log.append("hello\n")

assert_equal "hello\n", log.tail

log.append("world\n")

assert_equal "world\n", log.tail(6)

When the log object is deleted, the :text key will be deleted too. Note that the key is scoped to that particular instance of Log, so if log.id is 42 then the key will be Log:42:text.

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:

class Event < Ohm::Model
  attribute :name
  set :attendees, :Person
end

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, it exposes two sorting methods: Ohm::Model::Collection#sort returns the elements ordered by id, and Ohm::Model::Collection#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:

  1. ASC
  2. ASC ALPHA (or ALPHA ASC)
  3. DESC
  4. 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 and Ohm::Model::Collection#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.

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

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 and Ohm::Model::Collection#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:*->title

Post.all.sort(by: :title, get: :title)
# SORT Post:all BY title GET title

Associations

Ohm lets you declare references and collections to repres

View on GitHub
GitHub Stars1.4k
CategoryDevelopment
Updated5d ago
Forks164

Languages

Ruby

Security Score

100/100

Audited on Mar 23, 2026

No findings