database_resetter
Note: I used to use darcs as the source control tool for this project. I decided to move to git as other people will find it easier to collaborate on GitHub. As it was small enough, I decided to manually port it on a one-git-commit-per-darcs-patch basis. So (in case you looked at the commit history), no – I didn't write the entire thing in 20 minutes. (How I wish I could code that fast.)
Introduction
What does it do?
database_resetter is a small gem to handle automatically resetting your test databases when developing a Ruby web app.
Why do I want it?
Because you're sick of seeing "No column XXX in table YYY" or some such, because your test database is out of sync with your migrations. If you're making a lot of database changes, this can save you a fair bit of time.
I've used this code almost unmodified in all of my clients' projects over the last year or two. Pretty much everyone that's seen it has said it looked pretty handy, so I've bundled it up as a gem.
How does it work?
database_resetter writes timestamp files into log/database_resetter that track when the database was last reset. It tracks them:
- per environment (eg test, cucumber)
- per hostname
Per-hostname tracking is done in case you use Dropbox/JungleDisk etc to sync code between machines. (Note: there are no features/specs to prove this works.)
What isn't tracked is what directory / SCM branch you're in. So if you switch to a different working copy folder, or change eg Git branch, database_resetter may not run the migrations. If you'd like this feature please contact me.
The easiest way to make database_resetter run is to touch a migration file.
How is database_resetter different from database_cleaner?
Ben Mabey wrote database_cleaner to help with emptying a database between individual test cases. database_resetter is designed to get the initial schema of a database in place before any test cases run.
Basically, you want both :-)
Instructions
If you're working on a Rails app, and your tests are good to go after a
rake db:reset
, then place the following code somewhere that
will get executed once per test run:
DatabaseResetter.new.reset_if_required
Note: there are no features to prove this default call actually works :) I've always specified the options explicitly myself. But the defaults should work with Rails. If not, let me know.
For instructions on specifying the options, see below.
Changing the Rake task
If (like me), you don't use schema.rb to regenerate databases,
you'll need your own Rake task. I use one called rake db:rebuild
,
which is currently:
desc "Reset and re-migrate the database"
task :rebuild do
Rake::Task["db:drop"].invoke
Rake::Task["db:create"].invoke
Rake::Task["db:migrate"].invoke
end
To override the default command, specify the :command_pattern
option:
DatabaseResetter.new(
:command_pattern => "rake db:rebuild RAILS_ENV=%ENV%"
).reset_if_required
See below for a description of %ENV%
.
Using a different framework
If you use a different web framework, you may have to specify a different
environment variable to switch the framework environment (eg MERB_ENV
or RACK_ENV
). To override this, specify both :command_pattern
and
:environment
, eg:
DatabaseResetter.new(
:environment => ENV["MERB_ENV"],
:command_pattern => "rake db:reset MERB_ENV=%ENV%"
).reset_if_required
You may also need to specify a different migration file directory to watch, eg:
DatabaseResetter.new(
:migration_directory => "schema/migrations"
).reset_if_required
(You can, of course, combine all three.)
RSpec
Put the call to DatabaseResetter#reset_if_required
in spec/spec_helper.rb.
Cucumber
Put the call to DatabaseResetter#reset_if_required
in
features/support/env.rb.
Spork
If you use Spork, you want to call DatabaseResetter#reset_if_required
in the
each_run
block, eg:
Spork.each_run do
# ...
DatabaseResetter.new.reset_if_required
# ...
end
PostgreSQL
If you use PostgreSQL, and your Rake task attempts to drop the database, Postgres will stop you if there is a connection in place. You need to work around this somehow. If you're using Spork, you need to divert Postgres's eyes, eg in Rails:
Spork.each_run do
# ...
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
DatabaseResetter.new.reset_if_required
ActiveRecord::Base.establish_connection(config)
# ...
end
If you're not using Spork, you still need to make sure you call
DatabaseResetter#reset_if_required
before connecting to the database.
One way of doing this with Bundler is to define a Bundler group just
for database_resetter in your Gemfile:
group :database_resetter do
gem "database_resetter"
end
And then use this to load and run database_resetter before anything connects to the database. eg with Rails and Cucumber, in env.rb:
# ...
require 'bundler'
Bundler.setup(:database_resetter)
require 'database_resetter'
DatabaseResetter.new.reset_if_required
require File.expand_path(File.dirname(__FILE__) + '/../../config/environment')
# ...
Bugs / Feedback
There is currently no project page for database_resetter. If you want to report bugs in the code or documentation, or request features, please contact me:
Source code
database_resetter is maintained in a darcs repository, and is currently hosted on Patch-Tag: