Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

RailsConf 2022 It's been a minute!

Slide 3

Slide 3 text

PORTLAND

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Couch Glisan

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

3 YEARS!

Slide 12

Slide 12 text

#FridayHug

Slide 13

Slide 13 text

πŸ‡ΊπŸ‡¦πŸ‡ΊπŸ‡¦πŸ‡ΊπŸ‡¦

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

RIP Little Buddy 😭

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

~5 Photos / Day 🀣

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

https://www.youtube.com/ TenderlovesCoolStuff Subscribe

Slide 26

Slide 26 text

Tenderlove's Cool Stuff

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

Λ‘Λ‘Λ‘Λ‘Λ‘ 0 Stars: Do Not Recommend

Slide 29

Slide 29 text

πŸ’¨

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

JIT: Generate Machine Code at Runtime

Slide 32

Slide 32 text

Machine Code: Sequence of Bytes

Slide 33

Slide 33 text

Ruby: Can Make a Sequence of Bytes βœ…

Slide 34

Slide 34 text

πŸ€” Pure Ruby JIT? πŸ€”

Slide 35

Slide 35 text

Yes! Yes, we can.

Slide 36

Slide 36 text

https://github.com/tenderlove/tenderjit

Slide 37

Slide 37 text

TenderJIT

Slide 38

Slide 38 text

TenderJIT

Slide 39

Slide 39 text

"Just because you can, doesn't mean you should!"

Slide 40

Slide 40 text

Analog Terminal Bell https://analogterminalbell.com

Slide 41

Slide 41 text

Professional Software Development

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

Apple M1: AArch64

Slide 44

Slide 44 text

x86_64 Only

Slide 45

Slide 45 text

LeJIT JITs

Slide 46

Slide 46 text

AArch64 Gem https://github.com/tenderlove/aarch64

Slide 47

Slide 47 text

AArch64 Example JIT a function that returns 0xF00DCAFE $ gel exec ruby test.rb "f00dcafe" Terminal Output require "aarch64" require "jit_buffer" # create a JIT buffer jit_buffer = JITBuffer.new 4096 asm = AArch64::Assembler.new # Make some instructions asm.pretty do asm.movz x0, 0xCAFE asm.movk x0, 0xF00D, lsl(16) asm.ret end # Write the instructions to a JIT buffer jit_buffer.writeable! asm.write_to jit_buffer jit_buffer.executable! # Execute the JIT buffer p jit_buffer.to_function([], -Fiddle::TYPE_INT).call.to_s(16) # => f00dcafe JIT Code

Slide 48

Slide 48 text

AArch64 Example JIT a function that returns 0xF00DCAFE $ gel exec ruby test.rb "f00dcafe" Terminal Output asm = AArch64::Assembler.new # Make some instructions asm.pretty do asm.movz x0, 0xCAFE asm.movk x0, 0xF00D, lsl(16) asm.ret end JIT Code

Slide 49

Slide 49 text

Not Amateur Enough

Slide 50

Slide 50 text

Extremely Amateur

Slide 51

Slide 51 text

AArch64 Example JIT a function that returns 0xF00DCAFE $ gel exec ruby test.rb "f00dcafe" Terminal Output require "aarch64" require "jit_buffer" # create a JIT buffer jit_buffer = JITBuffer.new 4096 asm = AArch64::Assembler.new # Make some instructions asm.pretty do asm.movz x0, 0xCAFE asm.movk x0, 0xF00D, lsl(16) asm.ret end # Write the instructions to a JIT buffer jit_buffer.writeable! asm.write_to jit_buffer jit_buffer.executable! # Execute the JIT buffer p jit_buffer.to_function([], -Fiddle::TYPE_INT).call.to_s(16) # => f00dcafe JIT Code

Slide 52

Slide 52 text

