Ruby's DataMapper 101

It's been a while and lately I've been playing again with Merb so I figured I'll post about DataMapper. DataMapper is an ORM for Ruby which most Merb users prefer because of its simplicity and neat features.

Before we can start playing with it, you can install it using the following command:

sudo gem install dm-core

Also, for the purpose of this tutorial we're going to install the sqlite database driver:

gem install do_sqlite3

Once installed, we can require it in our application and setup our database connection:

  1. require 'rubygems'
  2. require 'dm-core'
  3.  
  4. DataMapper.setup(:default, 'sqlite3://<path to db>')

Then we can start defining our models, for this tutorial we're going to create a pizza recipe with ingredients:

  1. class Pizza
  2. include DataMapper::Resource
  3.  
  4. property :id, Serial
  5. property :name, String
  6. property :price, Float
  7.  
  8. has n, :ingredients
  9. end
  10.  
  11. class Ingredient
  12. include DataMapper::Resource
  13.  
  14. property :id, Serial
  15. property :name, String
  16.  
  17. belongs_to :pizza
  18. end

As you can see they're just regular classes except that we're using the DataMapper::Resource as mixin, this is what actually makes the class a DataMapper model. The properties define the fields and the "has n" and "belongs_to" defines the relationship between the two models. Here we're saying that a single pizza can have several ingredients and an ingredient belongs to pizzas.

Now that our models are all set, we can start migrating which actually constructs our database and tables.

  1. DataMapper.auto_migrate!

Alternatively we could have just run Pizza.auto_migrate! and Ingredients.auto_migrate!, it works the same way. Now we can start playing with it, lets start by creating pizzas and ingredients:

  1. pizza1 = Pizza.new(:name=>'Stupid pizza',:price=>3.0)
  2. pizza2 = Pizza.new(:name=>'Unknown pizza',:price=>2.5)
  3.  
  4. ing1 = Ingredient.new(:name=>'Bacon')
  5. ing2 = Ingredient.new(:name=>'Ranch Sauce')
  6. ing3 = Ingredient.new(:name=>'Cheese')
  7. ing4 = Ingredient.new(:name=>'Pepperoni')

Creating a new record is as easy as creating a new instance of every model we've defined. Next we're going to asisgn which ingredient belongs to what pizza:

  1. pizza1.ingredients << ing1
  2. pizza1.ingredients << ing2
  3.  
  4. pizza2.ingredients << ing3
  5. pizza2.ingredients << ing4
  6.  
  7. pizza1.save
  8. pizza2.save

We need to actually save them to persist the data by calling the save method of our model. We can now print all the pizzas and their ingredients:

  1. pizzas = Pizza.all
  2. pizzas.each do |pizza|
  3. puts pizza.name + ' (' + pizza.price.to_s + ')'
  4. pizza.ingredients.each do |ing|
  5. puts ' ' + ing.name
  6. end
  7. end

... and the output:

  1. Stupid pizza (3.0)
  2. Bacon
  3. Ranch Sauce
  4. Unknown pizza (2.5)
  5. Cheese
  6. Pepperoni

Updating the record is as easy as editing the model object's field and calling the save method. For deleting you can also call the destroy! method, lets say we want to delete the Cheese ingredient we can do:

  1. ing3.destroy!

Thats it! This is just a short introduction to DataMapper and there's more to it than just what I've showed you here, check their website for more info.