This is not a blog post about embedding Ember CLI in your Rails app. Instead, it’s a post about how to get the two to live in harmony next to each other by separately deploying Rails and Ember, but making them feel like one app.
Our first attempt
Last week we launched our first foray into Ember – an admin facing utility that helps us organize, curate, and police content on our site. Our admin capability is developed primarily in Rails, but we wanted one page to be the Ember app.
Our first instinct was to look at ways to integrate our ember app directly into the Rails admin so it can live “inside” the page. We tried ember-cli-rails, a project that promised a lot of magic.
With a few lines of configuration, we could get Rails to compile our ember app and ship it along with our asset pipeline. Great! Ship it! But…disaster struck.
Problems with ember-cli-rails
1. It forces an ember dependency on all our Rails developers. They now need to know about npm, bower, and more in order to get their Rails app to even boot. This is sadness.
2. It bloats our Rails codebase by introducing another big hunk of code into it (an entire ember app).
3. The worst part: it turned our relatively snappy 2 minute Jenkins deploy into an 8 minute deploy (!). The issue appeared to be in the asset pipeline. Something was causing a drastic slowdown in compilation, right around the time of dealing with ember’s vendor assets (things like ember-data). Whether this is a bug in ember-cli-rails or simply the asset pipeline being the slow beast that it is, still remains to be seen.
We could probably get over #1 and #2 after some initial pain, but a 4 fold increase in deploy times was an unacceptable tradeoff for having Ember part of our Rails app.
When we ran ember-cli’s preferred compilation method (ember build), the build time was just fine. In fact on the same Jenkins box that took 4 minutes to concatenate assets in the Rails asset pipeline, the ember build took less than 20 seconds!
So we decided we were going to separate the two apps. But we still wanted it to feel like one app. Let’s get to work.
1. The Ember app should share a session with the Rails app
Because we didn’t want to have to deal with fancy things like OAuth or token based authentication against our API, we could simply share our session cookies with the Rails app if we ran on the same domain. So we decided we would serve our app on the same domain – https://reverb.com/app_goes_here. If it was at the same domain, it would share cookies and the Rails app would see it as “logged in”.
So the first thing we need to do is get it into a public directory on our existing web servers. We’ll talk about this in the deploy section below.
2. The Ember app should be environment aware so it can point to different backends
When you build your ember app, you can pass in an environment with “ember build –environment production”. To make our app aware of different endpoints, we added this into its
3. What about CSRF?
Rails comes with some CSRF protection out of the box. The way it normally works is Rails will return your CSRF token as a tag in the body of the html you request. You would then submit forms back to Rails with that CSRF token. Ember does not pull html from Rails and all it’s requests are asynchronous. How to fix?
1. Make Rails return the CSRF token in a cookie for Ember to read
2. Make Ember pull that cookie and set it on every out going request
4. How to deploy?
public dir of our Rails app right? Ok, so all we need to do is:
1. compile the ember app (npm/bower/ember build)
2. upload it to an s3 bucket
3. tell all our servers to download it
This is not particularly polished, but you get the idea:
5. Bonus: make it feel like part of the app
I’ll describe this one instead of giving you code. We wanted the ember app to have the same “layout” as the rest of our admin interface. Ajax to the rescue: just make a controller to render a partial and have ember pull it using jQuery.load into a div of your choice. Style it similar to your Rails app, and the illusion is complete.
One thing to note is that the Ember app is currently fully self contained in terms of assets. So in order to mimic the look and feel of our admin (which was based on Bootstrap), we had to pull Bootstrap into the Ember project. In the future, we may want to pull assets from Rails to avoid duplicating CSS. We have some ideas on how to do this using a controller to serve up the asset paths via an API but we’ll blog about that once we have a working prototype.
Yan Pritzker – @skwp