Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Ben Smith

leave your migrations in your Rails engines

Ben Smith
Wednesday, May 8, 2013

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. 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!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Will Read

Experience Report: Engine Usage That Didn’t Work

Will Read
Sunday, September 16, 2012

On the project I’m currently working on we have a main portal that provides a user registration system and a generic billing mechanism. It also has several sub applications which need to know some information about the user and be able to publish billing events. With a fairly easy to articulate boundary, we thought it might make sense to be deliberate in how we organized our code – we came up with three main solutions:

  1. One big app, just use namespaces
  2. Create the portal and expose API endpoints over HTTP to get user data and set billing data.
  3. Create the portal, and have each sub application contained in a Rails Mountable Engine.

Our Boulder office had been making some noise about Rails Mountable Engines for some time, gave a presentation in the SF office, and I had experience working with engines both in the dark times of engines in Rails 2.x and the markedly improved days of Rails 3.x, and even better still in 3.1+. We had need to scale one sub-component of one of the applications independently, but not entire apps as the primary usage of the system would be low-volume. We set off down the engines path…

It worked pretty well. Two months in to the project we had a retrospective specifically about how engines were working. We agreed that we had felt some pain, but overall they drove out interesting decoupling and that the cost to pull them out could potentially outweigh the little pain we might encounter in the future.

Then our team size doubled. We had multiple epics that had high priority and deadlines associated with them. We also needed to ramp up four new people which led me to reassess our choice of engines once again, not to mention that we eventually will be handing over the code to developers who haven’t seen much Rails.

The conclusion I came to was that engines had to go. Here’s the list of why:

  • Asset pipeline already feels magical, with engines you have to think even harder about what you’re doing and what to include in your application.js or application.scss.
  • Testing. Writing an RSpec request spec didn’t make sense in the engines where certain styles were expected, or a specific layout – we found ourselves stubbing out a whole lot, or putting more and more into a gem that contained shared code between the portal and the engines.
  • Running Tests – We had to have multiple instances of RubyMine open to get the correct load paths for the engine and the portal, constantly trying to remember “Is this in the shared gem, or in the engine?” or “Oh wait, this is specific to the engine, but it is a request spec so we need to go back to the portal to re-run that” was not unsolvable, but felt like a tax on our choice every time I fumbled.
  • Migrations everywhere – You write the migration in the engine, then it has to be copied to the dummy app for testing, and also back up to the main app for running request specs, now you have three copies of the same migration, all with different timestamps. Not fun when you realize you also needed to change that string column to a text column.
  • Everything still had to be namespaced anyway for the engines so we had the folder explosion we were trying to avoid from the One Big App solution.
  • Confidence – It got to the point where I found myself asking “Is this broken because I wrote it wrong, or because there’s something I misunderstood about engines?”. I could never be sure of why something had gone wrong.
  • Documentation – Rails Mountable Engines documentation is a small subset of the knowledge available on StackOverflow for example. If we want to make the ramp up and handoff as smooth as possible, we want to do the most vanilla thing possible so that things are intuitive or at least Googleable.

None of this was impossible, or even difficult to solve. It’s just that it wasn’t intuitive, not what a well seasoned Rails developer was expecting out of the box. It would have been a waste of our client’s time to bring a whole new team up to speed on all that we had learned about engines, rather than having one big application.

This is especially true since the only semi-real-payoff was that it made us isolate our sub-application code from the portal code. I say “semi-real” because the boundary was artificial – the reality was that the sub applications needed to know about the user and his account, and anything we built out for billing was really a dependency of each of the apps. This was different from a great engine like rails_admin that really is a drop in and has no domain-specific dependencies. Here we had nothing but domain-specific dependencies and now, by removing engines, our code and our domain are back where they belong: together.

  • UPDATE *
    Engines really are great and there’s lots of situations where they can be really powerful. Boulder Pivot, Stephan Hagemann, had these additional tips that I wanted to share with you.

  • Regarding RubyMine and running specs: there is a simple way to make RubyMine run all specs in all engines. It will make all engines modules with their own “RubMine root”, which fixes spec runs. http://pivotallabs.com/users/shagemann/blog/articles/2008-intellij-modules-in-rubymine-

  • Regarding migration duplication: You can have an engine or app that requires others run their migrations. Check out the migrations run by the main app in this sample app: https://github.com/shageman/the_next_big_thing

  • Regarding testing: If an engine is relying on some layout or style to be around, it should depend on it and include it (potentially by way of another engine).

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Stephan Hagemann

Migrating from a single Rails app to a suite of Rails engines

Stephan Hagemann
Tuesday, March 13, 2012

TL;DR

We moved a Rails app into an unbuilt engine of a new blank slate container app to allow new parts of our app to live next to it as engines as well. It has been working great for us!

I have a sample app rails_container_and_engines of the result’s structure on github.

