Last Updated: February 25, 2016
·
10.32K
· vaneyckt

Adding a post execution hook to the rails db:migrate task

Preface: The original article for this post has since been moved to here on my personal blog.

It can be really useful to have rake automatically execute another task after db:migrate has been run. Such a task could be used to quickly check the correctness of the charset and collation of your database tables. This can be a lifesaver in those cases where an imperfect database upgrade had quietly changed the default charset and collation values.

Appending to an existing rake task can be done by redefining it. Though the code below may give the impression of overwriting the db:migrate task, the new code is actually just getting appended to it.

namespace :db do
  def my_appended_code
    puts 'this code gets run after the original rails db:migrate task'
    puts 'it only runs if the migration did not throw any exceptions'
  end

  task :migrate do
    my_appended_code
  end
end

However, the above example only runs the appended code if the original db:migrate task does not throw any exceptions. We need to take a slightly differrent approach if we want to ensure our appended code always gets run. Rather than appending our code, we are going to prepend it instead.

namespace :db do
  def my_appended_code
    puts 'this code gets run after the original rails db:migrate task'
    puts 'it will ALWAYS run'
  end

  task :post_migration_hook do
    at_exit { my_appended_code }
  end
end

Rake::Task['db:migrate'].enhance(['db:post_migration_hook'])

Here we make use of the enhance method to add a prerequisite task to db:migrate. This prerequisite task adds an at_exit hook that causes our appended code to get called when the db:migrate task exits. Furthermore, we could have this hook be executed only upon certain exceptions by using the $! variable to access the exception object.

As an aside, the enhance method can also be used to append code to a given rake task when used as shown below. Note that this approach causes the appended code to not be run in case of an exception.

Rake::Task['db:migrate'].enhance do
  my_appended_code
end