Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rails ama o seu Front end

Rails ama o seu Front end

5a90a67fa1a92e6a4b605cfd8da5e375?s=128

Lucas Mazza

August 29, 2013
Tweet

Transcript

  1. Rails ama o seu Front end

  2. lucasmazza

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

  4. None
  5. plataformatec/devise

  6. plataformatec/simple_form

  7. elixir-lang/elixir

  8. None
  9. http://guidelines.plataformatec.com.br

  10. Eu quero falar um pouco sobre Rails e Front end

  11. “Lucas, por quê eu devo me preocupar em aprender mais

    sobre Front end?”
  12. Dois motivos

  13. Você não é o Nando Vieira

  14. Você não trabalha com o Eduardo Shiota

  15. OK, vamos falar sério

  16. Por quê se importar com Front end?

  17. Front end não é “Web Design”

  18. Não é apenas HTML, CSS e JavaScript

  19. Não é só usar o Twitter Bootstrap

  20. Mas tudo o que se diz a respeito da experiência

    dos seus clientes na web
  21. Performance

  22. Segurança

  23. Usabilidade

  24. O Rails ajuda em diversos desses pontos

  25. Nem tudo vive no app/assets e app/views

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

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

  28. Renderizando HTML

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

  30. Partials, Helpers e Objetos

  31. Partials

  32. Enxugar duplicações e compartilhar HTML.

  33. Extrações prematuras podem ser ruins.

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

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

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

  37. Herança de Views

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

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

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

  41. Variações por view? content_for.

  42. <% # 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 %>
  43. Locals

  44. “argumentos” da sua partial.

  45. <%= render 'activities/feed' %> <%= render 'activities/feed', pagination: false %>

    <% # app/views/activities/_feed.html.erb %> <% pagination ||= true %> Valores padrão
  46. <%= render 'partial' %> <%= render 'partial', tagline: 'Rubyconf 2013'

    %> <% # app/views/things/_partial.html.erb %> <%= if defined?(tagline) %> <h2><%= tagline %></h2> <% end %> Locals opcionais
  47. “defined?” Melhor não.

  48. local_assigns Hash com todas as “locals”.

  49. <%= render 'partial' %> <%= render 'partial', tagline: 'Rubyconf 2013'

    %> <% # app/views/things/_partial.html.erb %> <%= if local_assigns.key?(:tagline) %> <h2><%= tagline %></h2> <% end %> Locals opcionais
  50. View Models Decorators Cells Widgets Presenters View Objects

  51. Soluções diferentes para problemas diferentes.

  52. 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
  53. Presenter Pattern “(..) a class representation of the state of

    the view.” Jay Fields - http://blog.jayfields.com/2007/03/rails-presenter-pattern.html
  54. 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
  55. 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 ಠ_ಠ
  56. Cadê a complexidade que você queria resolver?

  57. “Presenters do add an additional layer, and thus more complexity.

    (...)” Jay Fields - http://blog.jayfields.com/2007/03/rails-presenter-pattern.html
  58. •Indireção desnecessária.

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

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

  61. “(...) A presenter is not likely to be a automatic

    decision or standard. (...)” Jay Fields - http://blog.jayfields.com/2007/03/rails-presenter-pattern.html
  62. •‘Presenter’ ALL THE THINGS!

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

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

    fora das views
  65. “(...) 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
  66. •Agregações complexas

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

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

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

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

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

    Calendários • Gráficos • ‘Value Objects’ isolados
  72. calendar = EventsCalendar.new(8, 2013, events) calendar.month # => 'Agosto' calendar.days.each

    do |day, events| # 1 => [#<Event>, #<Event>] # ... end calendar.previous_month # => 07/2013 calendar.next_month # => 09/2013
  73. Domínio Interface

  74. Domínio Interface Genérico Específico

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

  76. (View) Models? (novas abstracões) Decorators? (novos comportamentos) Presenters? (componentes específicos

    da sua interface)
  77. Soluções diferentes para problemas diferentes

  78. Soluções diferentes para problemas diferentes (que o Rails não vai

    resolver para você)
  79. Helpers

  80. Muita gente reclama de Helpers

  81. None
  82. escopo global das views

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

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

  85. Helpers como uma DSL para melhorar as suas views

  86. def lazy_image_tag(name, options = {}) # ... end # <img

    src='dot.gif' data-original='...'> def follow_button_to(user, options = {}, &block) # ... end # <a href='/users/123/follow' class='follow-btn'>...</a>
  87. E não para fazer todo o trabalho sujo

  88. “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
  89. “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
  90. “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
  91. Views podem ficar complexas (e você precisa resolver isso)

  92. Refatore as suas views de acordo com o seu problema.

  93. Partials (HTML duplicado e compartilhado)

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

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

  96. JavaScript E Ajax

  97. JavaScript não obstrusivo para os usuários

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

  99. JavaScript não obstrusivo para desenvolvedores

  100. •Separação de responsabilidades

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

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

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

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

    js-* •Código modular •JavaScript testável
  105. None
  106. $(document).on('submit', 'form', function(event) {

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

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

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

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

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

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

    type: form.attr('method'), url: form.attr('action'), data: form.serializeArray();
  113. $(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() { },
  114. $(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() { },
  115. $(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() { }
  116. $(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() { } });
  117. $(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() { } }); });
  118. rails/jquery-ujs

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

    { // ... }); $(document).on('ajax:error', 'form[data-remote]', function() { // ... });
  120. <%= 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
  121. •Diversos eventos disponíveis.

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

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

    delegate.
  124. Abstraia o trabalho sujo de fazer requisições Ajax Assim como

    o Active Record abstrai consultas no seu banco.
  125. Abstraia o trabalho sujo de fazer requisições Ajax Escreva testes

    para os eventos, não para a interação de rede.
  126. E as repostas?

  127. 1. HTML 2.JSON 3.JS

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

  129. Server side HTML & Lógica client side

  130. $(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’
  131. •Reaproveitamento da sua camada inteira de views, helpers & cia.

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

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

    •Lógica cacheada no client. •Eventos testáveis.
  134. 2 Responda JSON caso você precise de algo mais complexo

  135. { "meta": "<p>Created 3 hours ago, <a href=\"#comments\">3 comments</a>.</p>", "new_comment":

    "<div>...</div>", // ... }
  136. 3 Respostas em JavaScript é código sem contexto

  137. 3 Respostas em JavaScript é código sem testes

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

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

    @post.id %>"]); }
  140. // 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');
  141. // 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...
  142. 4 Respostas em branco quando você não precisa de corpo

    nenhum :)
  143. $(document).on('ajax:success', function(event, html, status, xhr) { var redirectTo = xhr.getResponseHeader('Location');

    if(redirectTo) { window.location = redirectTo; } });
  144. $(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: '...'
  145. Sprockets

  146. Rails 3.1.x + Sprockets 2.2.2

  147. Rails 4.x Sprockets 2.8.x +

  148. sstephenson/sprockets rails/sprockets-rails

  149. Mais rápido mais estável novas features

  150. Até o Nando Vieira gostou.

  151. 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
  152. twitter/bower

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

  154. // 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" } }
  155. $ 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
  156. // app/assets/javascripts/application.js //= require jquery //= require jquery-pjax //= require

    momentjs //= require module
  157. Server Sent Events

  158. 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
  159. var source = new EventSource('/events/subscribe'); source.addEventListener('message', function(event) { alert(event.data); },

    false); E o seu JavaScript...
  160. 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
  161. Adeus long polling!

  162. Redis Pub/Sub PostgreSQL LISTEN/NOTIFY

  163. tenderlove/tusk

  164. Cross Origin Resource Sharing

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

  166. JSONP é gambiarra

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

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

  169. # 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'
  170. # app/controllers/your_controller.rb before_action :set_cors_headers def set_cors_headers headers['Access-Control-Allow-Origin'] = '*' #

    ... end
  171. cyu/rack-cors

  172. Content Security Policy

  173. XSS é coisa séria

  174. O Rails já ajuda bastante ao sanitizar o seu HTML

  175. Mas não quer dizer que você não deve se proteger

    (mais)
  176. •eval nunca mais

  177. •eval nunca mais •Whitelist de CDNs

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

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

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

    inline •Reporting API
  181. “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
  182. twitter/secureheaders

  183. RECAP

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

  185. E se você trabalha com a web não tem como

    fugir disso
  186. Não é para você trocar de cargo semana que vem

  187. Mas você ajudar a sua equipe a criar projetos melhores

  188. O Rails e a comunidade Ruby podem te ajudar bastante.

  189. .mobile views Mustache Cache Sweepers Compass AS::Notifications Pjax Jasmine Bourbon

    Turbolinks Peek Simple Form i18n-js AssetSync MailView
  190. Mas o resto é com você =)

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