Ribs - A Ruby ORM using Hibernate

The current ORM approaches for Ruby include ActiveRecord, DataMapper and RBatis. None of these have the versatility and power that Hibernate gives to any Java project. Ribs is a new interpretation of the idea ActiveHibernate, which was proposed in a blog post here. The original name didn’t really suit, though, since it was based on the ActiveRecord name, and Ribs will end up being something quite different.

So what is Ribs? It’s a Ruby framework written for JRuby, that allows you to use Hibernate to persist your Ruby objects. There are many things planned for Ribs, but currently it only supports quite basic operations.

Ribs is explicitly defined to solve several data access patterns for most Ruby development. In one end it means scaling down from something very much like ActiveRecord, but on the other end supports such things as Repository, Data Mapper, Unit of Work and Identity Map.

Definitions

To get started, you first need to define a database connection for Ribs to use. In most cases you only have one, but Ribs doesn’t preclude you defining several and using them in different places. The code for doing that looks like this:

Ribs::DB.define do |db|
  db.dialect = 'Derby'
  db.uri = 'jdbc:derby:test_database;create=true'
  db.driver = 'org.apache.derby.jdbc.EmbeddedDriver'
end

You can also provide username, password, and other properties that will be passed through to Hibernate. Currently, Derby is the only database tested against, but most of the features used are totally database independent, and Hibernate shields Ribs from most of the rest.

See the define method and the Ribs::DB class for more information on what’s available.

To actually make a Ruby class a database backed objects, you use the method Kernel#Ribs! In the simple case you don’t need to actually provide any configuration, but support is available for renaming columns, deciding which one is the primary key, and also avoid making columns part of the object. The simplest case looks like this:

class Blog
end

That is more or less the same as doing

class Blog
  Ribs!
end

This method call will not do anything that won’t be done implicitly at the first usage of it as a Ribs model. This means that if you follow conventions exactly, you don’t need to configure anything at all.

It can also be written as:

class Blog; end

Ribs! :on => Blog

There is no need to actually have the Ribs definition inside of the model class. You can define that a model should go against a specific database definition if you want - by default it will use the default database.

In the above example, the table name backing the model will be assumed to be “blog”. Case is not important here. Ribs tries to find the table no matter what.

To redefine which table to use, the names of the columns, and where the primary key is, you need to provide a block to the Ribs! method:

class Blog
  Ribs! :table => :blogs do |r|
    r.blog_id.primary_key!

    r.blog_title :column => :title

    r.irrelevant_column.avoid!
  end
end

This code will back the model against the “blogs” table, have the column name blog_id represent the primary key, and map the column title to the property blog_title. Finally, avoid! tells Ribs to avoid a specific column, so it won’t try to map that.

If you have a primary key that you want to have a different name, or a column you want to avoid but it’s not nullable, you can use these variations instead:

class Blog
  Ribs! :table => :blogs do |r|
    r.blog_id :primary_key, :column => :id

    r.irrelevant_column :avoid, :default => "value to insert:
  end
end

Currently Ribs only supports simple data types. It doesn’t include associations, and have no support for setting default values, or constraints in the definitions. It’s not possible to set the types in the definitions either.

Usage

Once you have a defined model, you can work with it in several different ways.

If you want to create new instances you can do it much like with ActiveRecord, except that you always need to surround a call to anything regarding the database with an invocation to the method

  1. This method takes either a class or an instance, and returns a

repository proxy for the argument. You can also send in a specific database name if you want to work with another database then the default one.:

blog = Blog.new
blog.blog_id = 1
blog.blog_title = "Foobar"
R(blog).save

The new-method returned from R can take the parameters as a hash of symbols too:

blog = R(Blog).new(
  :blog_id => 1,
  :blog_title => "Foobar")
R(blog).save

And as a short hand a create method is available:

blog = R(Blog).create(
  :blog_id => 1,
  :blog_title => "Foobar")

To find a specific entry:

blog = R(Blog).get(1)

Or to find all entries:

blogs = R(Blog).all

To update an entry:

blog.blog_title = "New title"
R(blog).save

To destroy an existing entry:

R(blog).destroy!

Or to destroy it based on id:

R(Blog).destroy(1)

License

Ribs is released under the MIT license.