Slide 1

Slide 1 text

Rails ama o seu Front end

Slide 2

Slide 2 text

lucasmazza

Slide 3

Slide 3 text

http://www.casadocodigo.com.br/products/livro-html-css

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

plataformatec/devise

Slide 6

Slide 6 text

plataformatec/simple_form

Slide 7

Slide 7 text

elixir-lang/elixir

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

http://guidelines.plataformatec.com.br

Slide 10

Slide 10 text

Eu quero falar um pouco sobre Rails e Front end

Slide 11

Slide 11 text

“Lucas, por quê eu devo me preocupar em aprender mais sobre Front end?”

Slide 12

Slide 12 text

Dois motivos

Slide 13

Slide 13 text

Você não é o Nando Vieira

Slide 14

Slide 14 text

Você não trabalha com o Eduardo Shiota

Slide 15

Slide 15 text

OK, vamos falar sério

Slide 16

Slide 16 text

Por quê se importar com Front end?

Slide 17

Slide 17 text

Front end não é “Web Design”

Slide 18

Slide 18 text

Não é apenas HTML, CSS e JavaScript

Slide 19

Slide 19 text

Não é só usar o Twitter Bootstrap

Slide 20

Slide 20 text

Mas tudo o que se diz a respeito da experiência dos seus clientes na web

Slide 21

Slide 21 text

Performance

Slide 22

Slide 22 text

Segurança

Slide 23

Slide 23 text

Usabilidade

Slide 24

Slide 24 text

O Rails ajuda em diversos desses pontos

Slide 25

Slide 25 text

Nem tudo vive no app/assets e app/views

Slide 26

Slide 26 text

Helpers Controllers Middlewares Configuração da aplicação

Slide 27

Slide 27 text

E vamos (re)passar por alguns desses tópicos

Slide 28

Slide 28 text

Renderizando HTML

Slide 29

Slide 29 text

Views podem ficar complexas (e alguém precisa resolver isso)

Slide 30

Slide 30 text

Partials, Helpers e Objetos

Slide 31

Slide 31 text

Partials

Slide 32

Slide 32 text

Enxugar duplicações e compartilhar HTML.

Slide 33

Slide 33 text

Extrações prematuras podem ser ruins.

Slide 34

Slide 34 text