AArch64 Example JIT a function that returns 0xF00DCAFE $ gel exec ruby test.rb "f00dcafe" Terminal Output require "aarch64" require "jit_buffer" # create a JIT buffer jit_buffer = JITBuffer.new 4096 asm = AArch64::Assembler.new # Make some instructions asm.pretty do asm.movz x0, 0xCAFE asm.movk x0, 0xF00D, lsl(16) asm.ret end # Write the instructions to a JIT buffer jit_buffer.writeable! asm.write_to jit_buffer jit_buffer.executable! # Execute the JIT buffer p jit_buffer.to_function([], -Fiddle::TYPE_INT).call.to_s(16) # => f00dcafe JIT Code JITBuffer.new

Slide 53

Slide 53 text

JITBuffer: Executable Memory

Slide 54

Slide 54 text

Linux / macOS

Slide 55

Slide 55 text

Any Platform

Slide 56

Slide 56 text

Test Code Execution Write some bytes and ensure it can execute them # create a JIT buffer jit_buffer = JITBuffer.new 4096 # Write the instructions to a JIT buffer jit_buffer.writeable! jit_buffer.write asm.to_binary jit_buffer.executable! # Execute the JIT buffer p jit_buffer.to_function([], -Fiddle::TYPE_INT).call

Slide 57

Slide 57 text

Test on x86_64 and ARM

Slide 58

Slide 58 text

Test Code Execution Add a check for the current platform # create a JIT buffer jit_buffer = JITBuffer.new 4096 # Write the instructions to a JIT buffer jit_buffer.writeable! if arm64? jit_buffer.write arm64_bytes else jit_buffer.write x86_bytes end jit_buffer.executable!

Slide 59

Slide 59 text

Test Code Execution Add a check for the current platform # create a JIT buffer jit_buffer = JITBuffer.new 4096 # Write the instructions to a JIT buffer jit_buffer.writeable! if arm64? jit_buffer.write arm64_bytes else jit_buffer.write x86_bytes end jit_buffer.executable!

Slide 60

Slide 60 text

If Statements Mean "Complexity"

Slide 61

Slide 61 text

Not "Cool"

Slide 62

Slide 62 text

Can we ask the CPU itself?

Slide 63

Slide 63 text

Machine Code is Just Bytes

Slide 64

Slide 64 text

Bytes Valid for x86_64 and ARM64?

Slide 65

Slide 65 text

YES!

Slide 66

Slide 66 text

Worst Best Idea Ever!

Slide 67

Slide 67 text

x86 Machine Code Very Cool Code (Intel Syntax) .foo: mov rax, 0x2b ret jmp foo Put 0x2b in the "RAX" register Return from the current function Jump to the "foo" label???

Slide 68

Slide 68 text

x86 Machine Code Bytes Bytes are in base 16 48 C7 C0 2B 00 00 00 mov rax, 0x2b C3 ret EB F6 jmp foo [0x48, 0xc7, 0xc0, 0x2b, 0x00, 0x00, 0x00, 0xc3, 0xeb, 0xf6]

Slide 69

Slide 69 text

ARM64 Machine Code Very Very Cool Code movz X11, 0x7b7 movz X0, 0x2b ret Write 0x7b7 to X11 register because??? Write 0x2b to the X0 register Return from the current function

Slide 70

Slide 70 text

ARM64 Machine Code Bytes Bytes are in base 16 EB F6 80 D2 movz x11, 0x7b7 60 05 80 D2 movz x0, 0x2b C0 03 5F D6 ret

Slide 71

Slide 71 text

Machine Code for x86 and ARM64 x86_64 Machine Code 48 C7 C0 2B 00 00 00 mov rax, 0x2b C3 ret EB F6 jmp foo ARM64 Machine Code EB F6 80 D2 movz x11, 0x7b7 60 05 80 D2 movz x0, 0x2b C0 03 5F D6 ret

Slide 72

Slide 72 text

Machine Code for x86 and ARM64 x86_64 Machine Code 48 C7 C0 2B 00 00 00 mov rax, 0x2b C3 ret EB F6 jmp foo ARM64 Machine Code EB F6 80 D2 movz x11, 0x7b7 60 05 80 D2 movz x0, 0x2b C0 03 5F D6 ret

Slide 73

Slide 73 text

