Reversible Migrations

Migrations have always been considered one of the many killer features in Rails. And in Rails 3.1 Migrations got a new trick up their sleeve that will greatly simplify the process of maintaining both the up and down logic. If you need a little refresher on what migrations are then I suggest reading the official Rails guide.

Lets start by looking at how a typical migration looks like in Rails 3.0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class CreatePosts < ActiveRecord::Migration
  def self.up
    create_table :posts do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end

  def self.down
    drop_table :posts
  end
end

This migration creates a posts table with two fields — title and body of type string and text, respectively. The timestamps helper creates datetime fields — created_at and updated_at for free. To reverse this migration we simply need to drop the posts table. The class method down does precisely this. When Rails is applying a migration it runs the class method up. To reverse the migration (as can be done with rake db:rollback) it runs the class method down.

Two questions come up when you look at this migration —

  • Why class methods instead of plain ‘old instance methods?
  • For simple cases why not just define the up migration and have Rails take care of reversing the migration?

The ever awesome Aaron Patterson thought the same thing and decided to simplify things for you and I.

Introducing change

If you run the following command in Edge Rails, the genarated migration will look something like the example below:

1
2
3
4
5
6
7
8
9
10
11
12
(~/code/migrahedron) rails g model Post title:string body:text

class CreatePosts < ActiveRecord::Migration
  def change
    create_table :posts do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end
end

By default Rails 3.1 will generate migrations for models using the change method that will hold the up logic. When a rollback is requested Rails will figure out how to reverse the migration for you merely by examining the ‘up’ direction directives. Go ahead and apply the migration and then rollback. You should see something like the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
(~/code/migrahedron) rake db:migrate
(in /home/rohit/code/migrahedron)
==  CreatePosts: migrating ============================
-- create_table(:posts)
   -> 0.0012s
==  CreatePosts: migrated (0.0013s) ===================

(~/code/migrahedron) rake db:rollback
(in /home/rohit/code/migrahedron)
==  CreatePosts: reverting ============================
-- drop_table("posts")
   -> 0.0007s
==  CreatePosts: reverted (0.0008s) ===================

Notice how Rails has figured out that in order to reverse the migration, it needs to drop the newly created table.

What about commands that can’t be reversed?

There are certain commands like remove_column that cannot be automatically reversed. This is because the information required to re-create the column is not available in the remove_column command. If Rails encounters such commands while reversing a migration, an ActiveRecord::IrreversibleMigration exception will be raised.

1
2
3
4
5
class RemoveTitleFromPost < ActiveRecord::Migration
  def change
    remove_column :posts, :title
  end
end

If you try rolling back the above migration you will get something like:

1
2
3
4
5
6
7
8
9
(~/code/migrahedron) rake db:rollback
(in /home/rohit/code/migrahedron)
==  RemoveTitleFromPost: reverting ====================
rake aborted!
An error has occurred, this and all later migrations canceled:

ActiveRecord::IrreversibleMigration

(See full trace by running task with --trace)

If you want to handle such cases manually you can still define the up and down methods almost like before.

up and down instance methods

The only change to the old up and down methods is that they are now instance methods. Say good bye to those awkward self.up and self.down method definitions.

1
2
3
4
5
6
7
8
9
class RemoveTitleFromPost < ActiveRecord::Migration
  def up
    remove_column :posts, :title
  end

  def down
    add_column :posts, :title, :string
  end
end