app/views/shared/* pode ser um smell.

Slide 35

Slide 35 text

app/views/shared/_user_thumb.html.erb app/views/users/_thumb.html.erb

Slide 36

Slide 36 text

app/views/shared/_search_box.html.erb app/views/search/_box.html.erb

Slide 37

Slide 37 text

Herança de Views

Slide 38

Slide 38 text

<% # app/layouts/application.html.erb <%= render 'sidebar' %> app/views/%{controller}/_sidebar.html.erb # ... app/views/application/_sidebar.html.erb

Slide 39

Slide 39 text

application/_sidebar.html.erb Sidebar genérica categories/_sidebar.html.erb Sidebar de categorias

Slide 40

Slide 40 text

Ótima solução onde as partials variam entre controllers.

Slide 41

Slide 41 text

Variações por view? content_for.

Slide 42

Slide 42 text

<% # app/views/products/index.html.erb %> <% content_for :head do %> <% # meta tags para a lista de produtos %> <% end %> <% # app/views/products/show.html.erb %> <% content_for :head do %> <% # meta tags para um produto %> <% end %>

Slide 43

Slide 43 text

Locals

Slide 44

Slide 44 text

“argumentos” da sua partial.

Slide 45

Slide 45 text

<%= render 'activities/feed' %> <%= render 'activities/feed', pagination: false %> <% # app/views/activities/_feed.html.erb %> <% pagination ||= true %> Valores padrão

Slide 46

Slide 46 text

<%= render 'partial' %> <%= render 'partial', tagline: 'Rubyconf 2013' %> <% # app/views/things/_partial.html.erb %> <%= if defined?(tagline) %>

<%= tagline %>

<% end %> Locals opcionais

Slide 47

Slide 47 text

“defined?” Melhor não.

Slide 48

Slide 48 text

local_assigns Hash com todas as “locals”.

Slide 49

Slide 49 text

<%= render 'partial' %> <%= render 'partial', tagline: 'Rubyconf 2013' %> <% # app/views/things/_partial.html.erb %> <%= if local_assigns.key?(:tagline) %>

<%= tagline %>

<% end %> Locals opcionais

Slide 50

Slide 50 text

View Models Decorators Cells Widgets Presenters View Objects

Slide 51

Slide 51 text

Soluções diferentes para problemas diferentes.

Slide 52

Slide 52 text

Decorator Pattern “(..) design pattern that allows behavior to be added to an individual object, (..), without affecting the behavior of other objects from the same class.” http://en.wikipedia.org/wiki/Decorator_pattern

Slide 53

Slide 53 text

Presenter Pattern “(..) a class representation of the state of the view.” Jay Fields - http://blog.jayfields.com/2007/03/rails-presenter-pattern.html

Slide 54

Slide 54 text

class ProductPresenter < SomePresenterGem attribute :name, :sku, :price, :photo, :category, :description, :id, :slug, :available?, :pretty_much_every_attribute_ever def created_at object.created_at.strftime('%A, %B %e') end def product_link helpers.link_to(name, object) end end

Slide 55

Slide 55 text

class ProductPresenter < SomePresenterGem attribute :name, :sku, :price, :photo, :category, :description, :id, :slug, :available?, :pretty_much_every_attribute_ever def created_at object.created_at.strftime('%A, %B %e') end def product_link helpers.link_to(name, object) end end ಠ_ಠ

Slide 56

Slide 56 text

Cadê a complexidade que você queria resolver?

Slide 57

Slide 57 text

“Presenters do add an additional layer, and thus more complexity. (...)” Jay Fields - http://blog.jayfields.com/2007/03/rails-presenter-pattern.html

Slide 58

Slide 58 text

•Indireção desnecessária.

Slide 59

Slide 59 text

•Indireção desnecessária. •Burocracia extra.

Slide 60

Slide 60 text

•Indireção desnecessária. •Burocracia extra. •‘@article’ não é um Article.

Slide 61

Slide 61 text

“(...) A presenter is not likely to be a automatic decision or standard. (...)” Jay Fields - http://blog.jayfields.com/2007/03/rails-presenter-pattern.html

Slide 62

Slide 62 text

•‘Presenter’ ALL THE THINGS!

Slide 63

Slide 63 text

•‘Presenter’ ALL THE THINGS! •Presenters no lugar de Helpers

Slide 64

Slide 64 text

•‘Presenter’ ALL THE THINGS! •Presenters no lugar de Helpers •HTML fora das views

Slide 65

Slide 65 text

“(...) Presenters are generally introduced when actions are required to act upon various models or the data in the database needs to be manipulated in various ways before it is displayed in the view.” Jay Fields - http://blog.jayfields.com/2007/03/rails-presenter-pattern.html

Slide 66

Slide 66 text

•Agregações complexas

Slide 67

Slide 67 text

•Agregações complexas •Abstrações que ainda não existem

Slide 68

Slide 68 text

•Agregações complexas •Abstrações que ainda não existem • Navegações

Slide 69

Slide 69 text

•Agregações complexas •Abstrações que ainda não existem • Navegações • Calendários

Slide 70

Slide 70 text

•Agregações complexas •Abstrações que ainda não existem • Navegações • Calendários • Gráficos

Slide 71

Slide 71 text

•Agregações complexas •Abstrações que ainda não existem • Navegações • Calendários • Gráficos • ‘Value Objects’ isolados

Slide 72

Slide 72 text

calendar = EventsCalendar.new(8, 2013, events) calendar.month # => 'Agosto' calendar.days.each do |day, events| # 1 => [#, #] # ... end calendar.previous_month # => 07/2013 calendar.next_month # => 09/2013

Slide 73

Slide 73 text

Domínio Interface

Slide 74

Slide 74 text

Domínio Interface Genérico Específico

Slide 75

Slide 75 text

Domínio Interface ?? Genérico Específico

Slide 76

Slide 76 text

(View) Models? (novas abstracões) Decorators? (novos comportamentos) Presenters? (componentes específicos da sua interface)

Slide 77

Slide 77 text

Soluções diferentes para problemas diferentes

Slide 78

Slide 78 text

Soluções diferentes para problemas diferentes (que o Rails não vai resolver para você)

Slide 79

Slide 79 text

Helpers

Slide 80

Slide 80 text

Muita gente reclama de Helpers

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

escopo global das views

Slide 83

Slide 83 text

Métodos sem relação escopo global das views

Slide 84

Slide 84 text

Métodos sem relação helpers are shit! escopo global das views

Slide 85

Slide 85 text

Helpers como uma DSL para melhorar as suas views

Slide 86

Slide 86 text

def lazy_image_tag(name, options = {}) # ... end # def follow_button_to(user, options = {}, &block) # ... end #

Slide 87

Slide 87 text

E não para fazer todo o trabalho sujo

Slide 88

Slide 88 text

“Generally speaking, it’s a good idea to keep your HTML in your templates and out of your helpers.” Ryan Singer - "What belongs in a helper method?" http://37signals.com/svn/posts/1108-what-belongs-in-a-helper-method

Slide 89

Slide 89 text

“Helpers are useful when they hide implementation details that are irrelevant to your template, or when they allow you to abstract common template code to avoid repetition” Ryan Singer - "What belongs in a helper method?" http://37signals.com/svn/posts/1108-what-belongs-in-a-helper-method

Slide 90

Slide 90 text

“If you find yourself generating a lot of HTML in a helper, think twice and try to keep as much HTML in your template as possible.” Ryan Singer - "What belongs in a helper method?" http://37signals.com/svn/posts/1108-what-belongs-in-a-helper-method

Slide 91

Slide 91 text

Views podem ficar complexas (e você precisa resolver isso)

Slide 92

Slide 92 text

Refatore as suas views de acordo com o seu problema.

Slide 93

Slide 93 text

Partials (HTML duplicado e compartilhado)

Slide 94

Slide 94 text

Helpers (Detalhes de implementação da sua view)

Slide 95

Slide 95 text

Objetos (Lógica demais que precisa de abstrações melhores)

Slide 96

Slide 96 text

JavaScript E Ajax

Slide 97

Slide 97 text

JavaScript não obstrusivo para os usuários

Slide 98

Slide 98 text

Sigh, JavaScript http://sighjavascript.tumblr.com/

Slide 99

Slide 99 text

JavaScript não obstrusivo para desenvolvedores

Slide 100

Slide 100 text

•Separação de responsabilidades

Slide 101

Slide 101 text

•Separação de responsabilidades •Desacoplamento do HTML e JS

Slide 102

Slide 102 text

•Separação de responsabilidades •Desacoplamento do HTML e JS •data-* ou js-*

Slide 103

Slide 103 text

•Separação de responsabilidades •Desacoplamento do HTML e JS •data-* ou js-* •Código modular

Slide 104

Slide 104 text

•Separação de responsabilidades •Desacoplamento do HTML e JS •data-* ou js-* •Código modular •JavaScript testável

Slide 105

Slide 105 text

No content

Slide 106

Slide 106 text

$(document).on('submit', 'form', function(event) {

Slide 107

Slide 107 text

$(document).on('submit', 'form', function(event) { event.preventDefault();

Slide 108

Slide 108 text

$(document).on('submit', 'form', function(event) { event.preventDefault(); var form = $(this);

Slide 109

Slide 109 text

$(document).on('submit', 'form', function(event) { event.preventDefault(); var form = $(this); $.ajax({

Slide 110

Slide 110 text

$(document).on('submit', 'form', function(event) { event.preventDefault(); var form = $(this); $.ajax({ type: form.attr('method'),

Slide 111

Slide 111 text

$(document).on('submit', 'form', function(event) { event.preventDefault(); var form = $(this); $.ajax({ type: form.attr('method'), url: form.attr('action'),

Slide 112

Slide 112 text

$(document).on('submit', 'form', function(event) { event.preventDefault(); var form = $(this); $.ajax({ type: form.attr('method'), url: form.attr('action'), data: form.serializeArray();

Slide 113

Slide 113 text

$(document).on('submit', 'form', function(event) { event.preventDefault(); var form = $(this); $.ajax({ type: form.attr('method'), url: form.attr('action'), data: form.serializeArray(); success: function() { },

Slide 114

Slide 114 text

$(document).on('submit', 'form', function(event) { event.preventDefault(); var form = $(this); $.ajax({ type: form.attr('method'), url: form.attr('action'), data: form.serializeArray(); success: function() { }, complete: function() { },

Slide 115

Slide 115 text

$(document).on('submit', 'form', function(event) { event.preventDefault(); var form = $(this); $.ajax({ type: form.attr('method'), url: form.attr('action'), data: form.serializeArray(); success: function() { }, complete: function() { }, error: function() { }

Slide 116

Slide 116 text

$(document).on('submit', 'form', function(event) { event.preventDefault(); var form = $(this); $.ajax({ type: form.attr('method'), url: form.attr('action'), data: form.serializeArray(); success: function() { }, complete: function() { }, error: function() { } });

Slide 117

Slide 117 text

$(document).on('submit', 'form', function(event) { event.preventDefault(); var form = $(this); $.ajax({ type: form.attr('method'), url: form.attr('action'), data: form.serializeArray(); success: function() { }, complete: function() { }, error: function() { } }); });

Slide 118

Slide 118 text

rails/jquery-ujs

Slide 119

Slide 119 text

$(document).on('ajax:success', 'form[data-remote]', function() { // ... }); $(document).on('ajax:before', 'form[data-remote]', function() { // ... }); $(document).on('ajax:error', 'form[data-remote]', function() { // ... });

Slide 120

Slide 120 text

<%= form_for @user, remote: true do |form| %> <%= form.submit 'Cadastrar!', data: { disable_with: 'Aguarde..' } %> <% end %> <%= link_to 'Apagar', @user, remote: true, method: :delete, data: { confirm: 'Tem certeza?' } %> data-* goodies

Slide 121

Slide 121 text

•Diversos eventos disponíveis.

Slide 122

Slide 122 text

•Diversos eventos disponíveis. •CSRF token em todas requisições.

Slide 123

Slide 123 text

•Diversos eventos disponíveis. •CSRF token em todas requisições. •Bindings via delegate.

Slide 124

Slide 124 text

Abstraia o trabalho sujo de fazer requisições Ajax Assim como o Active Record abstrai consultas no seu banco.

Slide 125

Slide 125 text

Abstraia o trabalho sujo de fazer requisições Ajax Escreva testes para os eventos, não para a interação de rede.

Slide 126

Slide 126 text

E as repostas?

Slide 127

Slide 127 text

1. HTML 2.JSON 3.JS

Slide 128

Slide 128 text

1 Responder HTML pode ser a solução mais prática.

Slide 129

Slide 129 text

Server side HTML & Lógica client side

Slide 130

Slide 130 text

$(document).on('ajax:error', '#new_comment', function(event, xhr) { $(this).replaceWith(xhr.responseText); }); $(document).on('ajax:success', '#new_comment', function(event, html) { $('#comments').prepend(html); }); render partial: @comment render partial: ‘form’

Slide 131

Slide 131 text

•Reaproveitamento da sua camada inteira de views, helpers & cia.

Slide 132

Slide 132 text

•Reaproveitamento da sua camada inteira de views, helpers & cia. •Lógica cacheada no client.

Slide 133

Slide 133 text

•Reaproveitamento da sua camada inteira de views, helpers & cia. •Lógica cacheada no client. •Eventos testáveis.

Slide 134

Slide 134 text

2 Responda JSON caso você precise de algo mais complexo

Slide 135

Slide 135 text

{ "meta": "

Created 3 hours ago, 3 comments.

", "new_comment": "
...
", // ... }

Slide 136

Slide 136 text

3 Respostas em JavaScript é código sem contexto

Slide 137

Slide 137 text

3 Respostas em JavaScript é código sem testes

Slide 138

Slide 138 text

// app/views/comments/create.js.erb $('#comments').prepend("<%= j(render(@comment)) %>");

Slide 139

Slide 139 text

// app/views/comments/create.js.erb $('#comments').prepend("<%= j(render(@comment)) %>"); if(window._gaq) { window._gaq.push(['_trackEvent', 'comment:created', "<%= @post.id %>"]); }

Slide 140

Slide 140 text

// app/views/comments/create.js.erb $('#comments').prepend("<%= j(render(@comment)) %>"); if(window._gaq) { window._gaq.push(['_trackEvent', 'comment:created', "<%= @post.id %>"]); } $('#comments :first').addClass('highlighted');

Slide 141

Slide 141 text

// app/views/comments/create.js.erb $('#comments').prepend("<%= j(render(@comment)) %>"); if(window._gaq) { window._gaq.push(['_trackEvent', 'comment:created', "<%= @post.id %>"]); } $('#comments :first').addClass('highlighted'); // Sabe lá o que mais...

Slide 142

Slide 142 text

4 Respostas em branco quando você não precisa de corpo nenhum :)

Slide 143

Slide 143 text

$(document).on('ajax:success', function(event, html, status, xhr) { var redirectTo = xhr.getResponseHeader('Location'); if(redirectTo) { window.location = redirectTo; } });

Slide 144

Slide 144 text

$(document).on('ajax:success', function(event, html, status, xhr) { var redirectTo = xhr.getResponseHeader('Location'); if(redirectTo) { window.location = redirectTo; } }); render nothing: true ou head :created, location: '...'

Slide 145

Slide 145 text

Sprockets

Slide 146

Slide 146 text

Rails 3.1.x + Sprockets 2.2.2

Slide 147

Slide 147 text

Rails 4.x Sprockets 2.8.x +

Slide 148

Slide 148 text

sstephenson/sprockets rails/sprockets-rails

Slide 149

Slide 149 text

Mais rápido mais estável novas features

Slide 150

Slide 150 text

Até o Nando Vieira gostou.

Slide 151

Slide 151 text

jquery-rails jquery-ui-rails twitter-bootstrap-rails less-rails-bootstrap chosen-rails font-awesome-sass- rails modernizr-rails select2-rails fancybox-rails underscore-rails bootstrap-sass-rails requirejs-rails facebox-rails formalize-rails momentjs-rails zero-clipboard-rails skeleton-rails mini-bootstrap-rails html5shiv-rails zepto-rails bootstrap-tooltip-rails kube-rails ETooMuchGems

Slide 152

Slide 152 text

twitter/bower

Slide 153

Slide 153 text

// .bowerrc { "directory": "vendor/assets/components" }

Slide 154

Slide 154 text

// bower.json { "name": "my-rails-app", "version": "0.0.0", "ignore": [ "**/.*", "node_modules", "bower_components", "test", "tests" ], "dependencies": { "jquery-pjax": "1.7.3", "momentjs": "2.1.0", "module": "git://github.com/fnando/module.git" } }

