Super Rescue

“Super rescue” is not a superhero, it’s a trick for Ruby on Rails developers that, indeed, may rescue you when you’re in trouble.

It involves models’ methods defined like:

class BlogPost < AR::Base
  def language
    super rescue 'en'
  end
end

Yes, that’s actual Ruby.

Generally speaking, it means “attempt to get language in the way of my super class, and if it fails, let’s assume it’s English”.

In practice, Super Rescue is handy when Rails’ rake db:migrate won’t run throughout. The main theme is:

Compatibility with former versions of the database schema

There’s a quite common situation in a Ruby on Rails application that undergoes many iterations : the application code is in sync with the last version of your migrations, not with former ones.

The OMG scenario is the following:

  • In a recent migration, you added a brand new model, or a column to another, and you source code reflected this changes by updating a validation, or adding a callback like after_save that does some important stuff.
  • In a former migration, you actually fill in the database with initial data (like an initial admin user, or an initial greeting post, whatever).
  • You deploy a brand new copy of your application, and trustfully rake db:migrate to setup your brand new empty database.

You see the pattern: as migrations run through from the beginning, they’ll eventually trigger parts of your present code that references columns or models that do not exist yet.

An exception abruptly wakes you up: you encounter the incompatibility-with-former-versions-of-the-database-schema syndrome.

Super rescue to the rescue!

You think your migration complains because a column does not exist (yet)?

That’s not quite the case.

Database columns do help Rails building getter and setter methods in your ActiveRecord subclasses. These methods are the indirect way column values are usually accessed.

So, what really do not exist are ruby methods.

So, let’s fool Rails. The language column does not exist yet in the blog_posts table, er, the language method does not exist yet in the BlogPost class? Take that, Rails!

class BlogPost < AR::Base
  def language
    super rescue 'en'
  end
end

Precisely speaking, it means: “let ActiveRecord handle the database access as usual, but should the column not exit, assume a default value”.

And now your

class BlogPost < AR::Base
  validates_presence_of :language
end

will succeed, even if the column is still to be created by a future migration.

Super rescue getter/setter

The getter/setter pair can be implemented as:

class BlogPost < AR::Base
  def language
    super rescue (@language ||= 'en')
  end
  def language=(value)
    super rescue (@language = value)
  end
end

And now your

class Blog < AR::Base
  has_many :blog_posts
  def after_create
    blog_posts.create(:content => 'This is your first post', :language => 'en')
  end
end

will work like a charm, despite the lack of the language column.

That’s the trick. Simple, effective, and harmless.

It’s not a panacea. It’s unable to handle complex situations. The incompatibility-with-former-versions-of-the-database-schema syndrome lost a battle, not the war.

Thank you for reading.

Posted on novembre 28, 2007 by Gwendal Roué - permalink

1 comment

Written on décembre 03, 2007, 02h02 by np

Quite useful thanks !!

Add your comment

Name
Email address
(your email won't be displayed)
Web site
Comment

Are you able to answer this simple question?

Release of the SearchAPI plugin