Exploring Object-Relational Mapping in Ruby

As for homework this week, I was given the task to create a Sinatra app with each Object-relational mapping (ORM) we learned in class: Sequel, ActiveRecord and DataMapper. I built these simple applications that gave a name, age, color and description to rabbits. Link to the repository here.

In this blog post, I am going to talk about my experience using all three ORMs and state my favorite one.

DataMapper

The pleasant characteristic of DataMapper is that I did not need to use any command line prompts to have my database up and running. This is because DataMapper defines the migrations on the fly.

To set up and update my database automatically, I typed:


DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/rabbits.db")

DataMapper.auto_upgrade!

The first command will check first if a database exists remotely. If not, it will create and assign a local one using sqlite3. The second one will update any changes I make to the database without deleting any of the data. Piece of cake.

Speaking of sqlite3, these are the gems I used (as typed in my Gemfile):


gem 'sinatra'
gem 'data_mapper'
gem 'dm-sqlite-adapter'
gem 'sqlite3'
gem 'haml'

Two things to notice: I am using haml but erb will do the job as well. And make sure you install ‘dm-sqlite-adapter’. At first, my app was not running and I did not know why but my classmate Nikki mentioned that in order to make DataMapper run with sqlite, I needed to have that gem. Bless her.

As for the model creation, DataMapper requires you to code somthing along these lines:


class Rabbit
  include DataMapper::Resource 
  property :id,           Serial
  property :name,         String, :required => true
  property :description,  Text
  property :age,          Integer
  property :color,       String
  property :created_at,   DateTime
  property :updated_at,   DateTime
end

The first id field is necessary as it provides your resource an auto-incrementing integer id that can be referenced by. This means the database will automatically take care of assigning a unique id to each resource.

Well this is neat. And besides installing a gem that I needed from the get go, I did not have any other problems with DataMapper.

Sequel

In order to use Sequel with my app I had to create a directory called migrations, which is like version control for your database. An extra step from what I did using DataMapper. .

Moving on, to set up the database I wrote in the model:


DB = Sequel.connect('sqlite://rabbit.db')

Also in the model, I created a rabbit class with a timestamps plugin:


class Rabbit < Sequel::Model
  plugin :timestamps
end

This was because in my migrations file, I wanted to create a create_at column that was of format DateTime. When I used DataMapper, the timestamps plugin came with it so I did not need to include it in my Rabbit class.

As for the migrations files, Sequel requires you to organize your files in some order. The preference order is alpha-numeric. This is because Sequel is going to execute each file in the migrations folder. If I had a file to add_column and a create_column, Sequel would run a add_column first even though create_column needed to run first(a comes before c, got it?). So in order to avoid Sequel confusion in the future, in the migrations directory I named the migration file: “01_create_rabbits.rb”; and in it added the following code:


Sequel.migration do
  change do
    create_table(:rabbits) do
      primary_key :id
      String :name, :null=>false
      Text :description
      Integer :age
      String :color
      DateTime :created_at
      DateTime :updated_at
    end
  end
end

In the code above I am creating the table and defining the columns (primary_key, String, etc).

Here is where one may think that s/he is done. After using DataMapper, I got used to just firing up my Sinatra app to see the database. With Sequel,however, you have first to use the following command line prompt in order to be good to go:


sequel -m migrations sqlite://rabbit.db

This is the extra step I mentioned earlier. From then I was able to get the application going.

ActiveRecord

I liked using ActiveRecord as it gave me exposure to Rake. Rake is short for Ruby Make, a task management tool that automates some tasks for you such as creating a database. But that was about it. ActiveRecord seems overbearing and required me to use the command line at least twice and tweak some of my code.

To begin with, nn my Gemfile I bundle installed these gems:


gem 'sinatra'
gem 'activerecord'
gem 'sinatra-activerecord'
gem 'sqlite3'
gem 'haml'
gem 'rake'

Moment of curiosity- I think the gem sinatra-activerecord installs rake for you because in my Rake file I have.


require './app'
require 'sinatra/activerecord/rake'

I am not entirely sure. Pardon my beginner digression… anyways, ActiveRecord requires you to set the database and I typed this piece of code in my lib folder:


set :database, "sqlite3:///rabbit.db"

class Rabbit < ActiveRecord::Base  
end

The class Rabbit here inherits from ActiveRecord::Base.
Also, note that I am using sqlite3 again as the database client and that there are “///” three forward slashes in ‘set :database, “sqlite3:///rabbit.db”‘. I originally had two and when I ran rake in my terminal it did not like it.

As for running rake in the terminal, these are the steps I needed to do to run the first database migration.

I went to Bash and typed:


rake db:create_migration NAME=rabbit

It generated the directory structure and file db/migrate/20131227195644_rabbits.rb

That number (20131227195644) is a timestamp and this is how Activerecord knows the order in which migrations should be applied to the database.

Cool, the schema and the migration files have been created but still I needed to create my table and its columns in 20131227195644_rabbits.rb.


class Rabbits  true
      t.text :description
      t.integer :age
      t.string :color
      t.string :breed
      t.timestamps
    end
  end

  def down
    drop_table :rabbits
  end
end

After plugging the code above(the bones are put there for me by rake by the way.) I only had to add what goes inside the methods up and down.

Done??? No! You have to run migration again with rake to put the table in its right place.

So I went to terminal and typed:


$ rake db:migrate

Then I was done??? No, no! I noticed that Active record did not like some of the code that was in my app.rb file.
Specifically the ones that had Rabbit.get in them.

Example:


get '/rabbits/edit/:id' do
    @rabbit = Rabbit.get(id: params[:id])
    haml :edit
  end

So ActiveRecord (and Sequel too I found out later) has a different syntax in this aspect them DataMapper to get the params. I guess you don’t GET the params you FIND them.

I changed my code promptly and it worked fine afterwards.


get '/rabbits/edit/:id' do
    @rabbit = Rabbit.find(id: params[:id])
    haml :edit
  end

Then I was done. ActiveRecord is cool and all, but not as simple to use as DataMapper. It is certainly an improvement from Sequel as timestamps in ActiveRecord orders the migrations for you avoiding the clunky way of adding a number at the beginning of your file such as 01_create_rabbits.rb

And my fav ORM is…

As you may have guessed if you’ve really read this post, my preferred ORM is DataMapper because it is direct and removes the need to write migrations as they are created and update on the go.

And these are the humble opinions of a programming neophyte.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s