Plugin Migrator: migrations for your Rails plugins

Posted by collin
on Monday, May 26

Overview

My current work involves writing a handful of Rails plugins. These plugins provide additional functionality that includes ActiveRecord models that need to be persisted to the database. I originally created a migration class for one of the plugins that re-used much of the ActiveRecord logic (in fact, just overrides schema management). This worked fine, but as we started creating new plugins that needed the same functionality, we decided to pull the migration logic into a separate Rails plugin.

And hence, the PluginMigrator was born!

In order to use the PluginMigrator, your plugin must simply extend the PluginMigrator::Migrator class:

module MyPlugin
  class Migrator < PluginMigrator::Migrator

    set_schema_table_override "my_plugin_schema_info" 
    set_migration_directory(File.dirname(__FILE__) + "/../../db/migrate") 

  end
end

The set_schema_table_override method tells the plugin where the version info for your plugin should be stored. For rails apps, this schema information lives in a table named “schema_info”. You’ll need to specify a different table name for your plugin, so that your plugin migrations can be managed separately from the Rails app. Don’t worry if it doesn’t exist yet – the migration system will automatically create it.

The set_migration_directory method tells the plugin where to find the migrations. The migrations for your plugin should probably be stored in the same way as the main rails app. In your plugin root, it’s easy to just have a db/migrate structure:

To actually migrate, simply create a Rake task in your tasks directory inside of your plugin or in the main Rakefile for your plugin, depending on from where you want to run the migrate task:

namespace :myplugin do
  desc "Run the migrations for my plugin" 
  task :migrate => :environment do
    MyPlugin::Migrator.migrate(ENV['VERSION'],false)
  end
end

And then:

% rake myplugin:migrate

Typical VERSION behavior is also supported:

% rake myplugin:migrate VERSION=1

Installation

The PluginMigrator project is hosted at GitHub.

From the Terminal:

~/testapp $ cd vendor/plugins
~/testapp/vendor/plugins $ git clone git://github.com/oculardisaster/plugin_migrator.git

If you don’t have Git on your system, you can simply go to the main project page and click the download button.

Known Issues and Planned Features

  • Would like to be able to optionally exclude having the main rails app include plugin tables when writing out the schema.rb file during a Rails app migration.
  • Would like to have the plugin migrator automatically create rake tasks to migrate the plugin
Comments

Leave a response

  1. Bui The HoaJune 22, 2008 @ 12:46 AM

    Hi Collin

    Thank you for the plugin. Following the instructions, I encounter the following error when trying to run rake rake myplugin:migrate (in /mnt/hoabt/Project/Lab/Rails/migrator) rake aborted! undefined method `schema_info_table_name_without_plugin’ for class `Class’

    Running $rake spec, I’ve got this rake spec (in /mnt/hoabt/Project/Lab/Rails/migrator/vendor/plugins/plugin_migrator) (in /mnt/hoabt/Project/Lab/Rails/migrator/vendor/plugins/plugin_migrator/test/.rails_root) (in /mnt/hoabt/Project/Lab/Rails/migrator/vendor/plugins/plugin_migrator/test/.rails_root) /usr/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:263:in `load_missing_constant’: uninitialized constant MyPlugin::PluginMigrator (NameError) from /usr/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:453:in `const_missing’ from /mnt/hoabt/Project/Lab/Rails/migrator/vendor/plugins/plugin_migrator/test/.rails_root/vendor/plugins/my_plugin/lib/my_plugin/migrator.rb:2 from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require’ ...

    Any help is appreciated Hoa

  2. Collin VanDyckJune 22, 2008 @ 09:40 AM

    Hi Hoa,

    Sorry you’ve run into trouble. I just re-cloned the project from GitHub and was able to run the specs from that directory using “rake spec” without any problem. Are you able to do the same (e.g. just cloning to a completely separate directory and running “rake spec”?

    FWIW, I’m running OS X, ruby 1.8.6. What environment are you running?

    -Collin

  3. Bui The HoaJune 22, 2008 @ 08:17 PM

    Hi Collin

    Thanh you for your reponse

    I don’t have git installed (I mostly use Subversion) so I download the package, unzip and copy it to <rails_app>/vendor/plugins. I change the directory to the plugin_migrator then I execute $rake spec from there. Do I miss any steps.

    I try this on both Windows XP and Fedora 9, Ruby 1.8.6.

    Thanks
    Hoa

  4. Collin VanDyckJune 23, 2008 @ 08:04 AM

    Hi Hoa

    No, I don’t think you’re missing any steps. I have been meaning to install a copy of Fedora on VMWare, so I’ll test that out when I get it up and running. In the meanwhile, what happens if you run the specs from the unpacked source code outside of any existing project? For example, unpack it to your desktop and see if that works?

    -Collin

  5. Bui The HoaJune 23, 2008 @ 09:30 PM

    Hi Collin

    Thanks a lot for your help.

    I’ve just tried it. $rake spec runs without any problems if I put it outside a Rails project. Are there any clues here?

    Regards
    Hoa

  6. Bui The HoaJuly 01, 2008 @ 03:56 PM

    Hi Collin

    Could you provide me with any hints to go on with this?

    Many thanks Hoa

  7. Collin VanDyckJuly 02, 2008 @ 01:50 PM

    Hi Hoa,

    Well, to be honest, I’m not sure why yours is not working. You could probably debug where the error is occurring by using ruby-debug, but at the moment I’m too busy to really devote a lot of attention to it. Best of luck.

    -Collin

  8. mgarciaAugust 25, 2008 @ 09:12 AM

    I’ve got the same problem:

    Steps:

    $ ruby script/plugin install git://github.com/oculardisaster/plugin_migrator.git $ script/generate plugin my_plugin $ mkdir vendor/plugins/my_plugin/lib/my_plugin/ $ cat vendor/plugins/my_plugin/lib/my_plugin/migrator.rb module MyPlugin class Migrator < PluginMigrator::Migrator set_schema_table_override “my_plugin_schema_info” set_migration_directory(File.dirname(FILE) + ”/../../db/migrate”) end end

    $ cat vendor/plugins/my_plugin/tasks/my_plugin_tasks.rake

    namespace :myplugin do desc “Run the migrations for my plugin” task :migrate => :environment do MyPlugin::Migrator.migrate(ENV[‘VERSION’],false) end end

    $ rake myplugin:migrate (in test/myplugin) rake aborted! undefined method `schema_info_table_name_without_plugin’ for class `Class’

  9. Collin VanDyckAugust 25, 2008 @ 09:50 AM

    Hmm, OK. I’ll take a look at it soon and post back here. Thanks guys for the heads up.

  10. Collin VanDyckAugust 26, 2008 @ 11:15 PM

    So I started from scratch, and followed these steps, and was able to run rake spec. Can you try and follow these steps?

    Note that the test app that the specs use has a gem dependency of 2.0.2. This is something I need to fix, but this example needs to have that gem installed.

    rails testapp
    cd testapp/
    script/plugin install git://github.com/oculardisaster/plugin_migrator.git
    script/plugin install git://github.com/dchelimsky/rspec.git
    script/plugin install git://github.com/dchelimsky/rspec-rails.git
    script/generate rspec
    cd vendor/plugins/plugin_migrator/test/.rails_root
    rake spec
    
    (in /Users/collin/Desktop/testapp/vendor/plugins/plugin_migrator/test/.rails_root)
    == 1 CreateProducts: migrating ================================================
    -- create_table(:products)
       -> 0.0010s
    == 1 CreateProducts: migrated (0.0012s) =======================================
    
    .== 1 CreateProducts: migrating ================================================
    -- create_table(:products)
       -> 0.0010s
    == 1 CreateProducts: migrated (0.0013s) =======================================
    
    == 1 CreateProducts: reverting ================================================
    -- drop_table(:products)
       -> 0.0003s
    == 1 CreateProducts: reverted (0.0005s) =======================================
    
    .== 1 CreateProducts: migrating ================================================
    -- create_table(:products)
       -> 0.0009s
    == 1 CreateProducts: migrated (0.0010s) =======================================
    
    .....
    
    Finished in 0.201363 seconds
    
    7 examples, 0 failures
    
    
Comment