111
Rails & Javascript Faça isso direito! Dicas de como organizar o javascript em apps Rails não SPA

TDC 2015 - Rails & Javascript: faça isso direito

Embed Size (px)

Citation preview

Page 1: TDC 2015 - Rails & Javascript: faça isso direito

Rails & JavascriptFaça isso direito!

Dicas de como organizar o javascript em apps Rails não SPA

Page 2: TDC 2015 - Rails & Javascript: faça isso direito

$ whoami• Cezinha Anjos.

• Comecei programando num Apple II e gravando programas em fitas cassetes há 26 anos atrás.

• Atualmente focado em Ruby on Rails e Javascript.

• Gosto de OO, Clean Code, Design Patterns, BDD e Lean.

• Diretor da ASSEINFO.

Page 3: TDC 2015 - Rails & Javascript: faça isso direito

• Estamos localizados em Tijucas - SC - 40 km de Florianópolis.

• Somos em torno de 20 pessoas.

• Quase nenhuma verticalização hierárquica. Quem tem chefe é índio!

• Desde 2001 no mercado de automação comercial.

• Trabalhamos com ERP.

• Foco em qualidade.

• Já nascemos agile, mesmo antes de conhecer o manifesto ágil.

3

Page 4: TDC 2015 - Rails & Javascript: faça isso direito

História desta palestra

Page 5: TDC 2015 - Rails & Javascript: faça isso direito

História desta palestra

Hands on de 9 horas

Page 6: TDC 2015 - Rails & Javascript: faça isso direito

História desta palestra

Hands on de 9 horas Talk de 50 minutos

Page 7: TDC 2015 - Rails & Javascript: faça isso direito

Qual o formato da palestra?

Page 8: TDC 2015 - Rails & Javascript: faça isso direito

Dicas

Page 9: TDC 2015 - Rails & Javascript: faça isso direito

Partiremos de um app tradicional Rails

Page 10: TDC 2015 - Rails & Javascript: faça isso direito

Evoluindo o código usando cada dica

Page 11: TDC 2015 - Rails & Javascript: faça isso direito

Use o Gemfile somente para dependências do

backend

01

(sempre que possível)

Page 12: TDC 2015 - Rails & Javascript: faça isso direito

# Arquivo: Gemfile

source 'https://rubygems.org'   gem 'rails', '4.2.0' gem 'sqlite3' gem 'sass-rails', '~> 5.0' gem 'uglifier', '>= 1.3.0' gem 'coffee-rails', '~> 4.1.0'gem 'jquery-rails'

Remover gems do front-end

Page 13: TDC 2015 - Rails & Javascript: faça isso direito

Use o Bower para as dependências do

front-end

02

Page 14: TDC 2015 - Rails & Javascript: faça isso direito

$ npm install -g bower

Instalação

Page 15: TDC 2015 - Rails & Javascript: faça isso direito

Configuração

$ bower init

# enter, enter, enter, enter… # até que o arquivo bower.json # seja gerado

Page 16: TDC 2015 - Rails & Javascript: faça isso direito

Configuração// Arquivo: bower.json

{ name: ‘rails-and-js’, version: '0.0.0', authors: [ 'Cezinha <[email protected]>' ], license: 'MIT', ignore: [ '**/.*', 'node_modules', 'bower_components', 'test', 'tests' ] }

Page 17: TDC 2015 - Rails & Javascript: faça isso direito

Configuração

$ vim .bowerrc

{ "directory": "vendor/assets/bower" }

Caminho onde os pacotes serão baixadose a asset pipeline poderá utilizar.

Page 18: TDC 2015 - Rails & Javascript: faça isso direito

Instalando dependências

$ bower install jquery#2.0.3 —-save

$ bower install jquery-ujs#1.0.3 --save

# Arquivo: Gemfile ... gem 'jquery-rails'

Page 19: TDC 2015 - Rails & Javascript: faça isso direito

Instalando dependências// arquivo: bower.json   { "name": "rails-and-js", "version": "0.0.0", "authors": [ "Cezinha <[email protected]>" ], "license": "MIT", "ignore": [ "**/.*", "node_modules", "bower_components", "test", "tests" ], "dependencies": { "jquery": "2.0.3", "jquery-ujs": "1.0.3" } }

