If you are using Rails engines to break up a single app into modular pieces, migrations (as they are currently implemented in Rails 3.2.13) become clumsy.
There are three options for migrations within an engine (spoiler: #3 is the best):
1) You can use the your_engine_name:install:migrations
rake task, which copies the migrations out of the engine and into the wrapping Rails app where they can be run normally. This works fine if your migrations in your engine never change, but if you’re actively developing your engine you need to run this rake task each time you add a migration.
2) You can put all your migrations in your wrapping Rails app. This works if you’re using your engines as a way to break up your app, but it doesn’t feel right. If your models, views, and controllers all live within the engine (and depend on migrations), shouldn’t your migrations live within the engine as well? If your migrations live in the wrapper Rails app, you actually create a weird upward dependency where the engine is actually dependent on the wrapper app. This is bad.
3) You can monkey patch Rails so all of your engine’s migrations automatically get run in the wrapper Rails app. Everything just works, and migrations live where they should: in the engine. If you’re breaking up your large Rails app into engines, this is the way to go. Here’s how you do it….
Within your Rails engine, there should be a file called engine.rb
here’s an example of it for an engine I called EngineWithMigrations
:
module EngineWithMigrations
class Engine < ::Rails::Engine
isolate_namespace EngineWithMigrations
end
end
All you need to do is tell Rails to add your engine’s migration directory to its list of places it looks for migrations (note: see the update at the bottom of the post if you are using Rails 4). Like so:
module EngineWithMigrations
class Engine < ::Rails::Engine
isolate_namespace EngineWithMigrations
initializer :append_migrations do |app|
unless app.root.to_s.match root.to_s
app.config.paths["db/migrate"] += config.paths["db/migrate"].expanded
end
end
end
end
app.config
is the config of your wrapper Rails app, config
is the config of your engine. The above line adds the engine’s migration directory to the wrapper Rails app’s migration directory list. The unless
wrapping it is to keep your migrations from running twice in your testing dummy app (which already runs migrations fine). Now when you run rake db:migrate
from your wrapper app, your engine’s migrations just work!
Rails 4 Update:
In order to get your migrations to work with Rails 4, the initializer needs to change slightly:
module EngineWithMigrations
class Engine < ::Rails::Engine
isolate_namespace EngineWithMigrations
initializer :append_migrations do |app|
unless app.root.to_s.match root.to_s
config.paths["db/migrate"].expanded.each do |expanded_path|
app.config.paths["db/migrate"] << expanded_path
end
end
end
end
end
Thanks Systho for pointing this out!