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
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!
About the Author