Ember CLI Rails
Unify your EmberCLI and Rails Workflows!
EmberCLI-Rails is designed to give you the best of both worlds:
- Stay up to date with the latest JavaScript technology and EmberCLI addons
- Develop your Rails API and Ember front-ends from within a single process
- Inject Rails-generated content into your EmberCLI application
- Avoid Cross-Origin Resource Sharing gotchas by serving your EmberCLI applications and your API from a single domain
- Write truly end-to-end integration tests, exercising your application's entire stack through JavaScript-enabled Capybara tests
- Deploy your entire suite of applications with a single
git push
EmberCLI-Rails Supports EmberCLI 1.13.x and later.
Installation
Add the following to your Gemfile
:
gem "ember-cli-rails"
Then run bundle install
:
$ bundle install
Usage
First, generate the gem's initializer:
$ rails generate ember-cli:init
This will create the following initializer:
# config/initializers/ember.rb
EmberCli.configure do |c|
c.app :frontend
end
The initializer assumes that your Ember application exists in
Rails.root.join("frontend")
.
If this is not the case, you could
- move your existing Ember application into
Rails.root.join("frontend")
- configure
frontend
to look for the Ember application in its current directory:
c.app :frontend, path: "~/projects/my-ember-app"
- generate a new Ember project:
$ ember new frontend
Initializer options
name
- this represents the name of the Ember CLI application.path
- the path where your Ember CLI application is located. The default value is the name of your app in the Rails root.
EmberCli.configure do |c|
c.app :adminpanel # path defaults to `Rails.root.join("adminpanel")`
c.app :frontend,
path: "/path/to/your/ember-cli-app/on/disk"
end
Next, install the ember-cli-rails-addon:
$ cd path/to/frontend
$ npm install --save-dev ember-cli-rails-addon
Be sure that the addon's MAJOR
and MINOR
version matches the gem's
MAJOR
and MINOR
versions.
For instance, if you're using the 0.5.x
version of the gem, specify
~> 0.5.0
ion in your Ember app's package.json
:
{
"devDependencies": {
"ember-cli-rails-addon": "~> 0.5.0"
}
}
Next, configure Rails to route requests to the frontend
Ember application:
# config/routes.rb
Rails.application.routes.draw do
mount_ember_app :frontend, to: "/"
end
Ember requests will be set params[:ember_app]
to the name of the application.
In the above example, params[:ember_app] == :frontend
.
Routing options
to
- The path to handle as an Ember application. This will only apply toformat: :html
requests. Additionally, this will handle child routes as well. For instance, mountingmount_ember_app :frontend, to: "/frontend"
will handle aformat: :html
request to/frontend/posts
.controller
- Defaults to"ember_cli/ember"
action
- Defaults to"index"
You should now be able to boot your Rails application, navigate to the root
route, and see your EmberCLI app!
Configuring the Ember controller
By default, routes defined by ember_app
will be rendered with the internal
EmberCli::EmberController
. The EmberCli::EmberController
renders the Ember
application's index.html
and injects the Rails-generated CSRF tags into the
<head>
.
To override this behavior, specify the controller
and action
options:
# config/routes
Rails.application.routes.draw do
mount_ember_app :frontend, to: "/", controller: "application", action: "index"
end
To serve the EmberCLI generated index.html
, use the render_ember_app
helper in your view:
<!-- app/views/application/index.html.erb -->
<%= render_ember_app :frontend %>
To inject markup into page, pass in a block that accepts the head
, and
(optionally) the body
:
<!-- app/views/application/index.html.erb -->
<%= render_ember_app :frontend do |head| %>
<% head.append do %>
<%= csrf_meta_tags %>
<% end %>
<% end %>
When serving the EmberCLI generated index.html
, don't use Rails' layout HTML:
# app/controllers/application.rb
class ApplicationController < ActionController::Base
def index
render layout: false
end
end
Rendering the EmberCLI generated JS and CSS
In addition to rendering the EmberCLI generated index.html
, you can inject the
<script>
and <link>
tags into your Rails generated views:
<!-- app/views/application/index.html.erb -->
<%= include_ember_script_tags :frontend %>
<%= include_ember_stylesheet_tags :frontend %>
Multiple Ember CLI apps
In the initializer you may specify multiple Ember CLI apps, each of which can be referenced with the view helper independently. You'd accomplish this like so:
EmberCLI.configure do |c|
c.app :frontend
c.app :admin_panel, path: "/somewhere/else"
end
Rendering Ember applications at routes other than /
requires additional setup
to avoid an Ember UnrecognizedURLError
.
For instance, if you had Ember applications named :frontend
and
:admin_panel
and you wanted to serve them at /frontend
and /admin_panel
,
you would set up the following Rails routes:
# /config/routes.rb
Rails.application.routes.draw do
mount_ember_app :frontend, to: "/frontend"
mount_ember_app :admin_panel, to: "/admin_panel"
end
You must modify each Ember app's baseURL
to point to the correct route:
// app/frontend/config/environment.js
module.exports = function(environment) {
var ENV = {
modulePrefix: 'frontend',
environment: environment,
baseURL: '/frontend', // originally '/'
...
}
}
// app/admin_panel/config/environment.js
module.exports = function(environment) {
var ENV = {
modulePrefix: 'admin_panel',
environment: environment,
baseURL: '/admin_panel', // originally '/'
...
}
}
Finally, configure each app's router.js
file so that rootURL
refers to the
new baseURL
:
/* app/frontend/app/router.js */
var Router = Ember.Router.extend({
rootURL: config.baseURL, // add this line
location: config.locationType
});
Repeat the process for admin_panel/app/router.js
.
CSRF Tokens
Your Rails controllers, by default, are expecting a valid authenticity token to be submitted with non-GET
requests.
Without it you'll receive a 422 Unprocessable Entity
error, specifically: ActionController::InvalidAuthenticityToken
.
In order to add that token to your requests, you need to add into your template:
<!-- app/views/application/index.html.erb -->
<%= render_ember_app :frontend do |head| %>
<% head.append do %>
<%= csrf_meta_tags %>
<% end %>
<% end %>
The default EmberCli::EmberController
and its accompanying view handle this
for you by default.
However, if you specify your own controller, make sure to append CSRF tags to
your view's <head>
.
The ember-cli-rails-addon addon will inject an initializer into your
app to set outgoing requests' X-CSRF-TOKEN
header to the value injected by
Rails.
Integrating with Rake
EmberCLI Rails exposes the ember:test
Rake task to execute Ember's test suite.
If you're using Rake to run your test suite, make sure to configure your test
task to depend on ember:test
.
For example, to configure a bare rake
command to run both RSpec and Ember test
suites, configure the default
task to depend on both spec
and ember:test
.
task default: [:spec, "ember:test"]
Serving from multi-process servers in development
If you're using a multi-process server (Puma, Unicorn, etc.) in development, make sure it's configured to run a single worker process.
Without restricting the server to a single process, it is possible for multiple EmberCLI runners to clobber each others' work.
Enabling LiveReload
In order to get LiveReload up and running with Ember CLI Rails, you can install
guard and
guard-livereload gems, run guard
init
and then add the following to your Guardfile
.
guard "livereload" do
# ...
watch %r{your-appname/app/\w+/.+\.(js|hbs|html|css|<other-extensions>)}
# ...
end
This tells Guard to watch your Ember CLI app for any changes to the JavaScript,
Handlebars, HTML, or CSS files within app
path. Take note that other
extensions can be added to the line (such as coffee
for CoffeeScript) to
watch them for changes as well.
NOTE: Ember CLI creates symlinks in your-appname/tmp
directory, which cannot
be handled properly by Guard. This might lead to performance issues on some
platforms (most notably on OSX), as well as warnings being printed by latest
versions of Guard. As a work-around, one might use
directories
option, explicitly specifying directories to watch, e.g. adding the following
to the Guardfile
.
# also add directories that need to be watched by other guard plugins
directories %w[app config lib spec your-appname/app]
Heroku
To configure your Ember CLI Rails app to be ready to deploy on Heroku:
- Run
rails g ember-cli:heroku
generator - Add the NodeJS buildpack and configure NPM to include the
bower
dependency's executable file.
$ heroku buildpacks:clear
$ heroku buildpacks:add --index 1 https://github.com/heroku/heroku-buildpack-nodejs
$ heroku buildpacks:add --index 2 https://github.com/heroku/heroku-buildpack-ruby
$ heroku config:set NPM_CONFIG_PRODUCTION=false
$ heroku config:unset SKIP_EMBER
You should be ready to deploy.
The generator will disable Rails' JavaScript compression by declaring:
config.assets.js_compressor = nil
This is recommended, but might not work for projects that have both Asset Pipeline and EmberCLI generated JavaScript.
To reverse this change, reconfigure Sprockets to use the uglifier
gem:
config.assets.js_compressor = :uglifier
NOTE Run the generator each time you introduce additional EmberCLI applications into the project.
Capistrano
To deploy an EmberCLI-Rails application with Capistrano, make sure your
EmberCLI app's package.json
file includes the bower
package as a development
dependency:
{
"devDependencies": {
"bower": "*"
}
}
Experiencing Slow Build/Deploy Times?
Remove ember-cli-uglify
from your package.json
file, and run
npm remove ember-cli-uglify
. This will improve your build/deploy
time by about 10 minutes.
The reason build/deploy times were slow is because ember uglified the JS and then added the files to the asset pipeline. Rails would then try and uglify the JS again, and this would be considerably slower than normal.
See also the note on Javascript minification
JavaScript minification
When pre-compiling assets in production, you will want to ensure that you are not minifying your JavaScript twice: once with EmberCLI's build tools and once with the Asset Pipeline. Repeated minification is wasteful and can increase deployment times exponentially.
You can either disable minification in your EmberCLI application's
ember-cli-build.js
:
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
var app = new EmberApp({
minifyJS: false,
});
// ...
};
or in your Rails application's config/environments/production.rb
:
Rails.application.configure do
config.assets.js_compressor = nil
end
Additional Information
When running in the development environment, Ember CLI Rails runs ember build
with the --output-path
and --watch
flags on. The --watch
flag tells
Ember CLI to watch for file system events and rebuild when an Ember CLI file is
changed. The --output-path
flag specifies where the distribution files will
be put. Ember CLI Rails does some fancy stuff to get it into your asset path
without polluting your git history. Note that for this to work, you must have
config.consider_all_requests_local = true
set in
config/environments/development.rb
, otherwise the middleware responsible for
building Ember CLI will not be enabled.
Alternatively, if you want to override the default behavior in any given Rails
environment, you can manually set the config.use_ember_middleware
and
config.use_ember_live_recompilation
flags in the environment-specific config
file.
ASSET_HOST
Used by the addon during fingerprinting.
When compiling an Ember app named "frontend"
from within Rails,
the addon will prepend the generated asset paths with:
${ASSET_HOST}/assets/frontend/
CDN_HOST
Behaves the same way as ASSET_HOST
, acting as a fallback.
RAILS_ENV
While being managed by EmberCLI Rails, EmberCLI process will have
access to the RAILS_ENV
environment variable. This can be helpful to detect
the Rails environment from within the EmberCLI process.
This can be useful to determine whether or not EmberCLI is running in its own standalone process or being managed by Rails.
For example, to enable ember-cli-mirage API responses in
development
while being run outside of Rails (while run by ember serve
),
check for the absence of the RAILS_ENV
environment variable:
// config/environment.js
if (environment === 'development') {
ENV['ember-cli-mirage'] = {
enabled: typeof process.env.RAILS_ENV === 'undefined',
}
}
RAILS_ENV
will be absent in production builds.
SKIP_EMBER
To disable asset compilation entirely, set an environment variable
SKIP_EMBER=1
.
This can be useful when an application's frontend is developed locally with EmberCLI-Rails, but deployed separately (for example, with ember-cli-deploy).
Ember Dependencies
Ember has several dependencies. Some of these dependencies might already be
present in your asset list. For example jQuery is bundled in jquery-rails
gem.
If you have the jQuery assets included on your page you may want to exclude them
from the Ember distribution. You can do so by setting the exclude_ember_deps
option like so:
EmberCli.configure do |c|
c.app :frontend, exclude_ember_deps: "jquery"
c.app :admin_panel, exclude_ember_deps: ["jquery", "handlebars"]
end
jQuery and Handlebars are the main use cases for this flag.
Ruby and Rails support
This project supports:
- Ruby versions
>= 2.1.0
- Rails
3.2.x
and>=4.1.x
.
To learn more about supported versions and upgrades, read the upgrading guide.
Contributing
See the CONTRIBUTING document. Thank you, contributors!
License
Open source templates are Copyright (c) 2015 thoughtbot, inc. It contains free software that may be redistributed under the terms specified in the LICENSE file.
About
ember-cli-rails was originally created by Pavel Pravosud and Jonathan Jackson.
ember-cli-rails is maintained by Sean Doyle and Jonathan Jackson.
ember-cli-rails is maintained and funded by thoughtbot, inc. The names and logos for thoughtbot are trademarks of thoughtbot, inc.
We love open source software! See our other projects or hire us to help build your product.