Machine Code for x86 and ARM64 48 C7 C0 2B 00 00 00 mov rax, 0x2b C3 ret EB F6 jmp foo EB F6 80 D2 movz x11, 0x7b7 60 05 80 D2 movz x0, 0x2b C0 03 5F D6 ret

Slide 74

Slide 74 text

Machine Code for x86 and ARM64 48 C7 C0 2B 00 00 00 mov rax, 0x2b C3 ret EB F6 jmp foo EB F6 80 D2 60 05 80 D2 movz x0, 0x2b C0 03 5F D6 ret

Slide 75

Slide 75 text

Machine Code for x86 and ARM64 48 C7 C0 2B 00 00 00 mov rax, 0x2b C3 ret EB F6 EB F6 80 D2 movz x11, 0x7b7 60 05 80 D2 movz x0, 0x2b C0 03 5F D6 ret

Slide 76

Slide 76 text

Both Return 0x2B

Slide 77

Slide 77 text

JITBuffer Test Case VCP (Very Cool Program) jit = JITBuffer.new 4096 bytes = [0x48, 0xc7, 0xc0, 0x2b, 0x00, 0x00, 0x00, # x86_64 mov rax, 0x2b 0xc3, # x86_64 ret 0xeb, 0xf6, # x86 jmp 0x80, 0xd2, # ARM movz X11, 0x7b7 0x60, 0x05, 0x80, 0xd2, # ARM movz X0, #0x2b 0xc0, 0x03, 0x5f, 0xd6] # ARM ret jit.writeable! jit.write bytes.pack("C*") jit.executable! func = Fiddle::Function.new(jit.to_i + 8, [], Fiddle::TYPE_INT) assert_equal 0x2b, func.call Start at offset 8 Offset 8 Alw ays 0x2b!!

Slide 78

Slide 78 text

Cross Platform!!!!!* *x86_64 and ARM64 only

Slide 79

Slide 79 text

No If Statements!

Slide 80

Slide 80 text

Complexity: 0

Slide 81

Slide 81 text

So Simple!

Slide 82

Slide 82 text

"Just because you can, doesn't mean you should!"

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

Anatomy of a Tenderlove Talk

Slide 85

Slide 85 text

Tenderlove Talk Timeline Teardown Jokes about local businesses Pictures of Cats W eird code not related to the topic M ain topic Abrupt ending after running out of tim e

Slide 86

Slide 86 text

Talk Timeline Breakdown Jokes about local businesses Pictures of Cats W eird code not related to the topic M ain topic Abrupt ending after running out of tim e

Slide 87

Slide 87 text

New Features in Rails 7!! Rack

Slide 88

Slide 88 text

Spring It is gone!

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

New Features in Rails 7 Cool! β€’ Import maps β€’ Hotwire Default β€’ Turbo 7 Default β€’ Stimulus 3 Default β€’ JS Bundling β€’ CSS Bundling

Slide 91

Slide 91 text

No content

Slide 92

Slide 92 text

I am not a Front End Developer

Slide 93

Slide 93 text

No Idea what these words mean

Slide 94

Slide 94 text

But It's OK

Slide 95

Slide 95 text

Import Maps and ES Modules

Slide 96

Slide 96 text

Sprockets Webpacker ESM + Import Maps

Slide 97

Slide 97 text

🫠🫠🫠🫠 "yay"

Slide 98

Slide 98 text

New application.html.erb javascript_importmap_tags Myapp <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %> <%= yield %>

Slide 99

Slide 99 text

Define importmaps con fi g/importmap.rb # Pin npm packages by running ./bin/importmap pin "application", preload: true pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true pin_all_from "app/javascript/controllers", under: "controllers"

Slide 100

Slide 100 text

Rendered Application Layout Import map automatically generated { "imports": { "application": "/assets/application-123.js", "@hotwired/turbo-rails": "/assets/turbo.min-123.js", "@hotwired/stimulus": "/assets/stimulus.min-123.js", "@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js", "controllers/application": "/assets/controllers/application-123.js", "controllers/hello_controller": "/assets/controllers/hello_controller-123.js", "controllers": "/assets/controllers/index-123.js" } }

Slide 101

Slide 101 text

Pinning Libraries

Slide 102

Slide 102 text

Put a pin in it! Add stu ff to the import map $ bin/importmap pin [email protected]/startCase.js Pinning "lodash-es/startCase.js" to https://ga.jspm.io/npm:[email protected]/startCase.js es: EspaΓ±ol

Slide 103

Slide 103 text

New config/importmap.rb With lodash-es # Pin npm packages by running ./bin/importmap pin "application", preload: true pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true pin_all_from "app/javascript/controllers", under: "controllers" pin "lodash-es/startCase.js", to: "https://ga.jspm.io/npm:[email protected]/startCase.js"

Slide 104

Slide 104 text

New config/importmap.rb With lodash-es # Pin npm packages by running ./bin/importmap pin "application", preload: true pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true pin_all_from "app/javascript/controllers", under: "controllers" pin "lodash-es/startCase.js", to: "https://ga.jspm.io/npm:[email protected]/startCase.js"

Slide 105

Slide 105 text

New config/importmap.rb With lodash-es # Pin npm packages by running ./bin/importmap pin "application", preload: true pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true pin_all_from "app/javascript/controllers", under: "controllers" pin "@lodash/startCase", to: "https://ga.jspm.io/npm:[email protected]/startCase.js"

Slide 106

Slide 106 text

New Sourcemap Has Lowdash! { "imports": { "application": "/assets/application-123.js", "@hotwired/turbo-rails": "/assets/turbo.min-123.js", "@hotwired/stimulus": "/assets/stimulus.min-123.js", "@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js", "@lodash/startCase": "https://ga.jspm.io/npm:[email protected]/startCase.js", "controllers/application": "/assets/controllers/application-123.js", "controllers/hello_controller": "/assets/controllers/hello_controller-123.js", "controllers": "/assets/controllers/index-123.js" } }

Slide 107

Slide 107 text

New Sourcemap Has Lowdash! { "imports": { "application": "/assets/application-123.js", "@hotwired/turbo-rails": "/assets/turbo.min-123.js", "@hotwired/stimulus": "/assets/stimulus.min-123.js", "@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js", "@lodash/startCase": "https://ga.jspm.io/npm:[email protected]/startCase.js", "controllers/application": "/assets/controllers/application-123.js", "controllers/hello_controller": "/assets/controllers/hello_controller-123.js", "controllers": "/assets/controllers/index-123.js" } }

Slide 108

Slide 108 text

Use the new module! import startCase from '@lodash/startCase'; const el = document.createElement('h1'); const words = "hello, mom!" const text = document.createTextNode(startCase(words)); el.appendChild(text); document.body.appendChild(el);

Slide 109

Slide 109 text

No content

Slide 110

Slide 110 text

Great!

Slide 111

Slide 111 text

Indirect Names Use names rather than fi les { "imports": { "application": "/assets/application-123.js", "@hotwired/turbo-rails": "/assets/turbo.min-123.js", "@hotwired/stimulus": "/assets/stimulus.min-123.js", "@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js", "controllers/application": "/assets/controllers/application-123.js", "controllers/hello_controller": "/assets/controllers/hello_controller-123.js", "controllers": "/assets/controllers/index-123.js" } }

Slide 112

Slide 112 text

Indirect Names Use names rather than fi les { "imports": { "application": "/assets/application-123.js", "@hotwired/turbo-rails": "/assets/turbo.min-123.js", "@hotwired/stimulus": "/assets/stimulus.min-123.js", "@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js", "controllers/application": "/assets/controllers/application-123.js", "controllers/hello_controller": "/assets/controllers/hello_controller-123.js", "controllers": "/assets/controllers/index-123.js" } }

Slide 113

Slide 113 text

We're Not Bundling JS

Slide 114

Slide 114 text

Better Caching

Slide 115

Slide 115 text

More Requests

Slide 116

Slide 116 text

HTTP2: Concurrent Requests on a Single Socket

Slide 117

Slide 117 text

Rack

Slide 118

Slide 118 text

Rack Defines an Interface Easily change webservers Rack Application Webserver Falcon

Slide 119

Slide 119 text

Rack Interface rack_app = lambda do |env| [200, {}, ["neat"]] end def webserver(app) status, headers, body = app.call({}) $socket.puts status $socket.puts headers body.each do |chunk| $socket.puts chunk end end webserver(rack_app) Rack App Webserver Request Data Response Data

Slide 120

Slide 120 text

Rack + HTTP

Slide 121

Slide 121 text

Rack + TCP Connection

Slide 122

Slide 122 text

HTTP Timeline + First Commit of Rack Starting with HTTP/0.9 1990 1998 2006 2014 2022 Websockets 2011 HTTP/3 2020 HTTP/2 2015 Rack 2007 HTTP/1.1 1997 HTTP/1.0 1996 HTTP/0.9 1991

Slide 123

Slide 123 text

HTTP 0.9 Request / Response Connections were closed Client New TCP Connection Server HTTP Request HTTP Response perl ... ENV rack_app = lambda do |env| [200, {}, ["neat"]] end

Slide 124

Slide 124 text

HTTP/1.0 RFC 1945 1990 1998 2006 2014 2022 Websockets 2011 HTTP/3 2020 HTTP/2 2015 Rack 2007 HTTP/1.1 1997 HTTP/1.0 1996 HTTP/0.9 1991

Slide 125

Slide 125 text

Connections are Expensive

Slide 126

Slide 126 text

Serial Requests

Slide 127

Slide 127 text

Fetch assets in parallel?

HELLO!

Happy Friday!!!

Slide 128

Slide 128 text

HTTP/1.1 RFC 2068 1990 1998 2006 2014 2022 Websockets 2011 HTTP/3 2020 HTTP/2 2015 Rack 2007 HTTP/1.1 1997 HTTP/1.0 1996 HTTP/0.9 1991

Slide 129

Slide 129 text

HTTP/1.1: Keep Alive

Slide 130

Slide 130 text

HTTP 1.1 Request / Response Connections are kept open Client New TCP Connection Server HTTP Request HTTP Response ruby ... ruby ... HTTP Request HTTP Response

Slide 131

Slide 131 text

HTTP/1.1: Have 2 Connections

Slide 132

Slide 132 text

HTTP 1.1 Multiple Connections Thanks buddy! New TCP Connection Server HTTP Request HTTP Response ruby ... ruby ... HTTP Request HTTP Response Client New TCP Connection Server HTTP Request HTTP Response ruby ... ruby ... HTTP Request HTTP Response

Slide 133

Slide 133 text

Requests Are Still Serial

Slide 134

Slide 134 text

Bundling Assets

Slide 135

Slide 135 text

Rack is released! Version 0.1 1990 1998 2006 2014 2022 Websockets 2011 HTTP/3 2020 HTTP/2 2015 Rack 2007 HTTP/1.1 1997 HTTP/1.0 1996 HTTP/0.9 1991

Slide 136

Slide 136 text

Based on PEP 333

Slide 137

Slide 137 text

"HTTP Translation"

Slide 138

Slide 138 text

Environment Variables -> Hash

Slide 139

Slide 139 text

stdout -> [200, {}, ["hi"]]

Slide 140

Slide 140 text

Full Rack Application Example rack_app = lambda do |env| [200, # Status Code { "X-Cool-Header" => "Neat" }, # Headers ["Response Body"]] # Response Body end

Slide 141

Slide 141 text

Websockets Sockets, but for the web! 1990 1998 2006 2014 2022 Websockets 2011 HTTP/3 2020 HTTP/2 2015 Rack 2007 HTTP/1.1 1997 HTTP/1.0 1996 HTTP/0.9 1991

Slide 142

Slide 142 text

Bidirectional Communication

Slide 143

Slide 143 text

Websockets: NOT HTTP

Slide 144

Slide 144 text

2013: Rack 1.5

Slide 145

Slide 145 text

Hijacking: Slap a proc in there! Get access to the socket rack_app = lambda do |env| io = env["rack.hijack"].call io.write "neat" io.close # socket is closed # Return stuff? [200, {}, []] end

Slide 146

Slide 146 text

HTTP Translator + Socket Getter? 🀷

Slide 147

Slide 147 text

HTTP/2 It's 0.9 better than HTTP 1.1 1990 1998 2006 2014 2022 Websockets 2011 HTTP/3 2020 HTTP/2 2015 Rack 2007 HTTP/1.1 1997 HTTP/1.0 1996 HTTP/0.9 1991

Slide 148

Slide 148 text

SSL Encouraged

Slide 149

Slide 149 text

Compressed Headers

Slide 150

Slide 150 text

Binary Protocol

Slide 151

Slide 151 text

Request / Response Multiplexing Concurrent requests on the same socket Client New SSL TCP Connection Server H2 Request 1 H2 Response 2 ruby ... ruby ... H2 Request 2 H2 Response 1

Slide 152

Slide 152 text

Import Maps

Slide 153

Slide 153 text

Rails 7: We want you to use H2?

Slide 154

Slide 154 text

How?

Slide 155

Slide 155 text

https://github.com/socketry/falcon

Slide 156

Slide 156 text

HTTP2 Proxy Server

Slide 157

Slide 157 text

HTTP 2 Proxy Server Proxy translates to HTTP 1.1 H2O Unicorn Rails HTTP 1.1 Rack HTTP 2

Slide 158

Slide 158 text

Push Promise

Slide 159

Slide 159 text

"Take this resource"

Slide 160

Slide 160 text

What if the client already has the resource cached? πŸ˜…

Slide 161

Slide 161 text

Push Promise in HTTP 1.1

Slide 162

Slide 162 text

HTTP Status Code 103: Early Hints

Slide 163

Slide 163 text

Sending Server Push with H2 Proxy HTTP 1.1 server sends 103, proxy converts to PUSH HTTP/1.1 103 Early Hints Link: ; rel=preload; as=style Link: ; rel=preload; as=script HTTP/1.1 200 OK Date: Fri, 26 May 2022 10:02:11 GMT Content-Length: 1234 Content-Type: text/html; charset=utf-8 Link: ; rel=preload; as=style Link: ; rel=preload; as=script HTTP 1.1 Response

Slide 164

Slide 164 text

Sending Server Push with H2 Proxy HTTP 1.1 server sends 103, proxy converts to PUSH HTTP/1.1 103 Early Hints Link: ; rel=preload; as=style Link: ; rel=preload; as=script HTTP 103 Response H2O Unicorn HTTP 1.1 HTTP 2 PUSH /style.css PUSH /script.js

Slide 165

Slide 165 text

Rails + (Puma || Falcon)

Slide 166

Slide 166 text

Slap another lambda in the env hash! Great! class Server def call env env['rack.early_hints'].call([ { link: "/style.css", as: "style" }, # push /style.css { link: "/script.js" }]) # push /script.js [200, { "X-Hello" => "World" }, ["Hello world!"]] end end

Slide 167

Slide 167 text

2020: HTTP/3

Slide 168

Slide 168 text

Rails 7 is HTTP/2 by default

Slide 169

Slide 169 text

Do we need H2 servers?

Slide 170

Slide 170 text

What does H2 mean for Hijack?

Slide 171

Slide 171 text

Slapping lambdas in a hash seems unsustainable

Slide 172

Slide 172 text

Next Generation Rack

Slide 173

Slide 173 text

Easy to Use

Slide 174

Slide 174 text

Easy to Extend

Slide 175

Slide 175 text

Easy to Deploy

Slide 176

Slide 176 text

This Whole Thing Is Moot

Slide 177

Slide 177 text

Ruby 3.1: WASM support

Slide 178

Slide 178 text

https://ruby-syntax-tree.github.io

Slide 179

Slide 179 text

Next Year: Rails in the browser, React on the server

Slide 180

Slide 180 text

THANKS PORTLAND! See you next year! 😘