Skip to the pitfalls and discoveries section to read about some of the speed bumps we during our transition. Interested in the why and how? Read on!

Rationale

As part of the project we had built a web app, a mobile app that talks to the web app’s API, two ETL tools to 1) do the initial data import from the app we were replacing and 2) get data into another of our client’s apps. At this point we knew that we would create one more big web app and several auxiliary apps.

Running up to the decision to using unbuilt engines we had several intense discussions on how to build loosely-coupled, highly-cohesive systems with Rails applications. We saw basically three choices:

  • All-in-one app that could get more structure through namespacing of its interals.
  • Services (REST or whatever, but) running as separate apps and communicating via APIs.
  • Engines running within a mostly feature-less container app.

The two big web apps share several models within the data access layer and use the same data. Because of this we chose the third option and left all of the apps within the same rails code base.

Internal discussions highlighted the costs/benefits of having all of these apps live within their own Rails project versus an engines approach. We feel that by using engines we are getting many of the benefits of a component based architecture without breaking Rails patterns. In addition, we feel that the cost of maintaining individual applications that share a central database or one giant application with less defined components would have been very high.

In order to make day to day development easier, and to avoid the “where do migrations live…” conversation and top level Rails deployment patterns, there is one twist to the architecture: everything resides in one git repo and engines are referenced from a single container application (similar to old school enterprise archive files). Each application is exposed via a unique context or resource identifier (each engine/app could also be isolated per instance via Apache). Here is the directory structure we ended up with:

container_rails_app/
  ...
  app
  config
  engines/
    etl/
    shared_modules/
    web_app_1/
    web_app_2/
  ...

Mike described this pattern in his recent blog posts Unbuilt Rails Dependencies: How to design for loosely-coupled, highly-cohesive components within a Rails application and Rails Contained: A Container for Web Application Development and Deployment. On how to make RubyMine work seamlessly with engines, read my post on IntelliJ Modules in Rubymine.

The steps we took

  • Generate a new, empty Rails app
  • Within the engines folder, create a new, mountable Rails engine
  • Copy all the tests from the original Rails app into the test directory and make them green.

    Ok. This step is a bit more involved. Essentially that’s it thought. Find some of the pitfalls we ran into described below. Here are a couple of highlights of what needs to happen:

    • Copy the files you need for a test to pass and namespace its class
    • Start with the model tests and work your way up towards integration and acceptance tests
    • Namespace everything with the name of the engine (either by fully qualifying every name – don’t do that) or with the lexical scope trick explained below. This includes tests and all classes.
    • Load needed gems explicitly: engines don’t load as much automatically as a Rails app
    • Namespace tables and assets
  • Copy the old .git directory into the new root folder in order to not loose any history. For us, git was not able to detect the changes as moves in many cases. This is certainly an area where you can improve on our solution!

For us, the real work started after this, when we started pullling out common code into a common models engine and began work on the second app.

We got all the tests to pass before we had namespaced any of the assets or rake tasks. That was an additional search-and-replace heavy step after the actual transition to an engine. It is not necessary right away if you do not have multiple applications at first, but to achieve the full effect, you will need to namespace the things as well.

Pitfalls and discoveries

Namespaces

Modules in your enige are not automatically loaded: make sure you reference them yourself before they are needed in other files.

Asset pre-compilation

Rails creates a default app/assets/stylesheets/application.css file which contains these lines:

/* ...
*= require_self
*= require_tree .
*/

If you have this file in your main app, all css files will be compiled into one file. For us, this made almost everything look right. Almost. Little things broke here and there. Our app contained a couple of sections for which the stylesheets were meant to be loaded separately. Add files that you want to have precompiled as individual files to config.assets.precompile list to have them precompiled into separate files and solve this problem.

Are all your references and associations breaking?

Try this

class M::Y
  def use_y
    M::Y.do_it!
  end
end
class M::Y
  def self.do_it!
  end
end

instead of

module M
  class X
    def use_y
      Y.do_it!
    end
  end
  class Y
    def self.do_it!
    end
  end
end

The first way sets the lexical scope to the module M as well as to the class Y allows you to references other classes in M without their full name. The second way sets the scope only to class M::Y and you have to fully qualify every class name to find it.

Don’t fight conventions

We were using fixture builder and while we were namespacing classes in modules we were trying to override the default table names to not be namespaced… Fixture builder didn’t like that at all. There may or may not be other dependencies that make leaving the conventions hard. So, save yourself the trouble and do the migrations (and stay consistent!) and namespace your tables as well!

HABTM

They seem to be falling out of favor, which is probably a good thing. With engines they don’t seem to work (well). We ended up getting rid of our last two habtm relationships instead of trying to make them work. Creating the join as a class and adding the necessary has many :through relationships is straight forward enough.

We ended up with one production performance bug due to this: Rails

Engines depending on engines

We used kaminari for our pagination requirements. When our app first became an engine, kaminari stopped picking up our custom views. Instead it used its standard views. The load order got screwed up to which there are basically two solutions:

  • require => nil on both engines in the Gemfile and force the correct load order in your app, or…
  • avoid name clashes by namespacing your views (which you were going to do anyways, right?)
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Stephan Hagemann

IntelliJ Modules in Rubymine

Stephan Hagemann
Tuesday, February 7, 2012

IntelliJ has a feature called modules: “a functional unit which you can compile, run, test and debug independently.”

Modules in IntelliJ: Multiple top-level folders

A module in IntelliJ is a top-level view on a part of a codebase. IntelliJ is for Java, which is why I do not typically use it. I use Rubymine – no similar functionality exists here… but a way around that!

It may have been true at some point that these kind of modules were not something that Rails offered, but they have been around for quite a while: Engines!

Engines are typically seen as a way to package a Rails app that can be reused and configured in the context of other Rails apps. However, many big Rails projects can benefit from engines and their ability to structure a large codebase into smaller, more independent parts. Namespacing within one Rails app can achieve a similar effect, but engines take it to the next level: all the code, including views, javascript, and even rake tasks and migrations can be separated consistently. Now, if the apps were totally separate, several independent Rails apps might be the right solution, but if those are tied together by the same database, one might do more harm then good when ripping that code apart…

The Problem

Imagine a Rails project with the following folder structure:

rails_app/
  app/
  ...
  engines/
    custom_engine1/
    custom_engine2/
  ...

The engines are somewhat hidden away, two levels deep in the folder structure. Also, running specs for engines in these sub-folders won’t work from Rubymine, because the paths it tries to use are wrong.

Whether you are actually working with engines or just want to see multiple root folders in Rubymine at once, here is how to do it.

Make modules work in Rubymine

1) You can get modules to work in Rubymine by opening your Rails project in IntelliJ: Cmd + ; opens the project structure dialog, select Modules from the list on the left and create a new module with the + button. Or, you can simply add a few files to your project…

2) The .idea folder in the root of your project holds the Rubymine configuration files. Edit modules.xml and add a module line for every module you would like to create:

<!-- ROOT/.idea/modules.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/custom_engine1/module1.iml" filepath="$PROJECT_DIR$/custom_engine1/custom_engine1.iml" />
      <module fileurl="file://$PROJECT_DIR$/custom_engine2/module2.iml" filepath="$PROJECT_DIR$/custom_engine2/custom_engine2.iml" />
    </modules>
  </component>
</project>

In the root folder of every module create a .iml file with the name of that module like so:

<!-- ROOT/engines/custom_engine1/custom_engine1.iml -->
<?xml version="1.0" encoding="UTF-8"?>
<module type="RUBY_MODULE" version="4">
  <component name="NewModuleRootManager">
    <content url="file://$MODULE_DIR$" />
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
  </component>
</module>

Voila: The next time you open the project folder in Rubymine, the custom_engine folders show up as top-level entries in the projects file list!

Modules in Rubymine!

Here is the gist: https://gist.github.com/1764127

This worked for me with Rubymine 3.2.4 and IntelliJ IDEA 11 CE.

Edit March 14: make sure that all module names are distinct and no module names are substrings of other module names. This confuses RubyMine and can prevent specs from running successfully.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
David Stevenson

Standup 11/22/2010: How can a rails engine provide migrations?

David Stevenson
Monday, November 22, 2010

Ask for help

  • We just upgraded one project from Devise 1.1 to Devise 1.2 and reported “many problems which blew up all sorts of stuff”. It was bad enough we had to rollback. Are there others with failure or success stories for this upgrade?
  • Can you rate limit EC2 nodes using an Elastic Load Balancer? We’d like to cap the amount of traffic that can be sent to an app instance. I’m thinking advanced use cases like this are probably why you run your own haproxy instead of using ELBs.
  • How are people running database migrations in their engine gems? I know rails 3.1 promises to bring this to the table, but is there a backport gem we can use?

Interesting

  • iOS 4.2 is out! We’re looking forward to trying it on the iPad (finally).
  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (778)
  • rails (113)
  • testing (86)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (53)
  • techtalk (44)
  • rspec (38)
  • activerecord (29)
  • productivity (29)
  • gogaruco (29)
  • ironblogger (29)
  • git (28)
  • nyc (27)
  • rubymine (25)
  • mobile (21)
  • cucumber (20)
  • bloggerdome (19)
  • process (19)
  • pivotal tracker (19)
  • design (18)
  • jasmine (18)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • palm (16)
  • "soft" ware (16)
  • fun (15)
  • tracker ecosystem (15)
  • ci (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • gem (13)
  • bdd (13)
  • selenium (12)
  • css (12)
  • goruco (12)
  • bundler (12)
  • tdd (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • rubygems (9)
Subscribe to engines Feed
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >