Recent Projects

Automatically Creating, Loading, and Migrating your Database (with magic!)

So, let’s say you’re working on this open source Rails app, and you’re doing your best to keep the requirements light and the installation as easy as possible. You’d love to have that Famous 5-Minute Install that WordPress has going for it, and you start to wonder…

Why should I even have to worry about the database?

Why can’t my app take care of itself? Why can’t it automatically create and maintain the database for me?

Well friend, I just wrapped up a little initializer that will automatically create the db, load the schema, and/or migrate as necessary. It all seems fine so far, but I still have this funny feeling that I’m doing something wrong…

/config/initializers/db_create_load_or_migrate.rb

I dunno… Have I gone too far this time?

unless defined?(Rake)
  load "#{RAILS_ROOT}/Rakefile"
  begin
    current_version = ActiveRecord::Migrator.current_version
    highest_version = Dir.glob("#{RAILS_ROOT}/db/migrate/*.rb").map { |f| f.match(/(\d+)_\w*\.rb$/) ? $1.to_i : 0 }.max
    Rake::Task["db:migrate"].invoke if current_version != highest_version
  rescue
    Rake::Task["db:create"].invoke
    abort 'ERROR: Database has no schema version and is not empty' unless ActiveRecord::Base.connection.tables.blank?
    Rake::Task["db:schema:load"].invoke
    retry
  end
end

Addendum

Thanks to the Fail Early chapter of the Advanced Rails Recipes book for turning me onto the idea of (at least) checking for an up-to-date schema with an initializer.

Also, make sure to check out this nifty trick that lets you run Rake tasks from within a Rails app. It’s pretty easy – all you need to do is load the Rails Rakefile and use .invoke:

load "#{RAILS_ROOT}/Rakefile"
Rake::Task["db:create"].invoke

P.S. I spent the last hour re-enabling comments on this site, just so somebody like you can tell me that I’m not allowed to do this and why. So, please, have at it!

Update: I updated the regex thanks to a patch that Ben sent to deal with more kinds of directory names.

Update 2: After living with this for a while, I decided to take it out of El Dorado for the time being. I still think it’s an interesting idea, but it’s too risky to play with production data like this, and I think it’s better to be on the safe side. Plus, the Rails developers I’ve spoken to about it seem to think that it’s introducing unexpected behavior into the app (e.g. auto-migrations are not the norm).