More magic? :(

If you’re wondering how migration reversal is determined, and in the spirit of Jose Valim’s wish to see all Rails magic deconstructed, I thought I’d give a brief idea as to how Rails is reversing a migration automagically.

The magic, erm I mean heavy lifting, happens in the ActiveRecord::Migration::CommandRecorder class. Basically if you define a change method in your migration and are applying the migration then the commands are executed as normal.

However while reversing the migration, the commands are recorded and a list of inverse commands is generated and run. Inverse commands are simply commands that perform the opposite of the original command. For eg: the inverse of rename_table(old, new) is rename_table(new, old). The logic to obtain an inverse of a command is provided in the class itself. For those commands whose inverse cannot be obtained, ActiveRecord::IrreversibleMigration is raised.

That was a very simple overview of what is happening behind the scenes. I encourage you to take a look at the code for yourself to understand how it works.

Rails Tickets Newsletter

While closing a few tickets on Rails Lighthouse I had a crazy idea. Why not compile a list of unresolved tickets and send it as a newsletter! I could create categories like stale tickets, feature requests/patches, tickets needing minor work and desparate love needing tickets. This would allow developers who wish to contribute to Rails get a quick overview of tickets that need attention.

What’s wrong with Rails lighthouse?

The biggest problem with Rails LH is the growing number of stale tickets. Many of them were created long ago and aren’t even relevant now. Unfortunately finding them was made harder by the spammers. The false updates made tickets which had not been updated for months become recent.

Poorly tagged tickets are also frustrating. If you are comfortable with Active Record and you wanted to help out with related tickets — it’s not so convenient. Tickets are tagged very poorly thus your ability to focus on issues dealing with specific parts is restricted. There is also a growing list of tickets that have patches which haven’t been reviewed or even looked at. For a patch to get into Rails it needs to get at least 3 positive reviews . Unfortunately plenty of wonderful patches are just lying there.

These problems discourage developers who want to start contributing to Rails. Existing contributors also hesitate because there is a good chance that their patches will not even be reviewed. Would be bug reporters also hesitate since nobody looks at their tickets. The core team find it hard to sift through the tickets to find those that matter. Basically contributors and core members have limited time to work on Rails and most of it is spent on needless persuits.

Why Rails lighthouse needs you

We need you to confirm/deny bug reports. We need you to write patches for issues. We need you to review patches written by your peers. We need your valuable opinion on feature requests. We need you to write patches for that wonderful feature you thought of. Basically you are the life of Rails LH and Ruby on Rails.

Why a newsletter?

The main idea behind having a newsletter is to allow developers to receive a concise list of tickets that need attention. Thus allowing them to gradually enter into the world of contributing to Rails. It will also allow patches to get some eyes on them. Most importantly I’m hoping to distribute the task of classifying tickets as stale and no longer relevant thus allowing us to close these tickets.

A leaner issue tracker will allow developers to be more inclined to help out. They will be willing to write patches. Will be willing to report bugs. I strongly believe that in the long run these contributions however tiny will help Rails become even more awesome.

When is the newsletter out?

I have created a small survey with simple questions regarding the newsletter. I’ve posted it on the core list and on rubyflow.

I’ll be keeping it up for a few more days to collect as many responses as possible. I’ll post the results of the survey in another article along with the decisions I make and when you can expect the first issue to be out.

In the mean time if you are a Rails developer of any skill level, please do take the time to fill the survey and don’t hesitate to send an email if you have any questions, suggestions or would like to help.

My First Rails Bugmash

Bugmash is an event held to reduce the number of open tickets in the Rails Core Issue Tracker, encourage more people to get involved with the Rails source and have some fun. I participated in the Rails Bugmash held on the 15th and 16th of May via the #railsbridge IRC channel. It was an awesome experience, wherein I learnt a lot and luckily got a couple of patches into Rails too. I thought I should write a little look back and thank all the people who helped.

Day 1, cautious

I had made up my mind, that I would only try and reproduce the bugs on Rails 3 beta 3. I thought it would be best if I did something simple and helped out that way. One particular issue seemed interesting, so after confirming it I asked the folks in the #railsbridge channel to try and reproduce it too. That’s when Sam Elliot encouraged me to try and debug the issue. He guided me to find the relevant files using grep, and also try an alternative approach to reproduce the bug on a lower level. Although I got nowhere near finding out why the issue was happening, I got a real kick out of trying.

Although I was still cautious, I got the confidence to try and reproduce bugs on Rails master with help from Anil Wadghule. Also I was using Ruby 1.9.2-head for bugmashing and this is why I was about to get lucky.

Day 2, can you write a patch for that?

On the second day I was feeling very content with my self. I had already helped out with a few tickets, reporting back whether the issue is reproducible or not. After setting up everything, I was also excited to verify others patches and also try and reproduce bugs on Rails master. I found a patch that I would be able to not only verify but also analyse if it was a good patch.

Before applying the patch, I thought I’d best run the test suite of Railties, because that’s where the patch applied. This is the part where I got lucky, because a couple of tests failed. I immediately asked in #railsbridge if somebody could confirm it. By the time Rizwan Reza and Santiago Pastorino confirmed that the tests indeed failed, I already had an idea as to why. After discussing it with both of them, Rizwan calmly asked me if I can write a patch for that.

After panicking a little I opened up the contributor guide on the issue tracker and followed it word to word resulting in my first patch. I submitted it to the issue tracker and within a few hours it was accepted into the Rails repo. I had become a Rails core contributor, besides the fixes to the guides. I found similar errors in ActionPack and made a patch for them too, which also got accepted. Even though the patches were very simple fixes to tests, I felt incredible!

Happily ever after

In the end I finished 5th on the official scoreboard with 2575 points. After a rocking Bugmash I found it difficult to stay away from Lighthouse. I even got one more patch accepted into Rails. It was simply awesome to get involved in the Bugmash. I also built Gimme Ticket for people like me, who missed the !gimme command of Mashie the bot from #railsbridge. It could crash randomly though, I’ll be fixing that soon’ish.

I have to thank José Valim, core team member, for discussing the issues and of course accepting the patches. I also have to thank Ryan Bigg for encouraging people to try and contribute to Rails. I hope to continue contributing to Rails and possibly other open source projects too. In case I have forgotten to thank any one I apologize.

Installing Rails 3.0 Beta 3 on Ubuntu Using RVM

After struggling for sometime I finally got Rails 3.0 beta 3 to work on Ubuntu. Instead of creating a virtual machine I gave RVM a try — and now I love RVM. I thought I’d best dump what worked for me in here.

If you don’t know what RVM is then read what Ruby Inside has to say about it. However for installation instructions I recommend the official installation guide. What follows is how to prepare RVM to compile Ruby with openssl and readline, install Ruby 1.9.2-head and finally install Rails 3.0 beta 3. I will try my best to keep it as short as possible.

Initial set up

I use Ubuntu 9.10 64-bit edition and assume that you are using a relatively new release of Ubuntu if not 9.10. You will also need RVM installed.

Let’s install some dependencies which will be required to compile Ruby. After that we install the openssl package for RVM. We need this package to compile Ruby with openssl because Rails will not start without it. Similarly we install the readline package without which the Rails console will not work.

  
  # Dependencies for compiling Ruby
  $ sudo apt-get install curl bison build-essential autoconf zlib1g-dev libssl-dev libxml2-dev libreadline6-dev git-core subversion

  # openssl package for RVM
  $ rvm package install openssl

  # readline package for RVM
  $ rvm package install readline
  

Installing Ruby

I wanted to try Ruby 1.9.x so I didn’t try older patchlevels of 1.8.7, but the latest patchlevels of Ruby 1.8.7 have marshalling bugs that crash Rails. Ruby 1.9.1 segfaults for Rails 3.0 beta 3. I also tried Ruby 1.9.2-preview1 which gives problems with timezone, although I believe this is fixed in the Rails repository. What did work was Ruby 1.9.2-head and it is also recommended in the release notes of the second beta release of Rails 3.0. Note that the problems with Ruby 1.8.7 and Ruby 1.9.1 could be fixed before you read this article. Moving on let’s install Ruby with openssl and readline using RVM. Note that there is no space after the comma.

  
  $ rvm install 1.9.2-head -C --with-openssl-dir=$rvm_path/usr,--with-readline-dir=$rvm_path/usr
  

This will get the latest source code from the SVN repository, configure it and then compile the source code. It will also install a few gems and set up all the paths. However RVM will not switch into this Ruby. After installation you will still be with your system Ruby.

Once the installation process is complete run the following commands to confirm that the installation was successful and finally switch to Ruby 1.9.2-head. Note that I have provided the output from my console as a sample. It will most likely be different for you.

  
  $ rvm list
  =>
  rvm Rubies
  ruby-1.9.2-head [ x86_64 ]
  System Ruby
  system [ ]

  $ ruby -v
  => ruby 1.8.7 (2009-06-12 patchlevel 174) [x86_64-linux]

  $ rvm use 1.9.2-head
  => Using ruby 1.9.2 head

  $ ruby -v
  => ruby 1.9.2dev (2010-04-17 trunk 27376) [x86_64-linux]
  

Install Rails 3.0 beta 3

Installing Rails is now very easy. Note that sudo is not used to install the gems. If you use sudo the gems will be installed in the system directories. We don’t want that because RVM maintains the Rubies and gems inside the home directory of the user.

  
  $ gem install sqlite3-ruby
  $ gem install rails --pre
  

If you want to use MySQL or some other database then install the necessary gems. That is it! You should have a working Rails 3.0 install now. Remember that every time you open a new terminal you will need to switch to Ruby 1.9.2-head. You can also set this as the default Ruby to be used. Have fun playing around with Rails 3.0 beta 3!