Don Werve
Don Werve

Reputation: 5120

Rails plugin to run migrations on startup?

Is there a plugin available to have Rails run through a db:migrate on startup? I'm looking for a solution that doesn't involve calling out to the Rake task via the shell; so, no "system('rake db:migrate')".

I can readily write my own plugin to do this, but figured it would be better to use/improve an existing migrate-on-init plugin if one exists.

Upvotes: 4

Views: 4340

Answers (5)

AlexChaffee
AlexChaffee

Reputation: 8252

Using config.after_initialize works, but there are two problems:

  1. it runs after all other initializers, so if those initializers do some databasey stuff they'd be using the old schema

  2. it runs in all environments, including Rake tasks and Resque workers. We don't want to automatically migrate every time we run rake routes, do we? And we don't want multiple migrations to happen simultaneously.

My solution is to use a config/initializers file, so I can decide what order it runs in, and to check for whether we're inside a rake task.

Also, until I'm comfortable with auto-migrate on deploy, I'm only doing this in the development and test environments.

Finally, I want to print some extra information (migrating to and from version), so instead of a one-liner, I reach into the Migrator, perhaps more intimately than I should.

Oh yeah! Also it should write schema.rb. So I'm blatantly stealing code from db:schema:dump from inside active_record/railties/databases.rake. (Wouldn't it be nice if rake tasks were methods on an object, so we could just call them?)

config/initializers/automatically_migrate.rb:

# don't do this in production
if (Rails.env.development? or Rails.env.test?) and
    # don't do this in a worker or task
    !defined?(Rake)  # SEE BELOW FOR POSSIBLE FIX

  migrations_paths = ActiveRecord::Migrator.migrations_paths
  migrator = ActiveRecord::Migrator.new(:up, migrations_paths, nil)
  pending_migrations = migrator.pending_migrations
  unless pending_migrations.empty?
    puts "Migrating from #{migrator.current_version} to #{pending_migrations.last.version}"
    migrator.migrate

    require 'active_record/schema_dumper'
    filename = ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb"
    File.open(filename, "w:utf-8") do |file|
      ActiveRecord::Base.establish_connection(Rails.env)
      ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
    end

  end

end

Update: Apparently the new version of Rake is more liberal about when it loads, so the expression !defined?(Rake) is always false, even when being run from the command line (i.e. not inside a Rake Task or a Resque Worker). I'm trying the following instead

caller.grep(/rake/).empty?

Upvotes: 4

Blue Smith
Blue Smith

Reputation: 8820

Here is my working version for Rails 3.2 on jRuby:

config.after_initialize do
  ActiveRecord::Migrator.migrate(Rails.root.join("db/migrate"), nil)
end

Upvotes: 3

Luis R.
Luis R.

Reputation: 846

Sam's Answer did not quite work for me. I'm using Rails 3.0, I think you might need to add this inside of application.rb, anywhere inside the class.

config.after_initialize do
  ActiveRecord::Migrator.migrate (RAILS_ROOT + "/db/migrate" )
end

On a similar note, I'm using JRuby and Deploying as a War, I wonder why this is not creating the tables for me, instead it complains that table such&such does not exist.

Upvotes: 1

Sam
Sam

Reputation:

Put the following inside Rails::Initializer block in environment.rb ...

  config.after_initialize do
    ActiveRecord::Migrator.migrate (RAILS_ROOT + "/db/migrate" )
  end

Upvotes: 2

ulver
ulver

Reputation: 1541

In my experience, db:migrate-like routine should only be called when new migrations are present, because it takes quite a bit of time. You shouldn't really have so many migrations that you can't track when they are present.

That said, the task of running new migration in production environment is best handled by deployment tools like Capistrano. when deploying to production box, capistrano runs migrations if you tell it too.

Running db:migrate-like routine upon every-init is resource and time wastful.

Upvotes: -3

Related Questions