Go to English Blog

Configurando o Ember.js no Rails com ember-cli

Leia em 8 minutos

Ember.js

Eu recebi alguns comentários no meu artigo sobre como configurar o Ember.js no Rails, sendo que o mais comum foi sobre o ember-cli.

O ember-cli é o modo canônico de executar aplicativos Ember, confirmado pela RFC que formaliza a versão 2.0

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

Conhecer o ember-cli é muito importante e, por isso, decidi testá-lo, mas não diretamente. Eu ainda acredito que manter o meu workflow no Rails é muito mais produtivo, por isso vou usar a gem ember-cli-rails, recomendada pelo próprio Ember.

Atenção: Este artigo possui uma versão anterior que usa ember-rails. Leia o artigo original.

Instalação

Instalando o ember-cli

Primeiro você precisa instalar o Node.js ou, melhor ainda, IO.js. Por isso visite o site do projeto e siga as instruções para sua plataforma.

Para instalar o ember-cli execute npm install ember-cli -g.

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

Perceba que estou usando IO.js, por isso minha versão do Node é identificada como 1.1.0. Se você tiver uma saída semelhante, então tudo funcionou corretamente.

Configurando seu aplicativo Rails

Nós temos que remover coisas que não iremos usar do arquivo Gemfile, como turbolinks e jquery-ujs. Você também tem que adicionar a gem ember-cli-rails.

source 'https://rubygems.org'

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

Instale as dependências com o comando bundle install.

A gem ember-cli-rails adicionará um generator chamado ember-cli:init, que irá criar um arquivo de inicialização.

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

Você pode usar este initializer para configurar os aplicativos Ember. Por padrão ele define um aplicativo chamado frontend, que será localizado em app/frontend.

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

Você pode definir um novo caminho ou até mesmo mapear múltiplos aplicativos.

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

Eu sugiro que você defina um caminho como :rails_root/frontend. Configurar o aplicativo Ember no diretório app irá impactar na performance no modo de desenvolvimento, porque o Rails irá adicionar todos os arquivos ao sistema de auto-loading, mesmo não sendo usado. Então, neste exemplo eu assumo que o aplicativo Ember está localizado em :rails_root/frontend.

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

Para iniciar o Ember, vamos criar um controller/action no Rails. Você pode usar algo como EmberController#bootstrap. Você não precisa adicionar nada no arquivo app/views/ember/bootstrap.html.erb.

class EmberController < ApplicationController
  def bootstrap
  end
end

Perceba que você não precisa especificar o método EmberController#bootstrap, mas eu gosto da visibilidade que isso fornece. Apenas lembre-se de criar o arquivo em branco em app/views/ember/bootstrap.html.erb.

Como você pode usar o arquivo app/views/layouts/application.html.erb para outras coisas que não apenas o Ember, eu sugiro que você use um novo arquivo em app/views/layouts/ember.html.erb.

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

  <body>
    <%= yield %>
  </body>
</html>

No momento o ember-cli-rails recomenda usar o ember-cli apenas para o JavaScript; isso significa que você deve usar o asset pipeline para todo o resto, como imagens e arquivos de CSS. Por isso estou usando o método stylesheet_link_tag em vez de include_ember_stylesheet_tags.

O Ember tem suporte para history.pushState; assim, toda vez que você visitar uma rota do Ember, a URL irá mudar, em vez de usar o formato #!. Você precisa de uma rota catch-all, so então atualize o arquivo config/routes.rb para algo como isso:

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

Criando o aplicativo Ember

Tudo o que fizemos até agora foi configurar o Rails. Agora vamos criar o aplicativo Ember. Para isso, use o comando ember. À partir do diretório raíz do Rails, execute o comando ember new frontend.

$ ember new frontend --skip-git
version: 0.1.12
installing
  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.

Você também vai precisar instalar o pacote NPM ember-cli-rails-addon. Instale-o à partir do diretório do aplicativo Ember.

$ cd frontend

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

$ cd ..

Vamos ver se tudo está funcionando. Crie um arquivo frontend/app/javascripts/templates/index.hbs com o seguinte conteúdo.

<p>
  Fuck yeah! It works!
</p>

