Ler blog em Português

Setting up Ember.js (ember-cli) with Rails

Read in 8 minutes


I received quite some feedback on my article about setting up Ember.js in Rails app, the most common being ember-cli.

ember-cli is the canonical way of running Ember apps, confirmed by the RFC that formalizes the upcoming v2.0 release.

While globals-based apps will continue to work in 2.0, we may introduce new features that rely on either Ember CLI or ES6 modules. You should begin moving your app to Ember CLI as soon as possible.
—Tom Dale, The Road to Ember 2.0

So ember-cli is a big deal. And so I decided to try it out, but not directly. I still think keeping my workflow under Rails is more productive, so I’ll use the recommended ember-cli-rails gem.

Warning: This article has a previous version that uses ember-rails instead. Read the original article.


Installing ember-cli

First, you’ll have to install Node.js or, even better, IO.js. So visit the project’s website and follow the instructions for your platform.

To install ember-cli, run npm install ember-cli -g.

$ npm install ember-cli -g
$ ember -v
version: 0.1.12
node: 1.1.0
npm: 2.1.8

Notice that I’m using IO.js, so that’s why my Node version is listed as 1.1.0. If you get similar output, then you’re good to go.

Configuring your Rails app

First, let’s remove things we won’t use from our Gemfile. This includes turbolinks and jquery-ujs. You also have to add the ember-cli-rails gem.

source 'https://rubygems.org'

gem 'rails', '4.2.0'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'ember-cli-rails'

You can install the dependencies with bundle install.

Setting up your Rails app

The ember-cli-rails will give you a generator called ember-cli:init, that will basically create an initializer file. Go ahead and generate it.

$ rails g ember-cli:init
    create  config/initializers/ember.rb

You can use this initializer to setting up your Ember apps. By default it’s configured to use an application called frontend that must live under app/frontend.

EmberCLI.configure do |config|
  config.app :frontend

You can define a different path, or even map multiple apps.

EmberCLI.configure do |config|
  config.app :frontend, path: 'app/ember/frontend'
  config.app :admin #=> path will be app/admin

For your Ember application, I suggest changing the app to something like :rails_root/frontend. Adding to the app directory will impact your performance in development mode, because Rails will track all files for auto-loading, even though this won’t be used. So for this example, I’m assuming your Ember app will live under :rails_root/frontend.

EmberCLI.configure do |config|
  config.app :frontend, path: Rails.root.join('frontend').to_s

To bootstrap your Ember app, let’s create a Rails controller/action. You can go with EmberController#bootstrap. You don’t have add anything to the app/views/ember/bootstrap.html.erb.

class EmberController < ApplicationController
  def bootstrap

Notice that you don’t have to specify the EmberController#bootstrap method, but I like the visibility. Again, remember to create a blank file at app/views/ember/bootstrap.html.erb.

Since you may use app/views/layouts/application.html.erb for other things than just Ember, I recommend creating a new layout file at app/views/layouts/ember.html.erb.

<!doctype html>
<html dir="ltr" lang="<%= I18n.locale %>">
    <meta charset="UTF-8">
    <%= stylesheet_link_tag 'frontend' %>
    <%= include_ember_script_tags :frontend %>

    <%= yield %>

For now, ember-cli-rails recommends using ember-cli just for JavaScript; that means that you should use asset pipeline for everything else, like images and CSS. So that’s why I’m using stylesheet_link_tag instead of include_ember_stylesheet_tags.

Ember has support for history.pushState; this means that every time you visit a route, the URL will change, instead of using the #! scheme. You’ll need a catch-all route for this to work, so update your config/routes.rb to something like this:

Rails.application.routes.draw do
  root 'ember#bootstrap'
  get '/*path' => 'ember#bootstrap'

Creating an Ember app

Everything we’ve done so far was configuring Rails. Now it’s time to generate the Ember application. You’ll have to use the ember command for this. From your Rails app root directory, run ember new frontend.

$ ember new frontend --skip-git
version: 0.1.12
  create .bowerrc
  create .editorconfig
  create .ember-cli
  create .jshintrc
  create .travis.yml
  create Brocfile.js
  create README.md
  create app/app.js
  create app/components/.gitkeep
  create app/controllers/.gitkeep
  create app/helpers/.gitkeep
  create app/index.html
  create app/models/.gitkeep
  create app/router.js
  create app/routes/.gitkeep
  create app/styles/.gitkeep
  create app/templates/application.hbs
  create app/templates/components/.gitkeep
  create app/views/.gitkeep
  create bower.json
  create config/environment.js
  create .gitignore
  create package.json
  create public/crossdomain.xml
  create public/robots.txt
  create testem.json
  create tests/.jshintrc
  create tests/helpers/resolver.js
  create tests/helpers/start-app.js
  create tests/index.html
  create tests/test-helper.js
  create tests/unit/.gitkeep
  create vendor/.gitkeep
  Installed browser packages via Bower.
  Installed packages for tooling via npm.