Slide 155

Slide 155 text

$ bower install ... bower module#* install module#518331e7d5 bower jquery-pjax#1.7.3 install jquery-pjax#1.7.3 bower momentjs#2.1.0 install momentjs#2.1.0 bower jquery#>=1.8 install jquery#2.0.3 module#518331e7d5 vendor/assets/components/module jquery-pjax#1.7.3 vendor/assets/components/jquery-pjax └── jquery#2.0.3 momentjs#2.1.0 vendor/assets/components/momentjs jquery#2.0.3 vendor/assets/components/jquery

Slide 156

Slide 156 text

// app/assets/javascripts/application.js //= require jquery //= require jquery-pjax //= require momentjs //= require module

Slide 157

Slide 157 text

Server Sent Events

Slide 158

Slide 158 text

class EventsController < ApplicationController include ActionController::Live def subscribe response.headers['Content-Type'] = 'text/event-stream' loop do response.stream.write 'data: WHATSUP!\n\n' sleep 5 end rescue IOError # Connection closed ensure response.stream.close end end ActionController::Live

Slide 159

Slide 159 text

var source = new EventSource('/events/subscribe'); source.addEventListener('message', function(event) { alert(event.data); }, false); E o seu JavaScript...

Slide 160

Slide 160 text

class EventsController < ApplicationController include ActionController::Live def subscribe response.headers['Content-Type'] = 'text/event-stream' sse = SSE.new(response.stream) loop do sse.write('WHATSUP!') sleep 5 end rescue IOError # Connection closed ensure sse.close end end Rails 4.1