Você também vai precisar do arquivo app/assets/stylesheets/frontend.scss, que pode ter o seguinte conteúdo:

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

Isso signifa que todo o CSS do aplicativo frontend deve ser colocado no diretório app/assets/stylesheets/frontend. Lembre-se de atualizar o arquivo config/initializers/assets.rb, incluindo o arquivo frontend.css à lista de precompilação.

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

Inicie o servidor com o comando rails server e abra o endereço http://localhost:3000. Você deve ver uma página como esta.

Ember.js em um app Ruby on Rails

Entendendo o ember-cli-rails

Mas o que exatamente faz a gem ember-cli-rails? Em poucas palavras, ela executa o comando ember através de um Rack middleware. Quando você visitou o endereço http://localhost:3000, a gem ember-cli-rails executou o comando ember build, como você pode ver no 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
Building...
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

Perceba que o processo ember continuará em background até que você pare o servidor Rails. Isso é muito útil, porque o ember-cli irá compilar apenas quando algum arquivo mudar, tornando o processo muito mais rápido.

Embora esta integração seja muito útil, não significa que você nunca precisará executar o comando ember-cli diretamente; existem muitos generators que tornarão sua vida mais simples, então é melhor conhecer a API do comando.

$ 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-addon:
      ember-data <name>
    ember-cli-qunit:
      ember-cli-qunit <name>
    ember-cli:
      acceptance-test <name>
        Generates an acceptance test for a feature.
      adapter <name> <options...>
        Generates an ember-data adapter.
        --base-class
      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.

Outra coisa que é importante mencionar é que algumas tarefas só podem ser realizadas pelos comandos npm ou bower; não existe uma alternativa como tarefa rake.

Então familiarize-se com o ember-cli, NPM e Bower, porque você precisará usá-los eventualmente.

Executando testes

O Ember possui integração com QUnit. Então aqui vai uma dica: use-lo! Caso contrário, você não poderá usar todos os helpers adicionados pelo próprio Ember. Você também encontrará muito mais documentação para Ember e QUnit do que com qualquer outro framework.

Todos os testes do Ember serão carregados de <seu app Ember>/tests. Quando você criou o aplicativo Ember, alguns testes foram gerados; execute-os com o comando rake ember-cli:test (à partir da raíz do projeto Rails) ou ember test (à partir da raíz do projeto Ember).

$ 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

1..5
# tests 5
# pass  5
# fail  0

# ok

Para executar os testes no navegador, execute ember test --serve e visite o endereço http://localhost:7357.

Executando testes no navegador

Um problema que você irá perceber é que o CSS da aplicação não será carregado; lembre-se que estamos usando o Rails (Asset Pipeline) para gerenciar imagens e CSS. Uma solução temporária é apontar o CSS para para o seu servidor de desenvolvimento. Apenas altere o arquivo frontend/tests/index.html, mudando de <link rel="stylesheet" href="assets/frontend.css"> para <link rel="stylesheet" href="http://localhost:3000/assets/frontend.css">.

Executando testes no navegador com Testem

Instalando o Ember Inspector

Se você pretender desenvolver com Ember, lembre-se de usar o Chrome com a extensão Ember Inspector. Com ela você consegue inspecionar os objetos criados, ver tempos de renderização, além de muito mais.

Ember Inspector

Finalizando

Ter um stack totalmente diferente para o seu front-end tem prós e contras. É muito bom ter esta separação quando você tem um desenvolvedor front-end que irá usar o Ember na maior parte do tempo, mas pode ser um problema se você está acostumado com o Rails e seu workflow. Sem mencionar que a configuração se torna muito mais complexa que a descrita no artigo original.

ember-cli-rails é um bom começo, mas ainda é uma ferramenta muito imatura. Em um mundo perfeito você usaria apenas o Rails e asset pipeline. Nada de ember-cli, nada de NPM, nada de Bower, mas eu não acho que isso vá acontecer.

Então, se você está apostando no Rails e Ember.js, acostume-se com o ember-cli e considere usá-lo sem o ember-cli-rails. Assuma que o Rails será apenas uma API e que todo o seu front-end será em Ember, um cliente completamente diferente.