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

Rails Frontend: 2016 edition.

Rails Frontend: 2016 edition.

Slides from talk about enhancements in Rails Frontend.

vipulnsward

June 24, 2016
Tweet

More Decks by vipulnsward

Other Decks in Programming

Transcript

  1. RAILS FRONTEND: 2016
    and That Sprockets Talk

    View full-size slide

  2. Vipul A M
    @vipulnsward

    View full-size slide

  3. timezones
    1.Mumbai TimeZone (UTC+05:30)
    2.Taiwan TimeZone (UTC+08:00)
    3.Pacific Time Zone (UTC-08:00)
    4.Singapore Time Zone (UTC+08:00)

    View full-size slide

  4. http://blog.bigbinary.com/categories/Rails-5
    Rails 5 Blog Series (55+ and counting)

    View full-size slide

  5. Sprockets: *Spotlight

    View full-size slide

  6. Sprockets
    https://speakerdeck.com/rafaelfranca/how-sprockets-works

    View full-size slide

  7. Sprockets
    1. sprockets
    2. sprockets-rails
    3. sass-rails
    4. execjs
    5. coffee-rails

    View full-size slide

  8. manifest.js
    # In Rails configuration

    config.assets.precompile += [“payment.css”,
    “advertising.css"]
    <%= stylesheet_link_tag "advertising" %>

    View full-size slide

  9. manifest.js
    config.assets.precompile = [“manifest.js”]
    // app/assets/config/manifest.js
    //= link_tree ../images

    //= link_directory ../stylesheets .css
    //= link application.css

    //= link advertising.css

    //= link payment.css

    //= link application.js


    View full-size slide

  10. manifest.js
    <%= stylesheet_link_tag "advertising" %>
    .logo {

    background: url(<%= asset_url("logo.png") %>)

    }

    View full-size slide

  11. folders as modules
    foo/index.js
    <%= asset_path("foo/index.js") %>

    <%= asset_path("foo.js") %>

    View full-size slide

  12. folders as modules
    //= require_tree .
    foo/jquery-ui.js and foo/jquery.min.js
    Now:
    //= require foo.js
    # foo/index.js
    //= require foo.min.js

    //= require foo-ui.js


    View full-size slide

  13. sourcemaps.js

    View full-size slide

  14. sourcemaps.js

    View full-size slide

  15. sourcemaps.js
    //# sourceMappingURL=
    File: public/assets/application.js
    Map: public/assets/application.js.map
    //# sourceMappingURL=/assets/application.js.map
    /*# sourceMappingURL=application.css.map */

    View full-size slide

  16. sourcemaps.js

    View full-size slide

  17. sourcemaps.js

    View full-size slide

  18. ES2016!
    1. Arrows and Lexical This
    2. Classes
    3. Enhanced Object Literals
    4. Template Strings
    5. Destructuring
    6. Default + Rest + Spread
    7. Let + Const
    8. Iterators + For..Of
    9. Symbols, etc

    View full-size slide

  19. 12x
    @schneems
    https://engineering.heroku.com/blogs/2016-02-18-
    speeding-up-sprockets/

    View full-size slide

  20. 1.raw asset
    2.processed asset
    3.bundled
    4.compressed
    5.asset fingerprint
    6.minified
    7.etc
    12x

    View full-size slide

  21. # https://github.com/rails/sprockets/blob/543a5a27190c26de8f3a1b03e18aed8da0367c63/lib/sprockets/base.rb#L46-L57


    def file_digest(path)

    if stat = File.stat(path)

    cache.fetch("file_digest:#{path}:#{stat.mtime.to_i}")
    do

    Digest::SHA256.file(path.to_s).digest

    end

    end

    end
    12x

    View full-size slide

  22. “file_digest:/Users/schneems/my_project/app/assets/javascripts/application.js:
    123456”
    "file_digest:/+Other/path/+my_project/app/assets/javascripts/application.js:
    123456"
    12x

    View full-size slide

  23. # https://github.com/rails/sprockets/blob/9ca80fe00971d45ccfacb6414c73d5ffad96275f/lib/sprockets/loader.rb#L55-L58
    digest =
    DigestUtils.digest(resolve_dependencies(paths))

    if uri_from_cache =
    cache.get(unloaded.digest_key(digest), true)

    asset_from_cache(UnloadedAsset.new(uri_from_cache,
    self).asset_key)

    end
    12x!

    View full-size slide

  24. <%= javascript_include_tag :application,
    integrity: true %>

    src="/assets/application.js"
    integrity=“sha256-
    TvVUHzSfftWg1rcfL6TIJ0XKEGrgLyEq6lEpcmrG9qs=">

    SRI

    View full-size slide

  25. class ItemsController < ApplicationController

    def show

    @item = Item.find(params[:id])

    fresh_when @item

    end

    end
    Etag

    View full-size slide

  26. $ curl -i http://localhost:3000/items/1


    HTTP/1.1 200 OK

    X-Frame-Options: SAMEORIGIN

    X-Xss-Protection: 1; mode=block

    X-Content-Type-Options: nosniff

    Etag: "618bbc92e2d35ea1945008b42799b0e7"

    Last-Modified: Sat, 30 Jan 2016 08:02:12 GMT

    Content-Type: text/html; charset=utf-8

    Cache-Control: max-age=0, private, must-revalidate

    X-Request-Id: 98359119-14ae-4e4e-8174-708abbc3fd4b

    X-Runtime: 0.412232

    Server: WEBrick/1.3.1 (Ruby/2.2.2/2015-04-13)

    Date: Fri, 04 Mar 2016 10:50:38 GMT

    Content-Length: 1014

    Connection: Keep-Alive
    Etag

    View full-size slide

  27. $ curl -i -H 'If-None-Match: "618bbc92e2d35ea1945008b42799b0e7"' http://
    localhost:3000/items/1


    HTTP/1.1 304 Not Modified

    X-Frame-Options: SAMEORIGIN

    X-Xss-Protection: 1; mode=block

    X-Content-Type-Options: nosniff

    Etag: "618bbc92e2d35ea1945008b42799b0e7"

    Last-Modified: Sat, 30 Jan 2016 08:02:12 GMT

    Cache-Control: max-age=0, private, must-revalidate

    X-Request-Id: e4447f82-b96c-4482-a5ff-4f5003910c18

    X-Runtime: 0.012878

    Server: WEBrick/1.3.1 (Ruby/2.2.2/2015-04-13)

    Date: Fri, 04 Mar 2016 10:51:22 GMT

    Connection: Keep-Alive
    Etag

    View full-size slide

  28. Strong Etag: ”543b39c23d8d34c232b457297d38ad99"
    Weak ETag: W/“543b39c23d8d34c232b457297d38ad99"
    Etag

    View full-size slide

  29. # app/controllers/home_controller.rb

    class HomeController < ApplicationController

    def index

    render

    end

    end


    # app/views/home/index.html.erb

    Welcome
    http_cache_forever

    View full-size slide

  30. Processing by HomeController#index as HTML

    Rendered home/index.html.erb within layouts/
    application (1.3ms)

    Completed 200 OK in 224ms (Views: 212.4ms |
    ActiveRecord: 0.0ms)


    And so on for every request for this action.

    http_cache_forever

    View full-size slide

  31. # app/controllers/home_controller.rb

    class HomeController < ApplicationController

    def index

    http_cache_forever(public: true) {}

    end

    end


    # OR

    class HomeController < ApplicationController

    def index

    http_cache_forever(public: true) do

    render

    end

    end

    end



    # app/views/home/index.html.erb

    Welcome
    http_cache_forever

    View full-size slide

  32. # When request is made for the first time.


    Processing by HomeController#index as HTML

    Rendered home/index.html.erb within layouts/
    application (1.3ms)

    Completed 200 OK in 224ms (Views: 212.4ms |
    ActiveRecord: 0.0ms)


    # For consecutive requests for the same page


    Processing by HomeController#index as HTML

    Completed 304 Not Modified in 2ms (ActiveRecord:
    0.0ms)
    http_cache_forever

    View full-size slide


  33. Cache-Control: max-age=3155760000
    http_cache_forever

    View full-size slide

  34. Asset Headers

    View full-size slide

  35. response.headers['X-Tracking-ID'] = '123456'
    Headers in Simple Responses

    View full-size slide

  36. # open config/environments/production.rb and add following line

    config.static_cache_control = 'public, max-age=1000'
    Asset Headers

    View full-size slide

  37. PageSpeed Warnings

    View full-size slide

  38. Missing Expires header

    View full-size slide

  39. # production.rb


    config.public_file_server.headers = {

    'Cache-Control' => 'public, s-maxage=31536000,
    maxage=15552000',

    'Expires' =>
    "#{1.year.from_now.to_formatted_s(:rfc822)}"

    }
    Public Asset Header

    View full-size slide

  40. 1.Page Caching
    2.Action Caching
    3.Fragment Caching
    4.Russian Doll Caching
    5.Low-Level Caching
    6.SQL Caching
    http://edgeguides.rubyonrails.org/
    caching_with_rails.html
    Caching

    View full-size slide

  41. # index.html.erb

    <%= render partial: 'todo', collection: @todos %>


    # _todo.html.erb

    <% cache todo do %>

    <%= todo.name %>

    <% end %>

    Partial rendering from cache

    View full-size slide

  42. # index.html.erb

    <%= render partial: 'todo', collection: @todos,
    cached: true %>


    # _todo.html.erb

    <% cache todo do %>

    <%= todo.name %>

    <% end %>
    multi_fetch_fragments

    View full-size slide

  43. cached: true => read_multi
    Rendered collection of todos/_todo.html.erb [100 /
    100 cache hits] (339.5ms)
    multi_fetch_fragments

    View full-size slide

  44. 72%
    read_multi

    View full-size slide

  45. config.action_mailer.perform_caching = true
    ActionMailer Fragment Caching

    View full-size slide



  46. <% cache 'signup-text' do %>

    Welcome to <%= @company.name %>

    You have successfully signed up to <%=
    @company.name %>, Your username is:

    <% end %>


    <%= @user.login %>.





    <%= render :partial => 'footer' %>



    ActionMailer Fragment Caching

    View full-size slide

  47. Cache digest for app/views/user_mailer/_footer.erb:
    7313427d26cc1f701b1e0212498cee38

    Cache digest for app/views/user_mailer/welcome_email.html.erb:
    30efff0173fd5f29a88ffe79a9eab617

    Rendered user_mailer/_footer.erb (0.3ms)

    Rendered user_mailer/welcome_email.html.erb (26.1ms)

    Cache digest for app/views/user_mailer/welcome_email.text.erb:
    77f41fe6159c5736ab2026a44bc8de55

    Rendered user_mailer/welcome_email.text.erb (0.2ms)

    UserMailer#welcome_email: processed outbound mail in 190.3ms
    ActionMailer Fragment Caching

    View full-size slide

  48. ActiveRecord::Relation#cache_key
    Caching result sets and collection

    View full-size slide

  49. @users = User.where(city: 'miami')
    1.The query statement doesn’t change. If we change
    city name from “Miami” to “Boston” then result might
    change.
    2.No record is deleted.
    3.No record is added.
    Caching result sets and collection

    View full-size slide

  50. @users = User.where(city: 'Miami')

    @users.cache_key

    => "users/query-67ed32b36805c4b1ec1948b4eef8d58f-3-20160116111659084027"
    # users.html.erb
    cache @users do 

    #content here

    end
    Caching result sets and collection

    View full-size slide

  51. *Layouts and Tags.

    View full-size slide

  52. async-defer
    www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html

    View full-size slide

  53. simple <br/>

    View full-size slide

  54. 1.Placement of JS/CSS imports
    2.Font imports
    3.Analytics tags
    4.Proper viewports
    5.responsive
    6.Inlining resources, css/js, etc
    PageSpeed

    View full-size slide






  55. <%= @page_title || default_page_title %> | BigBinary


    <%= stylesheet_link_tag "application.css", 'data-turbolinks-
    track' => true %>

    …

    <%= csrf_meta_tags %>



    <%= yield %>


    try{Typekit.load({ async: true });}catch(e){}


    <%= javascript_include_tag "application.js", media: 'all',
    'data-turbolinks-track' => true, async: true, defer: true %>




    View full-size slide

  56. Images: flickr.com/search/?
    text=singapore&license=2%2C3%2C4%2C5%2C6%2C9
    http://blog.bigbinary.com/categories/Rails%205
    https://www.packtpub.com/web-development/reactjs-example-building-
    modern-web-applications-react
    http://www.growingwiththeweb.com/2014/02/async-vs-defer-
    attributes.html
    https://speakerdeck.com/rafaelfranca/how-sprockets-works
    https://thenounproject.com/search/?q=sprocket&i=283880
    References

    View full-size slide