Slide 161

Slide 161 text

Adeus long polling!

Slide 162

Slide 162 text

Redis Pub/Sub PostgreSQL LISTEN/NOTIFY

Slide 163

Slide 163 text

tenderlove/tusk

Slide 164

Slide 164 text

Cross Origin Resource Sharing

Slide 165

Slide 165 text

TL,DR: Requisições ajax entre domínios diferentes.

Slide 166

Slide 166 text

JSONP é gambiarra

Slide 167

Slide 167 text

Permissões específicas de: •Hosts •Métodos •Headers

Slide 168

Slide 168 text

Access-Control-Allow-Origin Access-Control-Allow-Credentials Access-Control-Allow-Methods Access-Control-Allow-Headers Access-Control-Expose-Headers ...

Slide 169

Slide 169 text

# config/application.rb config.action_dispatch. default_headers['Access-Control-Allow-Credentials'] = 'true' config.action_dispatch. default_headers['Access-Control-Allow-Origin'] = '*' config.action_dispatch. default_headers['Access-Control-Expose-Headers'] = 'X-My-Header' config.action_dispatch. default_headers['Access-Control-Allow-Headers'] = 'X-Request-Header'

Slide 170

Slide 170 text

# app/controllers/your_controller.rb before_action :set_cors_headers def set_cors_headers headers['Access-Control-Allow-Origin'] = '*' # ... end

