Evolutions - Migrations for Model-Centric Apps

November 24, 2007

There is a project for Django called Evolution, which is conceptually similar to Rails' Migrations. There are some differences between Evolutions and Migrations, mainly due to the fact that Django is model-centric, meaning that you define the attributes of your models in the code and generate the database from that. Rails, or I guess ActiveRecord more specifically, as you probably know, is the opposite, where you define your database tables and an object model is generated at runtime from the database metadata. So I guess you would call that a database-centric ORM. Here's their take on the differences:

Isn't this just ActiveRecord::Migration in Python?

Superficially, yes, but not really.

There is a degree of similarity - the Django Evolution syntax is
a DSL for describing the changes that have been made to a model.

However, there are also some significant differences. In addition to
ActiveRecord::Migration, Django Evolution provides:

    1. An audit trail - a permanent archive in the database of the
       changes that have been applied, and when.
    2. An automated hinting scheme. If you don't want to write the
       migrations yourself, you don't have to.
    3. Validation that an evolution script will leave the database
       in a state consistent with the current model definition.

These differences are largely afforded by the model-centric design of
Django itself. Whereas a Ruby on Rails model is a description of a
database that has been created by hand, Django uses the Python model
to creates the database. As a result, the model definition is canonical
- not the database.

This means that audit, hinting, and verification schemes can use the
Django model as a point of reference. A Django Evolution script is
much more than just an alternate syntax for SQL ALTER statements - it
is an evolution scheme that is bound to the canonical model definition.

The DataMapper project is a model-centric ORM for Ruby apps and as that project continues to grow, their could be some ideas borrowed from Django Evolution, although they do already have their own thing going on with Auto-Migration.

But I'm a Ruby guy, so I've got to take one shot at them here :). They say "the Django Evolution syntax is a DSL". Really?

MUTATIONS = [
     AddField('Author', 'location', models.CharField, max_length=100, null=True)
]

I'm sorry, but if that's a DSL, then what isn't a DSL? In my opinion, there is too much Python syntax to call that a DSL. It's a subtle difference but this is a DSL:

add_column :authors, :location, :limit => 100

The question when is it a language specific library and when it is a DSL? Would you call this a DSL?

Mutation m = new AddField("Author", "location", CharField.class);
m.setMaxLength(100);
m.setAllowNulls(false);
Mutations.add(m);

Posted in Technology | Tags Django, Python, Ruby, Rails, DataMapper | 0 Comments

Merb and Data Mapper

November 16, 2007

A couple of new frameworks making waves in the Ruby community are Merb and Data Mapper. Merb and Data Mapper seem to go together like peanut butter and jelly in that Merb gives you pretty much everything Rails gives you except for an ORM framework, and Data Mapper is an ORM framework like Active Record.

Data Mapper has a fantastic Why Data Mapper? page. I love this page because it assumes you the reader are a Ruby on Rails web developer, so you know Active Record, and you want to know why you'd want to use Data Mapper instead of Active Record. After reading that page, I was definitely intrigued. It's amazing what good documentation can do for the success of a framework. Merb has documentation on the differences between Merb and Rails as well.

The most obvious difference that jumps out at you between Active Record and Data Mapper is that in the Data Mapper, you define the properties (a.k.a fields, attributes, columns) of your models inside the model itself, as opposed to Active Record, where you define no properties in your model and reflect the properties from the columns in your database. This has long been a point of contention for many developers critical of Rails.

The most obvious difference between Rails and Merb is that with Merb, controller actions always return the data that is to be sent to the client. A Merb action can simply return a String, or it could return a File, and IO or even a Proc. That sounds cool.

It will be interesting to see if Merb/Data Mapper gain popularity in the Ruby community. Rails is a full-stack solution, where as Merb is a MVC framework and leaves it up to you to choose an ORM framework and Data Mapper is just an ORM, not tied to any one MVC framework. This is more like the Java web framework world, where you have MVC frameworks like Struts, Stripes, etc. and ORM frameworks like Hibernate, and you glue them all together with something like Spring or Guice. The difference here is that you glue them together with Ruby, still no XML or Annotations needed.