You also need to install the ember-cli-rails-addon NPM package. Do it from your Ember app directory.

$ cd frontend

$ npm install ember-cli-rails-addon --save-dev
ember-cli-rails-addon@0.0.11 node_modules/ember-cli-rails-addon

$ cd ..

Let’s check if everything works together. Create a file at frontend/app/templates/index.hbs with the following content.

  Fuck yeah! It works!

We need to create a CSS file for our Ember app. So, create app/assets/stylesheets/frontend.scss with the following content.

// @import './frontend/**/*';

This means that all CSS for frontend Ember app will live under app/assets/stylesheets/frontend directory. Remember to update your config/initializers/assets.rb file to include this file to the precompiling list.

Rails.application.config.assets.precompile += %w[ frontend.css ]

Fire up your server with rails server and head to http://localhost:3000. You must see a page like this:

Ember.js in a Ruby on Rails app

Understanding ember-cli-rails

So, what exactly does ember-cli-rails do? In a few words, it runs ember commands through a Rack middleware. When you visited http://localhost:3000, ember-cli-rails issued the ember build command, as you can see in your server log.

$ rails s
=> Booting WEBrick
=> Rails 4.2.0 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2015-02-07 22:37:06] INFO  WEBrick 1.3.1
[2015-02-07 22:37:06] INFO  ruby 2.2.0 (2014-12-25) [x86_64-darwin14]
[2015-02-07 22:37:06] INFO  WEBrick::HTTPServer#start: pid=87021 port=3000

Started GET "/" for ::1 at 2015-02-07 22:37:07 -0200
version: 0.1.12
Build successful - 792ms.

Slowest Trees                  | Total
Concat: Vendor                 | 139ms
SixToFive                      | 71ms
JSHint app- QUnit              | 59ms
ES6Modules                     | 55ms
ES3SafeFilter                  | 53ms

Processing by EmberController#bootstrap as HTML
  Rendered ember/bootstrap.html.erb within layouts/ember (1.5ms)
Completed 200 OK in 118ms (Views: 117.2ms)

Started GET "/assets/frontend/vendor-64e62ddc273c2f5847f30d698ca14b67.css?body=1" for ::1 at 2015-02-07 22:37:12 -0200

Started GET "/assets/frontend/frontend-64e62ddc273c2f5847f30d698ca14b67.css?body=1" for ::1 at 2015-02-07 22:37:12 -0200

Started GET "/assets/frontend/frontend-e07fffa094f820251c6586556d21c934.js?body=1" for ::1 at 2015-02-07 22:37:12 -0200

Started GET "/assets/frontend/vendor-a050afbf761cda6b6c9046c755b3dceb.js?body=1" for ::1 at 2015-02-07 22:37:12 -0200

Notice that the ember process will be running in background until you stop your Rails server. This is useful, because ember-cli only compiles your Ember app when a file changes, making the whole process so much faster.

Although this integration is really helpful, it doesn’t mean you won’t ever run ember-cli directly; ember-cli has generators that will make you life easier, so you better off knowing the command-line’s API.

$ ember g -h
version: 0.1.12
Requested ember-cli commands:

