Rails Rake Tasks Plugin

Some really convenient rake tasks for some painfully common Rails tasks.

Just like everyone else who develops software, we found ourselves constantly typing the same commands over and over for a variety of little tasks while developing Rails apps. Over the course of a number of projects, we developed this collection of rake tasks to automate many of them. I find the db:remigrate, db:migrations:test, db:migrations:merge, and testing convenience tasks the most useful.

Most (if not all) of these tasks have no doubt been created before, by numerous people, and I don’t claim to have invented any of them — in fact, most of them are fairly obvious. If you have any useful tasks of your own that you think we should add, let us know.

Some tasks assume you are using Subversion for version control and MySQL as a database. Others assume you have the GNU Coreutils installed. I could abstract them further to remove some of these dependencies, but we use Subversion and MySQL and the Coreutils, so, hey. If you make any changes to make them work with other software, let us know and we’ll post a link here.

Update: Yes, indeed, many of these tasks have been obviated in Rails 2.0. But, many people are still using older versions of Rails, and I’ll be releasing a Rails 2.0 version of the plugin that makes use of the new tasks where appropriate.

Installation

As easy as:

script/plugin install -e https://dev.wearesakuzaku.com/svn/public/rails_plugins/sakuzaku_rake_tasks/trunk

Alternatively, with Piston:

piston import https://dev.wearesakuzaku.com/svn/public/rails_plugins/sakuzaku_rake_tasks/trunk vendor/plugins/sakuzaku_query_dehydrator

Or, you can browse the source directly.

Usage

app:initialize

Initializes a newly-created Rails application, by making the following changes to the app’s Subversion working copy:

This task assumes your new Rails app has been imported into Subversion, and that you’re running the task on a freshly checked-out working copy. This task probably isn’t very useful, however, unless installing this plugin is the very first thing you do after importing the app into Subversion. That precondition happens to be true in my case, so it comes in handy. But if you want this task available without installing the plugin, perhaps you should check out Sake.

db:empty

Removes all the data from the current environment’s database, but does not drop the database itself.

db:recreate

Drops the current environment’s database and recreates it.

db:remigrate

Drops and recreates the current environment’s database and reruns all migrations.

Because this task is destructive, by default it is configured to not run on the production database. To change the databases that destructive tasks won’t run on, you can edit the PROTECTED_ENVIRONMENTS constant at the top of tasks/database.rake.

db:migrate:all

Migrates all databases to bring each environment’s database up to the latest database version. You can target a specific database version with VERSION=x, just like with the regular db:migrate task.

db:remigrate:all

Remigrates all databases by dropping each environment’s database, recreating it, and then rerunning all migrations on it. You can target a specific database version with VERSION=x, just like with the regular db:migrate task.

Because this task is destructive, by default it is configured to not run on the production database. To change the databases that destructive tasks won’t run on, you can edit the PROTECTED_ENVIRONMENTS constant at the top of tasks/database.rake.

db:migrations:test

Tests any uncommitted migrations by remigrating the current environment’s database, migrating back to the last committed revision, and then migrating forward again. This task makes it easy for you to test that new migrations properly run both forwards and backwards.

You can manually specify the number of migrations to test with NUMBER=x. For example, if you’d like to test the last four migrations, you could do

rake db:migrations:test NUMBER=4

db:migrations:merge

Renames the files for all uncommitted migrations so that they follow, numerically, any migrations that have snuck into the codebase before you’ve had a chance to commit. This is convenient if you work on a team and new migrations have been checked into the codebase while you’re working on migrations of your own. For example, let’s say this happens to you:

% svn status db/migrate
A     004_create_houses
A     005_create_cars
?     006_create_retaining_brackets
?     007_add_fracture_length_column_to_structural_problems

% svn update
A     004_remove_lighting_director_column_from_ballet_recitals
A     005_create_incunabulas

% ArrGGHH!!!!! NOT AGAIN!!!!!!!asdlfjlsdfjlskdfj
zsh: event not found: asdlfjlsdfjlskdfj
ArrGGHHsvn updatesvn update! NOT AGAINsvn updatesvn updatesvn update

% ls db/migrate
...
004_create_houses
004_remove_lighting_director_column_from_ballet_recitals
005_create_cars
005_create_incunabulas
006_create_retaining_brackets
007_add_fracture_length_column_to_structural_problems

It can quickly become a pain to have to constantly revert the migrations you’ve added and manually rename them, especially if you like to keep your working copy updated and a lot of other team members are making frequent commits. With this task, all you need to do to resolve the above naming conflicts is

% rake db:migrations:merge

% svn status
A     006_create_houses
A     007_create_cars
?     008_create_retaining_brackets
?     009_add_fracture_length_column_to_structural_problems

Don’t forget to remigrate your databases after running this task.

db:fixtures:generate

Create YAML test fixtures from the data in the current environment’s database. A separate fixtures file will be created in test/fixtures/environment for each database file, with each individual fixture given a numeric index. This is a good way to avoid having to type a lot of YAML if you, like me, like to create your test fixtures directly in the database first.

test:complexity

Produce code complexity reports for the application with Saikuro. The reports are output to test/complexity. You must have Saikuro installed and in your path for this task to work — you can simply type saikuro at your shell prompt to find out whether that’s the case.

test:coverage

Produce aggregate code coverage reports for unit, functional and integration tests with RCov. The reports are output to test/coverage. You must have RCov installed and in your path for this task to work — you can simply type rcov at your shell prompt to find out whether that’s the case.

You might like Coda Hale’s task better for this, but hey — I only found that after we had already written our own.

Testing convenience tasks

Dynamic tasks to automatically run specific unit and functional tests without a lot of typing. These tasks are a big time-saver.

Running All Tests for a Model or Controller

To run all unit tests for a particular model and/or all functional tests for a particular controller, just do

rake target

where target is the name of the model or controller. If there are both a model and controller with the given name, the tests for both are run.

Example:

# The following is equivalent to:
# ruby test/unit/user_test.rb ; ruby test/functional/user_controller_test.rb
rake user

To run only a unit or functional test (but not both) for a model or controller:

rake target-type

Where target is the same as above, and type is either u (for the unit test) or f (for the functional test).

Example:

# The following is equivalent to:
# ruby test/unit/user_test.rb
rake user-u

# The following is equivalent to:
# ruby test/functional/user_controller_test.rb
rake user-f

Running a Specific Test Only

To run a specific individual unit or functional test for a model or controller, just do

rake target:test

where target is the name of the model or controller, and test is the name of the particular test to run.

Example:

# The following is equivalent to:
# ruby test/unit/user_test.rb -n /^test_name/ ; ruby test/functional/user_controller_test.rb -n /^test_name/
rake user:name

If both a unit and functional test for exist with the given name, then both will be run. To restrict the specific individual test that is run, you can use the same syntax as above:

# The following is equivalent to:
# ruby test/unit/user_test.rb -n /^test_name/
rake user-u:name

# The following is equivalent to:
# ruby test/functional/user_controller_test.rb -n /^test_name/
rake user-f:name

Bugs

December 15, 2007

Takeshi Akima informed me that the db:empty task doesn’t work with an Oracle database. Since this plugin is primarily for use with MySQL, and because we don’t use Oracle and therefore have no way to test with it, I have included Akima-san’s patch for download instead of integrating it into the plugin: