Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

The Lifecycle of a Rails Response

The Lifecycle of a Rails Response

This breathtaking documentary series combines rare action, unimaginable scale, impossible locations and intimate moments captured from the deepest depths of Rails internals. Last year, we followed the lifecycle of best loved, wildest and most elusive Rails components through a request, from the browser to a controller action. This year, our journey takes us across the great text/plain, following the flow of HTTP streams, taking in the spectacular Action View as we find our way back to the browser. Join us to unearth the amazing lifecycle of a Rails response.

Krystan HuffMenne

May 04, 2020
Tweet

More Decks by Krystan HuffMenne

Other Decks in Programming

Transcript

  1. https://skylight.io/r/railsconf Don’t struggle to learn why your app is slow.

    Get answers with Skylight. DO THE ANSWER DANCE. SKYLIGHT
  2. app/controllers/hello_controller.rb Terminal $ Trying 34.194.84.73... Connected to ec2-34-194-84-73.compute-1.amazonaws.com. Escape character

    is ‘^]’. > telnet 34.194.84.73 80 Picture: Masai Mara Wildlife / Matt Scobel / CC BY 3.0
  3. app/controllers/hello_controller.rb class SafariController < ActionController::Base def hello # ...do stuff

    here... render plain: 'Hello World' end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb Terminal $ Trying 34.194.84.73... Connected to ec2-34-194-84-73.compute-1.amazonaws.com. Escape character is ‘^]’. > telnet 34.194.84.73 80 GET /safari HTTP/1.1 > Host: skylight.io > Picture: Masai Mara Wildlife / Matt Scobel / CC BY 3.0
  4. app/controllers/hello_controller.rb Terminal $ Trying 34.194.84.73... Connected to ec2-34-194-84-73.compute-1.amazonaws.com. Escape character

    is ‘^]’. > telnet 34.194.84.73 80 GET /safari HTTP/1.1 > Host: skylight.io > < HTTP/1.1 200 OK < Content-Type: text/plain < Content-Length: 11 < Date: Thu, 25 Apr 2019 18:52:54 GMT < < Hello World app/controllers/hello_controller.rb class SafariController < ActionController::Base def hello # ...do stuff here... render plain: 'Hello World' end end app/controllers/safari_controller.rb Picture: Three Elephants in Early Morning / Yu Miyawaki / CC BY 3.0
  5. Sounds good. It’s a GET request for /safari, Host header

    is skylight.io 200 OK. Content type is text/plain, body is “Roar Savanna”. Hey, I have a request for you to handle.
  6. app/controllers/hello_controller.rb http://skylight.io/safari app/controllers/hello_controller.rb Terminal $ Trying 34.194.84.73... Connected to ec2-34-194-84-73.compute-1.amazonaws.com.

    Escape character is ‘^]’. > telnet 34.194.84.73 80 < HTTP/1.1 200 OK < Content-Type: text/plain < Content-Length: 12 < Date: Thu, 25 Apr 2019 18:52:54 GMT < < Roar Savanna GET /safari HTTP/1.1 > Host: skylight.io > Roar Savanna
  7. app/controllers/hello_controller.rb http://skylight.io/safari app/controllers/hello_controller.rb env = { 'REQUEST_METHOD' => 'GET', 'PATH_INFO'

    => ‘/safari', 'HTTP_HOST' => 'skylight.io', # ... } app.call(env) server.rb handwave handwave Check out last year's talk for more info!
  8. app/controllers/hello_controller.rb http://skylight.io/safari app/controllers/hello_controller.rb env = { 'REQUEST_METHOD' => 'GET', 'PATH_INFO'

    => ‘/safari', 'HTTP_HOST' => 'skylight.io', # ... } app.call(env) server.rb app/controllers/hello_controller.rb class SafariController < ActionController::Base def hello # Get it? A savanna is a type of plain... render plain: 'Roar Savanna' end end app/controllers/safari_controller.rb
  9. app/controllers/hello_controller.rb http://skylight.io/safari app/controllers/hello_controller.rb env = { 'REQUEST_METHOD' => 'GET', 'PATH_INFO'

    => ‘/safari', 'HTTP_HOST' => 'skylight.io', # ... } status, headers, body = app.call(env) status # => 200 headers # => { 'Content-Type' => 'text/plain' } body # => [‘Roar Savanna’] server.rb 1 2 3 The Response Array
  10. app/controllers/hello_controller.rb http://skylight.io/safari Roar Savanna app/controllers/hello_controller.rb < HTTP/1.1 200 OK <

    Content-Type: text/plain < Content-Length: 12 < Date: Thu, 25 Apr 2019 18:52:54 GMT < < Roar Savanna
  11. app/controllers/hello_controller.rb http://skylight.io/safari Roar Savanna app/controllers/hello_controller.rb < HTTP/1.1 200 OK <

    Content-Type: text/plain < Content-Length: 12 < Date: Thu, 25 Apr 2019 18:52:54 GMT < < Roar Savanna
  12. app/controllers/hello_controller.rb env = { 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => ‘/safari',

    'HTTP_HOST' => 'skylight.io', # ... } status, headers, body = app.call(env) status # => 200 headers # => { 'Content-Type' => 'text/plain' } body # => [‘Roar Savanna’] server.rb 1 The Response Array
  13. 1xx — 2xx — 3xx — 4xx — 5xx —

    STATUS CODES Informational Success!! Redirection Client Error Server Error
  14. Great, I can render that. Here’s a GET request for

    /safari, Host header is skylight.io 200 OK; Roar Savanna
  15. Should I index the page at /safari ? 500 Internal

    Server Error OK, I’ll be back later! … How about now? 200 OK; Roar Savanna Great! I’ve indexed it.
  16. 1xx — 2xx — 3xx — 4xx — 5xx —

    STATUS CODES Informational Success!! Redirection Client Error Server Error PRO-TIP! Learn a lot more about status codes by going to httpstatuses.com
  17. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } before_action { redirect_to oasis_url if dry_season? } def find_hippo render :hippo end end app/controllers/safari_controller.rb
  18. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } before_action { redirect_to oasis_url if dry_season? } def find_hippo render :hippo end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 302 Found < Location: https://www.skylight.io/oasis < Content-Type: text/plain < Date: Thu, 25 Apr 2019 18:52:54 GMT
  19. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo redirect_to oasis_url, status: :moved_permanently end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 301 Moved Permanently < Location: https://www.skylight.io/oasis < Content-Type: text/plain < Date: Thu, 25 Apr 2019 18:52:54 GMT
  20. app/controllers/hello_controller.rb Rails.application.routes.draw do get '/find-hippo', to: redirect('/oasis') end config/routes.rb app/controllers/hello_controller.rb

    < HTTP/1.1 301 Moved Permanently < Location: https://www.skylight.io/oasis < Content-Type: text/plain < Date: Thu, 25 Apr 2019 18:52:54 GMT
  21. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo redirect_to oasis_url if dry_season? render :hippo end end app/controllers/safari_controller.rb
  22. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo redirect_to oasis_url if dry_season? render :hippo end end app/controllers/safari_controller.rb http://skylight.io/find-hippo
  23. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } before_action { redirect_to oasis_url if dry_season? } def find_hippo render :hippo end end app/controllers/safari_controller.rb
  24. Here’s a GET request for /safari, Host header is skylight.io

    200 OK; Roar Savanna Great, I can render that. Wait…I need more information.
  25. Here’s a GET request for /safari, Host header is skylight.io

    200 OK; Roar Savanna Great, I can render that. Wait…I need more information. Oh yeah. It’s a plain text response. Oh! OK!
  26. app/controllers/hello_controller.rb env = { 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => ‘/safari',

    'HTTP_HOST' => 'skylight.io', # ... } status, headers, body = app.call(env) status # => 200 headers # => { 'Content-Type' => 'text/plain' } body # => [‘Roar Savanna’] server.rb 1 2 The Response Array
  27. app/controllers/hello_controller.rb < HTTP/1.1 302 Found < Content-Type: text/plain < Location:

    https://www.skylight.io/oasis < Date: Thu, 25 Apr 2019 18:52:54 GMT
  28. app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/plain < Content-Length:

    12 < Set-Cookie: _safari_session=some-token; < Date: Thu, 25 Apr 2019 18:52:54 GMT < < Roar Savanna
  29. app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/plain < Content-Length:

    12 < Set-Cookie: _safari_session=some-token; < Date: Thu, 25 Apr 2019 18:52:54 GMT < < Roar Savanna
  30. app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/plain < Content-Length:

    12 < Set-Cookie: _safari_session=some-token; < Date: Thu, 25 Apr 2019 18:52:54 GMT < < Roar Savanna
  31. app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/plain < Content-Length:

    12 < Set-Cookie: _safari_session=some-token; < Date: Thu, 25 Apr 2019 18:52:54 GMT < < Roar Savanna
  32. Here’s a GET request for /safari, Host header is skylight.io

    200 OK; Roar Savanna Here’s a GET request for /safari, Host header is skylight.io Just use the last 200 Nice! Thanks!
  33. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo render :hippo end end app/controllers/safari_controller.rb PRO-TIP! Turn on caching in development by running rails dev:cache
  34. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo http_cache_forever { render :hippo } end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=3155695200, private < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  35. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo http_cache_forever { render :hippo } end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=3155695200, private < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  36. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo http_cache_forever(public: true) { render :hippo } end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=3155695200, public < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  37. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo http_cache_forever { render :hippo } end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=3155695200, private < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  38. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo expires_in 1.hour render :hippo end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=3600, private < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  39. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo expires_in 1.hour render :hippo end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=3600, private < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  40. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo render :hippo end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=0, private, must-revalidate < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  41. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo render :hippo end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=0, private, must-revalidate < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  42. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo render :hippo end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb <!DOCTYPE html> <html> <head> <title>Safari</title> <link rel="stylesheet" media=“all" href="/assets/application-gobbledygook.css" /> <script src="/packs/js/application-gobbledygook.js"></script> </head> <body>
  43. app/controllers/hello_controller.rb # a simplified Rack::ETag module Rack class ETag def

    initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if status == 200 digest = digest_body(body) headers[Etag] = %(W/"#{digest}") end [status, headers, body] end private #... end end rack/lib/rack/etag.rb
  44. app/controllers/hello_controller.rb # a simplified Rack::ETag module Rack class ETag def

    initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if status == 200 digest = digest_body(body) headers[Etag] = %(W/"#{digest}") end [status, headers, body] end private #... end end rack/lib/rack/etag.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=0, private, must-revalidate < ETag: W/“48a7e47309e0ec54e32df3a272094025" < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  45. app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control:

    max-age=0, private, must-revalidate < ETag: W/“48a7e47309e0ec54e32df3a272094025" < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html> app/controllers/hello_controller.rb > GET /find-hippo HTTP/1.1 > Host: skylight.io > If-None-Match: W/"48a7e47309e0ec54e32df3a272094025"
  46. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo render :hippo end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb <!DOCTYPE html> <html> <head> <title>Safari</title> <link rel="stylesheet" media=“all" href="/assets/application-gobbledygook.css" /> <script src="/packs/js/application-gobbledygook.js"></script> </head> <body>
  47. app/controllers/hello_controller.rb # a simplified Rack::ETag module Rack class ETag def

    initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if status == 200 digest = digest_body(body) headers[Etag] = %(W/"#{digest}") end [status, headers, body] end private #... end end rack/lib/rack/etag.rb
  48. app/controllers/hello_controller.rb # a simplified Rack::ETag module Rack class ETag def

    initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if status == 200 digest = digest_body(body) headers[Etag] = %(W/"#{digest}") end [status, headers, body] end private #... end end rack/lib/rack/etag.rb
  49. app/controllers/hello_controller.rb # a simplified Rack::ETag module Rack class ETag def

    initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if status == 200 digest = digest_body(body) headers[Etag] = %(W/"#{digest}") end [status, headers, body] end private #... end end rack/lib/rack/etag.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=0, private, must-revalidate < ETag: W/“48a7e47309e0ec54e32df3a272094025" < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  50. app/controllers/hello_controller.rb # a simplified Rack::ConditionalGet module Rack class ConditionalGet def

    initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if status == 200 && etag_matches? status = 304 body = [] end [status, headers, body] end private def etag_matches? headers['ETag'] == env['HTTP_IF_NONE_MATCH'] end end end rack/lib/rack/conditional_get.rb
  51. app/controllers/hello_controller.rb # a simplified Rack::ConditionalGet module Rack class ConditionalGet def

    initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if status == 200 && etag_matches? status = 304 body = [] end [status, headers, body] end private def etag_matches? headers['ETag'] == env['HTTP_IF_NONE_MATCH'] end end end rack/lib/rack/conditional_get.rb app/controllers/hello_controller.rb < HTTP/1.1 304 Not Modified < Cache-Control: max-age=0, private, must-revalidate < ETag: W/“48a7e47309e0ec54e32df3a272094025” < Content-Type: text/html < Date: Thu, 25 Apr 2019 18:52:54 GMT <
  52. app/controllers/hello_controller.rb # a simplified Rack::ConditionalGet module Rack class ConditionalGet def

    initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if status == 200 && etag_matches? status = 304 body = [] end [status, headers, body] end private def etag_matches? headers['ETag'] == env['HTTP_IF_NONE_MATCH'] end end end rack/lib/rack/conditional_get.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Cache-Control: max-age=0, private, must-revalidate < ETag: W/“48a7e47309e0ec54e32df3a272094025” < Content-Type: text/html < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  53. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo render :hippo if stale?(@hippo) end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=0, private, must-revalidate < ETag: W/“48a7e47309e0ec54e32df3a272094025" < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html> app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=0, private, must-revalidate < ETag: W/"60bee75b8031cda7761a332685151766" < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  54. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo render :hippo if stale?(@hippo) end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: max-age=0, private, must-revalidate < ETag: W/"60bee75b8031cda7761a332685151766" < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  55. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo response.headers["Cache-Control"] = "no-store" render :hippo end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: no-store < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  56. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo response.headers["Cache-Control"] = "no-store" render :hippo end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Cache-Control: no-store < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html> no-store ≠ no-cache
  57. app/controllers/hello_controller.rb env = { 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => ‘/safari',

    'HTTP_HOST' => 'skylight.io', # ... } status, headers, body = app.call(env) status # => 200 headers # => { 'Content-Type' => 'text/plain' } body # => [‘Roar Savanna’] server.rb 1 2 3 The Response Array
  58. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo render :hippo end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb <!DOCTYPE html> <html> <head> <title>Safari</title> <link rel="stylesheet" media=“all" href="/assets/application-gobbledygook.css" /> <script src="/packs/js/application-gobbledygook.js"></script> </head> <body>
  59. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo respond_to do |format| format.html { render :hippo } format.json { render json: @hippo } end end end app/controllers/safari_controller.rb
  60. http://skylight.io/find-hippo { "id": 1, "name": "Jason", "created_at": "2020-04-10T17:14:20.771Z", "updated_at": "2020-04-10T17:14:20.771Z"

    } app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Cache-Control: max-age=0, private, must-revalidate < Date: Thu, 25 Apr 2019 18:52:54 GMT < Content-Type: application/json < < {"id": 1, "name": “Jason", … }
  61. app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < X-Content-Type-Options:

    nosniff < Cache-Control: max-age=0, private, must-revalidate < Date: Thu, 25 Apr 2019 18:52:54 GMT < < <html>…</html>
  62. app/controllers/hello_controller.rb class SafariController < ApplicationController before_action { @hippo = Hippo.first

    } def find_hippo render :hippo end end app/controllers/safari_controller.rb app/controllers/hello_controller.rb Hey <%= current_user.name %>, meet <%= link_to @hippo.name, @hippo %>! app/views/safari/hippo.html.erb
  63. app/controllers/hello_controller.rb class SafariControllerViewContext < ActionView::Base include Rails::AllTheHelpers # link_to, etc.

    include MyApp::AllTheHelpers # current_user, etc. def initialize(assigns) assigns.each { |k, v| instance_variable_set("@#{k}", v) } end private # Hey <%= current_user.name %>, meet # <%= link_to @hippo.name, @hippo %>! def __compiled_app_templates_hippo_erb output = "" output << "Hey " output << html_escape(current_user.name) output << ", meet" output << link_to(html_escape(@hippo.name), @hippo) output << "!" output end end NOTE: To see the actual code, look in ActionView!::Rendering, ActionView!::Base, ActionView!::Renderer, ActionView!::TemplateRenderer, ActionView!::Template
  64. app/controllers/hello_controller.rb class SafariControllerViewContext < ActionView::Base include Rails::AllTheHelpers # link_to, etc.

    include MyApp::AllTheHelpers # current_user, etc. def initialize(assigns) assigns.each { |k, v| instance_variable_set("@#{k}", v) } end private # Hey <%= current_user.name %>, meet # <%= link_to @hippo.name, @hippo %>! def __compiled_app_templates_hippo_erb output = "" output << "Hey " output << html_escape(current_user.name) output << ", meet" output << link_to(html_escape(@hippo.name), @hippo) output << "!" output end end
  65. app/controllers/hello_controller.rb class SafariControllerViewContext < ActionView::Base include Rails::AllTheHelpers # link_to, etc.

    include MyApp::AllTheHelpers # current_user, etc. def initialize(assigns) assigns.each { |k, v| instance_variable_set("@#{k}", v) } end private # Hey <%= current_user.name %>, meet # <%= link_to @hippo.name, @hippo %>! def __compiled_app_templates_hippo_erb output = "" output << "Hey " output << html_escape(current_user.name) output << ", meet" output << link_to(html_escape(@hippo.name), @hippo) output << "!" output end end
  66. app/controllers/hello_controller.rb class SafariControllerViewContext < ActionView::Base include Rails::AllTheHelpers # link_to, etc.

    include MyApp::AllTheHelpers # current_user, etc. def initialize(assigns) assigns.each { |k, v| instance_variable_set("@#{k}", v) } end private # Hey <%= current_user.name %>, meet # <%= link_to @hippo.name, @hippo %>! def __compiled_app_templates_hippo_erb output = "" output << "Hey " output << html_escape(current_user.name) output << ", meet" output << link_to(html_escape(@hippo.name), @hippo) output << "!" output end end
  67. app/controllers/hello_controller.rb class SafariControllerViewContext < ActionView::Base include Rails::AllTheHelpers # link_to, etc.

    include MyApp::AllTheHelpers # current_user, etc. def initialize(assigns) assigns.each { |k, v| instance_variable_set("@#{k}", v) } end private # Hey <%= current_user.name %>, meet # <%= link_to @hippo.name, @hippo %>! def __compiled_app_templates_hippo_erb output = "" output << "Hey " output << html_escape(current_user.name) output << ", meet" output << link_to(html_escape(@hippo.name), @hippo) output << "!" output end end
  68. app/controllers/hello_controller.rb class SafariControllerViewContext < ActionView::Base include Rails::AllTheHelpers # link_to, etc.

    include MyApp::AllTheHelpers # current_user, etc. def initialize(assigns) assigns.each { |k, v| instance_variable_set("@#{k}", v) } end private # Hey <%= current_user.name %>, meet # <%= link_to @hippo.name, @hippo %>! def __compiled_app_templates_hippo_erb output = "" output << "Hey " output << html_escape(current_user.name) output << ", meet" output << link_to(html_escape(@hippo.name), @hippo) output << "!" output end end
  69. app/controllers/hello_controller.rb class SafariControllerViewContext < ActionView::Base include Rails::AllTheHelpers # link_to, etc.

    include MyApp::AllTheHelpers # current_user, etc. def initialize(assigns) assigns.each { |k, v| instance_variable_set("@#{k}", v) } end private # Hey <%= current_user.name %>, meet # <%= link_to @hippo.name, @hippo %>! def __compiled_app_templates_hippo_erb output = "" output << "Hey " output << html_escape(current_user.name) output << ", meet" output << link_to(html_escape(@hippo.name), @hippo) output << "!" output end end
  70. app/controllers/hello_controller.rb class SafariControllerViewContext < ActionView::Base include Rails::AllTheHelpers # link_to, etc.

    include MyApp::AllTheHelpers # current_user, etc. def initialize(assigns) assigns.each { |k, v| instance_variable_set("@#{k}", v) } end private # Hey <%= current_user.name %>, meet # <%= link_to @hippo.name, @hippo %>! def __compiled_app_templates_hippo_erb output = "" output << "Hey " output << html_escape(current_user.name) output << ", meet" output << link_to(html_escape(@hippo.name), @hippo) output << "!" output end end
  71. app/controllers/hello_controller.rb class SafariControllerViewContext < ActionView::Base include Rails::AllTheHelpers # link_to, etc.

    include MyApp::AllTheHelpers # current_user, etc. def initialize(assigns) assigns.each { |k, v| instance_variable_set("@#{k}", v) } end private # Hey <%= current_user.name %>, meet # <%= link_to @hippo.name, @hippo %>! def __compiled_app_templates_hippo_erb output = "" output << "Hey " output << html_escape(current_user.name) output << ", meet" output << link_to(html_escape(@hippo.name), @hippo) output << "!" output end end
  72. app/controllers/hello_controller.rb class SafariControllerViewContext < ActionView::Base include Rails::AllTheHelpers # link_to, etc.

    include MyApp::AllTheHelpers # current_user, etc. def initialize(assigns) assigns.each { |k, v| instance_variable_set("@#{k}", v) } end private # Hey <%= current_user.name %>, meet # <%= link_to @hippo.name, @hippo %>! def __compiled_app_templates_hippo_erb output = "" output << "Hey " output << html_escape(current_user.name) output << ", meet" output << link_to(html_escape(@hippo.name), @hippo) output << "!" output end end
  73. http://skylight.io/find-hippo <!DOCTYPE html> <html> <head> <title>Safari</title> <link rel="stylesheet" media="all" href="/assets/application-gobbledygook.css"

    /> <script src="/packs/js/application-gobbledygook.js"></script> </head> <body> Hey RailsConf, meet <a href="/hippos/1">Phyllis</a>! </body> </html> Hey RailsConf, meet Phyllis!
  74. http://skylight.io/find-hippo <!DOCTYPE html> <html> <head> <title>Safari</title> <link rel="stylesheet" media="all" href="/assets/application-gobbledygook.css"

    /> <script src="/packs/js/application-gobbledygook.js"></script> </head> <body> Hey RailsConf, meet <a href="/hippos/1">Phyllis</a>! </body> </html> Hey RailsConf, meet Phyllis! app/controllers/hello_controller.rb < HTTP/1.1 200 OK < Content-Type: text/html < Content-Length: 306 < Cache-Control: max-age=0, private, must-revalidate < ETag: W/“60bee75b8031cda7761a332685151766” < Set-Cookie: _safari_session=some-session-token; path=/; < X-Content-Type-Options: nosniff < Date: Thu, 25 Apr 2019 18:52:54 GMT