ember generate <blueprint> <options...>
  Generates new code from blueprints.
  aliases: g
  --dry-run (Boolean) (Default: false)
    aliases: -d
  --verbose (Boolean) (Default: false)
    aliases: -v
  --pod (Boolean) (Default: false)
    aliases: -p

  Available blueprints:
      ember-data <name>
      ember-cli-qunit <name>
      acceptance-test <name>
        Generates an acceptance test for a feature.
      adapter <name> <options...>
        Generates an ember-data adapter.
      adapter-test <name>
        Generates an ember-data adapter unit test
      addon <name>
        The default blueprint for ember-cli addons.
      app <name>
        The default blueprint for ember-cli projects.
      blueprint <name>
        Generates a blueprint and definition.
      component <name>
        Generates a component. Name must contain a hyphen.
      component-test <name>
        Generates a component unit test.
      controller <name>
        Generates a controller.
      controller-test <name>
        Generates a controller unit test.
      helper <name>
        Generates a helper function.
      helper-test <name>
        Generates a helper unit test.
      http-mock <endpoint-path>
        Generates a mock api endpoint in /api prefix.
      http-proxy <local-path> <remote-url>
        Generates a relative proxy to another server.
      in-repo-addon <name>
        The blueprint for addon in repo ember-cli addons.
      initializer <name>
        Generates an initializer.
      initializer-test <name>
        Generates an initializer unit test.
      lib <name>
        Generates a lib directory for in-repo addons.
      mixin <name>
        Generates a mixin.
      mixin-test <name>
        Generates a mixin unit test.
      model <name> <attr:type>
        Generates an ember-data model.
      model-test <name>
        Generates a model unit test.
      resource <name>
        Generates a model and route.
      route <name> <options...>
        Generates a route and registers it with the router.
        --type=route|resource (Default: route)
          aliases: -route (--type=route), -resource (--type=resource)
        --path (Default: )
      route-test <name>
        Generates a route unit test.
      serializer <name>
        Generates an ember-data serializer.
      serializer-test <name>
        Generates a serializer unit test.
      server <name>
        Generates a server directory for mocks and proxies.
      service <name>
        Generates a service and initializer for injections.
      service-test <name>
        Generates a service unit test.
      template <name>
        Generates a template.
      test-helper <name>
        Generates a test helper.
      transform <name>
        Generates an ember-data value transform.
      transform-test <name>
        Generates a transform unit test.
      util <name>
        Generates a simple utility module/function.
      util-test <name>
        Generates a util unit test.
      view <name>
        Generates a view subclass.
      view-test <name>
        Generates a view unit test.

Another thing that’s worth mentioning is that some tasks can only be performed by npm or bower commands; there’s no alternative as a rake task.

So, make yourself comfortable with ember-cli, NPM and Bower. You’ll probably have to do this eventually.

Running tests

Ember has integration with QUnit. So here’s a tip: use it! Otherwise you won’t be able to use all built-in helpers. You also have way more documentation about Ember + QUnit than with any other framework.

All your Ember’s tests must be loaded from <your ember app>/tests. When you generated the Ember app, some tests were generated too. You can run them by issuing rake ember-cli:test (Rails app root) or ember test (Ember app root).

$ cd frontend

$ ember test
version: 0.1.12
Built project successfully. Stored in "myapp/frontend/tmp/class-tests_dist-YU0yGWyj.tmp".
ok 1 PhantomJS 1.9 - JSHint - .: app.js should pass jshint
ok 2 PhantomJS 1.9 - JSHint - helpers: helpers/resolver.js should pass jshint
ok 3 PhantomJS 1.9 - JSHint - helpers: helpers/start-app.js should pass jshint
ok 4 PhantomJS 1.9 - JSHint - .: router.js should pass jshint
ok 5 PhantomJS 1.9 - JSHint - .: test-helper.js should pass jshint

# tests 5
# pass  5
# fail  0

# ok

To run tests in your browser, execute ember test --serve and visit http://localhost:7357.

Running tests in the browser

One problem you’ll notice is that your app’s CSS won’t be loaded; remember that we’re using Rails Asset Pipeline to handle images and CSS. A quick fix while we don’t have an official solution is pointing the CSS to your development’s web server. Just change the file frontend/tests/index.html and <link rel="stylesheet" href="assets/frontend.css"> to <link rel="stylesheet" href="http://localhost:3000/assets/frontend.css">.

Running tests in the browser with Testem

Installing Ember Inspector

If you want to develop Ember apps, make yourself a favor and install Ember Inspector, a Chrome extension that can inspect your app. You check objects, see rendering times, and more.

Ember Inspector

Wrapping up

Having a totally different stack for your front-end has pros and cons. It’s great to have this separation when you have a front-end engineer that will hack Ember most of the time, but can be a pain point when you’re used to Rails and its workflow. Not to mention that the setup got way more complex than the one described by the original text.

ember-cli-rails is a nice start, but is still immature. In a perfect world you’d just use Rails and the asset pipeline. No ember-cli, no NPM, no Bower, but I don’t think that will ever happen.

So if you’re betting on Rails and Ember.js, make sure you get comfortable with ember-cli and consider using it without ember-cli-rails. Assume that Rails will be just an API and that you’re front-end is Ember, a completely different client.

You can found the source code for this example at Github.