Page 20: TDC 2015 - Rails & Javascript: faça isso direito

Ajustando o manifesto

// arquivo: application.js   //= require jquery

//= require_tree .//= require jquery_ujs//= require jquery-ujs

Page 21: TDC 2015 - Rails & Javascript: faça isso direito

Você pode instalarcoisas como:• JQuery;

• JQuery UI;

• Twitter Bootstrap;

• Font Awesome;

• Angular JS;

• Backbone;

• React JS;

Qualquer projeto

que esteja no github

(ou similar)

Page 22: TDC 2015 - Rails & Javascript: faça isso direito
Page 23: TDC 2015 - Rails & Javascript: faça isso direito

Crie uma pasta para cada view e pelo menos um

arquivo para cada action

03

Page 24: TDC 2015 - Rails & Javascript: faça isso direito

$ mkdir app/assets/javascripts/views $ mkdir app/assets/javascripts/views/people

Uma pasta por view

Pelo menos um arquivo por action

Page 25: TDC 2015 - Rails & Javascript: faça isso direito

A separação dos scripts ajudará nas futuras

manutenções

Page 26: TDC 2015 - Rails & Javascript: faça isso direito

Aceite que todos os seus javascripts acabarão em

um único arquivo

04

(nem sempre isso é verdade ;-)

Page 27: TDC 2015 - Rails & Javascript: faça isso direito

Visão do programador

4 arquivos separados

Page 28: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo: app/assets/javascripts/views/people/index.js   alert("Running index.js");

// arquivo: app/assets/javascripts/views/people/edit.js   alert("Running edit.js");

// arquivo: app/assets/javascripts/views/people/show.js   alert("Running show.js");

// arquivo: app/assets/javascripts/views/people/new.js   alert("Running new.js");

Page 29: TDC 2015 - Rails & Javascript: faça isso direito

http://localhost:3000/people/new

Page 30: TDC 2015 - Rails & Javascript: faça isso direito

http://localhost:3000/people/new

12

34

Page 31: TDC 2015 - Rails & Javascript: faça isso direito

http://localhost:3000/people/new

12

34WTF?!

Page 32: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo: application.js // na visão do sprockets // sem "uglificação"   alert("Running edit.js"); alert("Running index.js"); alert("Running new.js"); alert("Running show.js");

Visão do sprockets

Page 33: TDC 2015 - Rails & Javascript: faça isso direito

Você não tem como separar fisicamente os scripts sem aumentar o número de

requisições.

Minha opinião: não separe!

Page 34: TDC 2015 - Rails & Javascript: faça isso direito

O segredo é modularizar

05

Page 35: TDC 2015 - Rails & Javascript: faça isso direito

http://larsjung.de/modulejs/

Page 36: TDC 2015 - Rails & Javascript: faça isso direito

Instalação

$ bower install modulejs#1.5.0 --save

Page 37: TDC 2015 - Rails & Javascript: faça isso direito

Ajustando o manifesto

// arquivo: application.js   //= require jquery //= require jquery-ujs//= require_tree .//= require modulejs //= require_tree .

Page 38: TDC 2015 - Rails & Javascript: faça isso direito

alert("Running index.js");

// arquivo: app/assets/javascripts/views/people/index.js

modulejs.define("people.index", function() { return function() { alert("Running index.js”); }; });

// Resultado do console: function () { alert("Running index.js"); }

1 2 3

4

var mymodule = modulejs.require("people.index"); console.log(mymodule); mymodule();

Page 39: TDC 2015 - Rails & Javascript: faça isso direito

Estabeleça um Single Entry Point

06

Page 40: TDC 2015 - Rails & Javascript: faça isso direito

Defina um único ponto para início de

execução de todo o seu javascript.

Page 41: TDC 2015 - Rails & Javascript: faça isso direito

O fluxo de execução tende a ficar mais

claro.

Page 42: TDC 2015 - Rails & Javascript: faça isso direito

Carregamento da página

Executa JS de terceiros

Dispatcher (boot.js)

people/index.js

foo.js

people/new.js people/edit.js people/show.js

bar.js

x.js y.js

Fluxo de execução

Page 43: TDC 2015 - Rails & Javascript: faça isso direito

Carregamento da página

Executa JS de terceiros

Dispatcher (boot.js)

people/index.js

foo.js

people/new.js people/edit.js people/show.js

bar.js

x.js y.js

Fluxo de execução

Page 44: TDC 2015 - Rails & Javascript: faça isso direito

Carregamento da página

Executa JS de terceiros

Dispatcher (boot.js)

people/index.js

foo.js

people/new.js people/edit.js people/show.js

bar.js

x.js y.js

Fluxo de execução

Page 45: TDC 2015 - Rails & Javascript: faça isso direito

Carregamento da página

Executa JS de terceiros

Dispatcher (boot.js)

people/index.js

foo.js

people/new.js people/edit.js people/show.js

bar.js

x.js y.js

Fluxo de execução

Page 46: TDC 2015 - Rails & Javascript: faça isso direito

Carregamento da página

Executa JS de terceiros

Dispatcher (boot.js)

people/index.js

foo.js

people/new.js people/edit.js people/show.js

bar.js

x.js y.js

Fluxo de execução

Page 47: TDC 2015 - Rails & Javascript: faça isso direito

Carregamento da página

Executa JS de terceiros

Dispatcher (boot.js)

people/index.js

foo.js

people/new.js people/edit.js people/show.js

bar.js

x.js y.js

Fluxo de execução

Page 48: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo app/assets/javascripts/boot.js   (function() { "use strict";   $(document).ready(function() {

// Aqui deve ser o seu primeiro // ponto de execução de javascript

}); })()

Page 49: TDC 2015 - Rails & Javascript: faça isso direito

Ajustando o manifesto// arquivo: application.js   //= require jquery //= require jquery-ujs //= require modulejs//= require_tree .//= require boot//= require_tree ./views

Page 50: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo app/assets/javascripts/boot.js   (function() { "use strict";   $(document).ready(function() {

}); })()

// Aqui deve ser o seu primeiro // ponto de execução de javascript //

modulejs.define("people.index", function() { return function() { alert("Running index.js") }; });

// arquivo: app/assets/javascripts/views/people/index.js

var mymodule = modulejs.require("people.index"); console.log(mymodule); mymodule();

Page 51: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo app/assets/javascripts/boot.js   (function() { "use strict";   $(document).ready(function() {

}); })()

// Aqui deve ser o seu primeiro // ponto de execução de javascript //

modulejs.define("people.index", function() { return function() { alert("Running index.js") }; });

// arquivo: app/assets/javascripts/views/people/index.js

var mymodule = modulejs.require("people.index"); console.log(mymodule); mymodule();

Page 52: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo app/assets/javascripts/boot.js   (function() { "use strict";   $(document).ready(function() {

}); })()

// Aqui deve ser o seu primeiro // ponto de execução de javascript //

modulejs.define("people.index", function() { return function() { alert("Running index.js") }; });

// arquivo: app/assets/javascripts/views/people/index.js

var mymodule = modulejs.require("people.index"); console.log(mymodule); mymodule();

var mymodule = modulejs.require("people.index"); console.log(mymodule); mymodule();

Page 53: TDC 2015 - Rails & Javascript: faça isso direito

Use o Dispatcher para executar o JS de cada

view

07

Page 54: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo app/assets/javascripts/boot.js   (function() { "use strict";   $(document).ready(function() {

var mymodule = modulejs.require("people.index"); console.log(mymodule); mymodule();

}); })()

Page 55: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo app/assets/javascripts/boot.js   (function() { "use strict";   $(document).ready(function() {

var mymodule = modulejs.require("people.index"); console.log(mymodule); mymodule();

}); })()

A action está hard coded neste ponto!

Page 56: TDC 2015 - Rails & Javascript: faça isso direito

Desafio: como sinalizar para o JS qual a action

que estamos executando?

Page 57: TDC 2015 - Rails & Javascript: faça isso direito

Através do HTML gerado pelo servidor.

Page 58: TDC 2015 - Rails & Javascript: faça isso direito

<body dispatcher="people.index">

Page 59: TDC 2015 - Rails & Javascript: faça isso direito

<body dispatcher="people.index">

<!-- arquivo: app/views/layouts/application.html.erb -->   <body <%= dispatcher_tag %>>

Page 60: TDC 2015 - Rails & Javascript: faça isso direito

<body dispatcher="people.index">

<!-- arquivo: app/views/layouts/application.html.erb -->   <body <%= dispatcher_tag %>>

Criaremos um helper para “batizar" a tag body

Page 61: TDC 2015 - Rails & Javascript: faça isso direito

<body dispatcher="people.index">

# arquivo: app/helpers/application_helper.rb   module ApplicationHelper def dispatcher_tag controller_name = controller.class.name.underscore controller_name.gsub!(/\//, "_") controller_name.gsub!(/_controller$/, "")   div_tag = %(dispatcher="#{controller_name}.#{controller.action_name}")   div_tag.html_safe end end

Page 62: TDC 2015 - Rails & Javascript: faça isso direito

<body dispatcher="people.index">

# arquivo: app/helpers/application_helper.rb   module ApplicationHelper def dispatcher_tag controller_name = controller.class.name.underscore controller_name.gsub!(/\//, "_") controller_name.gsub!(/_controller$/, "")   div_tag = %(dispatcher="#{controller_name}.#{controller.action_name}")   div_tag.html_safe end end

Page 63: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo app/assets/javascripts/boot.js   (function() { "use strict";   $(document).ready(function() {

var mymodule = modulejs.require("people.index"); console.log(mymodule); mymodule();

}); })()

A action está hard coded neste ponto!

<body dispatcher="people.index">

Page 64: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo app/assets/javascripts/boot.js   (function() { "use strict";   $(document).ready(function() {

var mymodule = modulejs.require("people.index"); console.log(mymodule); mymodule();

}); })()

<body dispatcher="people.index">

var dispatch_to = $("body").attr("dispatcher");

Page 65: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo app/assets/javascripts/boot.js   (function() { "use strict";   $(document).ready(function() {

var mymodule = modulejs.require("people.index"); console.log(mymodule); mymodule();

}); })()

<body dispatcher="people.index">

var dispatch_to = $("body").attr("dispatcher");

Page 66: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo app/assets/javascripts/boot.js   (function() { "use strict";   $(document).ready(function() {

var mymodule = modulejs.require(dispatch_to); console.log(mymodule); mymodule();

}); })()

<body dispatcher="people.index">

var dispatch_to = $("body").attr("dispatcher");

Page 67: TDC 2015 - Rails & Javascript: faça isso direito

// arquivo app/assets/javascripts/boot.js   (function() { "use strict";   $(document).ready(function() {

var mymodule = modulejs.require(dispatch_to); console.log(mymodule); mymodule();

}); })()

<body dispatcher="people.index">

var dispatch_to = $("body").attr("dispatcher");

Page 68: TDC 2015 - Rails & Javascript: faça isso direito

Faça seus controllers responderem JSON

08

Page 69: TDC 2015 - Rails & Javascript: faça isso direito

# arquivo: app/controllers/people_controller.rb   ... def index @people = Person.all   respond_to do |format| format.html format.json { render json: @people.to_json } end end ...

Page 70: TDC 2015 - Rails & Javascript: faça isso direito

respond_with foi removido do Rails na versão 4.2.

Page 71: TDC 2015 - Rails & Javascript: faça isso direito

IMHO respond_to deixa mais claro a intenção do

programador.

Page 72: TDC 2015 - Rails & Javascript: faça isso direito

ActiveModel::Serializers Existe coisa melhor do

que to_json

09

Page 73: TDC 2015 - Rails & Javascript: faça isso direito
Page 74: TDC 2015 - Rails & Javascript: faça isso direito

class PostSerializer < ActiveModel::Serializer attributes :title, :body

has_many :comments end

class CommentSerializer < ActiveModel::Serializer attributes :name, :body

belongs_to :post end

Page 75: TDC 2015 - Rails & Javascript: faça isso direito

[ { "title":"Obama mentiu sobre operação que matou Bin Laden", "body":"O jornalista Seymour Hersh, que recebeu o Prêmio Pulitzer…”, "comments": [ { "name":"Fulano", "body":"Eu não acredito!" } ] } ]

Page 76: TDC 2015 - Rails & Javascript: faça isso direito

js-routes: Named Routes do Rails no JS

10

Page 77: TDC 2015 - Rails & Javascript: faça isso direito

Rails.application.routes.draw do resources :people end

people GET /people(.:format) people#index new_person GET /people/new(.:format) people#new

edit_person GET /people/:id/edit(.:format) people#edit person GET /people/:id(.:format) people#show

$ bin/rake routes

Page 78: TDC 2015 - Rails & Javascript: faça isso direito
Page 79: TDC 2015 - Rails & Javascript: faça isso direito

# Arquivo: Gemfile

source 'https://rubygems.org'   gem 'rails', '4.2.0' gem 'sqlite3' gem 'sass-rails', '~> 5.0' gem 'uglifier', '>= 1.3.0' gem 'coffee-rails', '~> 4.1.0’ gem 'jquery-rails'gem 'js-routes'

Page 80: TDC 2015 - Rails & Javascript: faça isso direito

//= require_tree ./views //= require boot//= require js-routes //= require_tree ./views //= require boot

Ajustando o manifesto

// arquivo: application.js   //= require jquery //= require jquery-ujs //= require modulejs

Page 81: TDC 2015 - Rails & Javascript: faça isso direito

Routes.people_path() "/people.json"   Routes.new_person_path() "/people/new.json"   Routes.edit_person_path(1) "/people/1/edit.json"   Routes.person_path(1) "/people/1.json"

Page 82: TDC 2015 - Rails & Javascript: faça isso direito

var promise;   promise = $.get("/people.json");

promise = $.get(Routes.people_path());

Page 83: TDC 2015 - Rails & Javascript: faça isso direito

ejs: fazendo o sprockets servir templates

11

Page 84: TDC 2015 - Rails & Javascript: faça isso direito
Page 85: TDC 2015 - Rails & Javascript: faça isso direito

# Arquivo: Gemfile

source 'https://rubygems.org'   gem 'rails', '4.2.0' gem 'sqlite3' gem 'sass-rails', '~> 5.0' gem 'uglifier', '>= 1.3.0' gem 'coffee-rails', '~> 4.1.0’ gem 'jquery-rails' gem 'js-routes'gem 'ejs'

Page 86: TDC 2015 - Rails & Javascript: faça isso direito

Ajustando o manifesto

//= require_tree ./views //= require boot//= require_tree ./templates //= require_tree ./views //= require boot

// arquivo: application.js   //= require jquery //= require jquery-ujs //= require modulejs //= require js-routes

Page 87: TDC 2015 - Rails & Javascript: faça isso direito

<!-- arquivo: app/assets/javascripts/templates/people/example.jst.ejs -->   <p>Name:</p> <p><%= name %></p>   <p>Phone:</p> <p><%= phone %></p>

Criando um template

Page 88: TDC 2015 - Rails & Javascript: faça isso direito

(function() { "use strict" var html = JST["templates/people/example"]({ name: "The name", phone: "(48) 1234-5678" }); $("div#person").html(html); }())

Consumindo um template

<p>Name:</p> <p><%= name %></p>   <p>Phone:</p> <p><%= phone %></p>

Page 89: TDC 2015 - Rails & Javascript: faça isso direito

(function() { "use strict" var html = JST["templates/people/example"]({ name: "The name", phone: "(48) 1234-5678" }); $("div#person").html(html); }())

Consumindo um template

<p>Name:</p> <p><%= name %></p>   <p>Phone:</p> <p><%= phone %></p>

Array de templatescompilados

Page 90: TDC 2015 - Rails & Javascript: faça isso direito

(function() { "use strict" var html = JST["templates/people/example"]({ name: "The name", phone: "(48) 1234-5678" }); $("div#person").html(html); }())

Consumindo um template

<p>Name:</p> <p><%= name %></p>   <p>Phone:</p> <p><%= phone %></p>

A chave do arrayé o path dotemplate

Page 91: TDC 2015 - Rails & Javascript: faça isso direito

(function() { "use strict" var html = JST["templates/people/example"]({ name: "The name", phone: "(48) 1234-5678" }); $("div#person").html(html); }())

Consumindo um template

<p>Name:</p> <p><%= name %></p>   <p>Phone:</p> <p><%= phone %></p>

O conteúdo doarray é uma função

Page 92: TDC 2015 - Rails & Javascript: faça isso direito

(function() { "use strict" var html = JST["templates/people/example"]({ name: "The name", phone: "(48) 1234-5678" }); $("div#person").html(html); }())

Consumindo um template

<p>Name:</p> <p><%= name %></p>   <p>Phone:</p> <p><%= phone %></p>

Objeto literal com parâmetros do template

Page 93: TDC 2015 - Rails & Javascript: faça isso direito

(function() { "use strict" var html = JST["templates/people/example"]({ name: "The name", phone: "(48) 1234-5678" }); $("div#person").html(html); }())

Consumindo um template

<p>Name:</p> <p><%= name %></p>   <p>Phone:</p> <p><%= phone %></p>

Use o html resul-tante como quiser

Page 94: TDC 2015 - Rails & Javascript: faça isso direito

Crie escopos com IIFE

12

Page 95: TDC 2015 - Rails & Javascript: faça isso direito

IIFE Immediately-invoked function expression

(expressão de função invocada imediatamente)

Page 96: TDC 2015 - Rails & Javascript: faça isso direito

IIFE pode ser usada para isolar escopos

Page 97: TDC 2015 - Rails & Javascript: faça isso direito

  (function() { }())

Immediately-invoked function expression

Page 98: TDC 2015 - Rails & Javascript: faça isso direito

  (function() { }())

Expressão

Immediately-invoked function expression

Page 99: TDC 2015 - Rails & Javascript: faça isso direito

  (function() { }())

Função

Immediately-invoked function expression

Page 100: TDC 2015 - Rails & Javascript: faça isso direito

  (function() { }())

Execução imediata

Immediately-invoked function expression

Page 101: TDC 2015 - Rails & Javascript: faça isso direito

var foo = "value outside IIFE";   (function() { var foo = "value inside IIFE"; console.log(foo); }())   console.log(foo);

// Resultado: // value inside IIFE // value outside IIFE

Page 102: TDC 2015 - Rails & Javascript: faça isso direito

Lembre-se que todos os seus javascripts acabarão

em um único arquivo.

Page 103: TDC 2015 - Rails & Javascript: faça isso direito

Sem escopos os resultados da "uglificação"

são imprevisíveis.

Page 104: TDC 2015 - Rails & Javascript: faça isso direito

Na boa… “use strict"

13

Page 105: TDC 2015 - Rails & Javascript: faça isso direito

(function () { "use strict"; // Introduzido no ECMA 5. // Converte enganos em erros. // Simplifica o uso de variáveis. // Simplifica "eval" e argumentos. // Ajuda a escrever JS mais seguro. // // Referência: Mozilla Developer Network }())

Page 106: TDC 2015 - Rails & Javascript: faça isso direito

(function () { foo = "this should be a private content"; }())

Falta do "var" fará de "foo" global

Falta do “use strict" fará não gerar erro

Page 107: TDC 2015 - Rails & Javascript: faça isso direito

(function () { foo = "this should be a private content"; }())

"use strict";

ReferenceError: Can’t find variable foo

Page 108: TDC 2015 - Rails & Javascript: faça isso direito

(function () { foo = "this should be a private content"; }())

"use strict";

var foo = "this should be a private content";

Page 109: TDC 2015 - Rails & Javascript: faça isso direito

Fuja do callback hell use promises

14

Page 110: TDC 2015 - Rails & Javascript: faça isso direito

var promise;   promise = $.get("/people.json");   promise.done(function(data) { alert("done"); alert(JSON.stringify(data)); });   promise.fail(function(error) { alert("fail"); alert("status text:" + error.statusText); });   promise.always(function() { alert("always"); });

Page 111: TDC 2015 - Rails & Javascript: faça isso direito

So long, and thanks for all the fish!

@cezinha_anjos

cezinha.info

asseinfo.com.br

111