Slide 171

Slide 171 text

cyu/rack-cors

Slide 172

Slide 172 text

Content Security Policy

Slide 173

Slide 173 text

XSS é coisa séria

Slide 174

Slide 174 text

O Rails já ajuda bastante ao sanitizar o seu HTML

Slide 175

Slide 175 text

Mas não quer dizer que você não deve se proteger (mais)

Slide 176

Slide 176 text

•eval nunca mais

Slide 177

Slide 177 text

•eval nunca mais •Whitelist de CDNs

Slide 178

Slide 178 text

•eval nunca mais •Whitelist de CDNs •‘onclick’ & amigos

Slide 179

Slide 179 text

•eval nunca mais •Whitelist de CDNs •‘onclick’ & amigos •CSS/JavaScript inline

Slide 180

Slide 180 text

•eval nunca mais •Whitelist de CDNs •‘onclick’ & amigos •CSS/JavaScript inline •Reporting API

Slide 181

Slide 181 text

“Activating CSP in a Rails app is trivial since it's just a simple header. You don't need any separate libraries; a simple before filter should do.” https://github.com/blog/1477-content-security-policy class ApplicationController < ActionController::Base before_filter :set_csp def set_csp response.headers['Content-Security-Policy'] = "default-src *; script-src https://assets.example.com; style-src https:// assets.example.com" end end

Slide 182

Slide 182 text

twitter/secureheaders

Slide 183

Slide 183 text

RECAP

Slide 184

Slide 184 text

Front end é uma parte importante da sua aplicação

Slide 185

Slide 185 text

E se você trabalha com a web não tem como fugir disso

Slide 186

Slide 186 text

Não é para você trocar de cargo semana que vem

Slide 187

Slide 187 text

Mas você ajudar a sua equipe a criar projetos melhores

Slide 188

Slide 188 text

O Rails e a comunidade Ruby podem te ajudar bastante.

Slide 189

Slide 189 text

.mobile views Mustache Cache Sweepers Compass AS::Notifications Pjax Jasmine Bourbon Turbolinks Peek Simple Form i18n-js AssetSync MailView

Slide 190

Slide 190 text

Mas o resto é com você =)

Slide 191

Slide 191 text

Obrigado! twitter.com/lucasmazza speakerdeck.com/lucas