Err the Blog Atom Feed Icon
Err the Blog
Rubyisms and Railities
  • “rake remigrate”
    – Chris on July 24, 2006

    There is a confession in this. The confession that we, while in development crunch mode, do not use migrations. We use them when our apps go to production, but the last project would have had over 200 migrations by the time it went live. A development DB is a volatile thing, especially when features are constantly being changed and added. “Yet,” you say, “this is the very problem migrations were created to solve.”

    Let me propose another solution: rake remigrate. We keep our initial migration under version control. When a change is made, we svn up and run rake remigrate. This task deletes and recreates our development database, runs the initial migration, then loads our fixtures. When we take our app live we begin creating new migrations, as the schema has been solidified.

    This method is advantageous if two people are working on a new migration at the same time. Version control is designed for this situation and new files don’t have to be added with the potential for conflict. It also ensures the entire development team always has the proper fixtures loaded. Plus, who wants to constantly write alter table when you can just change the schema definition?

    Without further adieu, enjoy rake remigrate:

    desc "Drop then recreate the dev database, migrate up, and load fixtures"
    task :remigrate => :environment do
      return unless %w[development test staging].include? RAILS_ENV
      ActiveRecord::Base.connection.tables.each { |t| ActiveRecord::Base.connection.drop_table t }
      Rake::Task[:migrate].invoke
      Rake::Task["db:fixtures:load"].invoke
    end
    

    Just stick it in rails_root/lib/tasks (named something like remigrate.rake) and you’re ready to remigrate.

    Update: As topfunky points out, this is currently MySQL-only. The Postgres adapter doesn’t have the needed recreated_database (or any database create / drop) methods.

    Update 2: Thanks to erik’s critical thinking prowess we now have a database agnostic rake remigrate. Three cheers!

  • Dave Snider, 1 day later:

    I don’t get it. Does this come with a javascript rollover?

  • topfunky, 14 days later:

    Very useful! I was doing this another way, but this will work much better.

  • topfunky, 14 days later:

    Hmm…appears to be MySQL-only. The Postgres adapter doesn’t have the recreate_database method.

  • Chris, 14 days later:

    Doh, thanks for checking it out topfunky. A warning has been issued.

  • erik, 17 days later:

    replacing:

    ActiveRecord::Base.connection.recreate_database ActiveRecord::Base.connection.current_database

    with:

    ActiveRecord::Base.connection.tables.each do |t| ActiveRecord::Base.connection.drop_table t; end

    seems to work with sqlite, maybe even in the general case?

  • Eric Anderson, 18 days later:

    I love the utility of this task but I am having some problem preventing me from using it. I have some records that are created as part of my migration (i.e. not fixtures). About 20 of them. When I run this task two records get created both completely empty. I tried separating the task into two tasks (one to clear the other to migrate and load fixtures) but that didn’t seem to help either. Not sure the reason but I would love to know why.

    Thanks for the task anyway. Maybe when my rails skills get better I can figure out why.

  • Eric Anderson, 22 days later:

    Nevermind my last comment. Forgot that fixtures will wipe out data in the tables including data that was in the migration.

  • Brian Donovan, 5 months later:

    Since we’re using FK constraints we can’t just go dropping tables in any old order. Instead this is the uglified solution we’re using, with some production protection baked in.

    desc "Drop and recreate the database."
    task :recreate => :environment do
      if RAILS_ENV == 'production'
        if ENV['CONFIRM'] != 'YES'
          puts "Canceled to prevent accidental dropping of the production database. Please run with CONFIRM=YES to proceed."
          break
        end
      end
    
      config = ActiveRecord::Base.configurations[RAILS_ENV]
      case ActiveRecord::Base.connection.adapter_name
      when /postgres/i
        ActiveRecord::Base.connection.disconnect!
        `sudo -u #{config['username']} dropdb #{config['database']} && sudo -u postgres createdb #{config['database']}`
        ActiveRecord::Base.establish_connection(config)
      when /mysql/i
        `echo "DROP DATABASE #{config['database']}; CREATE DATABASE #{config['database']}" | mysql -u #{config['username']}`
      when /sqlite/i
        `rm #{RAILS_ROOT}/#{config['database']}`
      end
      Rake::Task['db:migrate'].execute
    end
    
  • Matt, 9 months later:

    You might want to look at:

    http://svn.integrumtech.com/public/plugins/rake_tasks/

    It has a nice rake db:reset rake command which drops, creates and then migrates the database for your current RAILS_ENV

  • Mihai, about 1 year later:

    Whats with the second case ?

  • Ten people have commented.
    Chime in.
    Sorry, no more comments :(
This is Err, the weblog of PJ Hyett and Chris Wanstrath.
All original content copyright ©2006-2008 the aforementioned.