One thing I thought I liked about Rails is that it's the only web framework people in the Ruby community use to build web apps. In the Java world, managers and business types would say "We're a Java shop" or "We're building our app in Java", but the reality is that there are so many different frameworks and saying you are building it in "Java" could mean one of a hundred different things. That might really mean "JSF/Struts 2/Spring/Hibernate" or "Freemarker/Stripes/Guice/iBatis". Matt Raible has basically built his entire career around analyzing all the different options there are for Java web frameworks, as well as developing a meta-framework to help tie them all together. Up until now, on the Ruby side of things, if someone says we're building an app with Ruby on Rails, you know what you are talking about.

But it seems this could be the start of trend, where we get more frameworks within the Ruby community, which is a good thing because you can make choices, but on the other hand it could start to fragment the community. Overall I think it's a win and the reality you come to realize after developing with Rails for a year or so is that Ruby is really the thing that makes the whole thing go and that knowledge is easily portable to other Ruby frameworks. To make it even easier, Merb and Data Mapper both have many of the same concepts as Rails, so I can't imagine the learning curve of switching from one to the other to be particularly steep.

Another crazy idea is that with specialized frameworks like Merb and Data Mapper that are meant to be just one part of a stack rather than the full stack like Rails, and JRuby becoming a legitimate option, you could start to see some weird hybrid Ruby/Java apps, like Merb+Hibernate, or Stripes+Data Mapper. I doubt that would be common, but you could do it.

So talk is cheap, let's get something going. To install Merb and Data Mapper, I followed these instructions for setting up Merb and Data Mapper.

sudo gem install merb --include-dependencies
sudo gem install datamapper
sudo gem install ruby2ruby --include-dependencies
sudo gem install merb_datamapper
cd /usr/local/lib/ruby/gems/1.8/gems/datamapper-0.2.3
sudo bash -c "ARCHFLAGS='-arch i386' rake dm:install:mysql"

Believe it or not that actually worked for my on my Macbook. That last step just outputs the following if it works:

(in /usr/local/lib/ruby/gems/1.8/gems/datamapper-0.2.3)

Here's to hoping I can just do this at some point in the future:

sudo gem install merb_datamapper_mysql --include-dependencies

But you can't for now. So I continued following the instructions and got this error when I tried to run rake:

paulbarry@paulbarry: ~/projects/foo $ rake
(in /Users/paulbarry/projects/foo)
Using pure ruby JSON lib
rake aborted!
no such file to load -- json/pure
/Users/paulbarry/projects/foo/rakefile:10
(See full trace by running task with --trace)

Why didn't gem install merb --include-dependencies install json and json_pure for me if they are required dependencies? Good question, I don't know, but we'll just install them now to get things going:

sudo gem install json json_pure

So after that, everything goes off without a hitch. Picking up where the instructions left off, let's at least create a hello world app. Create a file in app/controllers called hello_world.rb with the following contents:

class HelloWorld < Application
  def index
    "<h1>Hello, World!</h1>"
  end
end

Go to http://localhost:4000/hello_world and bask in the glory of your fist Merb web application. This should seem familar, except for the fact that you are naming it HelloWorld instead of HelloWorldController. How do you avoid namespace collisions when you have a User controller and a User model? Good question, I'll let you know when I find the answer.

So as promised, you can just return a string and that's when gets rendered in the browser. But you don't want to do that for real, so let's render a template. Create a file app/views/hello_world/index.html.erb and put the html in there. Change the index method to just call render, like this:

class HelloWorld < Application
  def index
    render
  end
end

And there's our hello world with a template. Ok, so what about data mapper? So step one is create file in app/models called foo.rb with the following contents:

class Foo < DataMapper::Base
  property :bar, :string
  property :created_at, :datetime
  property :updated_at, :datetime

  validates_presence_of :bar
  validates_length_of :bar, :minimum => 1
end

This also should make sense if you are Rails developer. The only difference is that we are defining our properties in the class, rather than in a migration. To create our table and a test record, start the merb console, which you do by executing merb -i, and then enter these commands:

irb(main):004:0> DataMapper::Base.auto_migrate!
=> [Foo]
irb(main):005:0> Foo.create(:bar => 'Hello, World!')
=> #<Foo:0x22c5964 @new_record=false, ...

Ok, so them we modify our controller and view to use this:

class HelloWorld < Application
  def index
    @foo = Foo.first
    render
  end
end

and:

<h1><%= @foo.bar %></h1>

And there we go, Merb and Data Mapper working together as one.

Posted in Technology | Tags Merb, Ruby, Rails, DataMapper | 18 Comments