Migration Data Dumper Plugin

written about over 2 years ago |
0 comments

Migration Data Dumper plugin

This plugin adds two functions for use in migrations that will save and restore data for a single table into a fixture file.

  • save_table_to_fixture dumps data from the table into a fixture file.
  • restore_table_from_fixture copies the data from a fixture file into the table.

The fixtures are kept under #{RAILS_ROOT}/db/data/#{RAILS_ENV}/, and can then be version controlled (via svn, cvs, etc).

Example

Let’s say we create a posts table…


  class PostsTable < ActiveRecord::Migration
    def self.up
      create_table "posts", :force => true do |t|
        t.column "title", :string, :default => "", :null => false
        t.column "text", :text, :default => "", :null => false
      end

      restore_table_from_fixture("posts")
    end

    def self.down
      save_table_to_fixture("posts")
      drop_table "posts" 
    end
  end

When you migrate down (via rake migrate VERSION=0), the data for the posts table is saved in db/data/development/posts.yml, and that data is pushed back into the table when you migrate back up (via rake migrate).

Rake Tasks

(Thanks to Richard Livsey for this addition)

MMD now ships with a couple of rake tasks to manage your table fixtures outside of ruby:


rake --tasks
<snip>
rake db:restore_table_from_fixture             # Restores a database table from a yaml fixture
rake db:save_table_to_fixture                  # Saves a database table to a yaml fixture

Cause there’s always gotta be a gotcha…

This works great until you have to edit an existing table. Consider adding a column to the posts table:


  class DraftFlagForPosts < ActiveRecord::Migration
    def self.up
      add_column "posts", "draft", :integer, :limit => 4, :default => 1, :null => false
      Post.reset_column_information
      Post.find(:all).each { |p| p.draft == 0 }
      restore_table_from_fixture("posts")
    end

    def self.down
      save_table_to_fixture("posts")
      remove_column "posts", "draft" 
    end
  end

With these two migrations, if you run rake migrate VERSION=0, the following things will happen:

  1. db/data/development/posts.yml will be created (by migration 002) with the latest version of the table (including the draft column).
  2. db/data/development/posts.yml will be recreated (by migration 001) with the latest version of the table (including the draft column).

This is an error, but doesn’t cause any real problems just yet. The problem arrises when you attempt to migrate back up (via rake migrate). The 001 migration attempts to restore the yaml file, but cannot, since the specified draft column doesn’t exist in the table.

Version to the rescue

If we add the version parameter to the calls to save_table_to_fixture and restore_table_from_fixture in the 002 migration like such:


  class DraftFlagForPosts < ActiveRecord::Migration
    def self.up
      add_column "posts", "draft", :integer, :limit => 4, :default => 1, :null => false
      Post.reset_column_information
      Post.find(:all).each { |p| p.draft == 0 }
      restore_table_from_fixture("posts", ".draftcol")
    end

    def self.down
      save_table_to_fixture("posts", ".draftcol")
      remove_column "posts", "draft" 
    end
  end

Then the latter fixture is named db/data/development/posts.draftcol.yml, avoiding the whole issue.

Update

Thanks to Richard Livsey for adding the rake tasks.

Caveats

This is in use in my personal projects, but not highly tested. It is almost definitely MySQL and UNIX dependent, since I’m too lazy to change those things. Enhancements and bug fixes are very welcome.

Download

You can grab this plugin from the RubyForge account:


./script/plugin install -x svn://rubyforge.org/var/svn/mdd

Leave a comment...

formatting help...