Slide 1

Slide 1 text

Vladimir Dementyev Evil Martians FROM SERVER TO CLIENT Ruby on Rails on WebAssembly

Slide 2

Slide 2 text

palkan_tula palkan 2 rubyonrails.org

Slide 3

Slide 3 text

palkan_tula palkan 2 rubyonrails.org No Wasm detected !

Slide 4

Slide 4 text

palkan_tula palkan 3 github.com/palkan

Slide 5

Slide 5 text

palkan_tula palkan 3 github.com/palkan Still no Wasm !

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Wasm! Wasm! Wasm! !

Slide 8

Slide 8 text

palkan_tula palkan 5 stackblitz.com

Slide 9

Slide 9 text

palkan_tula palkan 6 runruby.dev

Slide 10

Slide 10 text

palkan_tula palkan 7 ruby-lang.org

Slide 11

Slide 11 text

palkan_tula palkan 8 rubyonrails.org

Slide 12

Slide 12 text

palkan_tula palkan 9 rubyonrails.org

Slide 13

Slide 13 text

palkan_tula palkan Ruby VM Rails Components 10

Slide 14

Slide 14 text

palkan_tula palkan Ruby VM Gems Rails Components 11

Slide 15

Slide 15 text

palkan_tula palkan Ruby VM Gems Rails Components 12 Native extensions

Slide 16

Slide 16 text

palkan_tula palkan Ruby VM Gems Rails Components 12 Native extensions ruby.wasm handles this! !

Slide 17

Slide 17 text

palkan_tula palkan Ruby VM Gems Native extensions System tools Rails Components 13

Slide 18

Slide 18 text

palkan_tula palkan Ruby VM Gems Native extensions System tools Database Rails Components 14

Slide 19

Slide 19 text

palkan_tula palkan Ruby VM Gems Native extensions System tools Database Rails Components 14 Web Server

Slide 20

Slide 20 text

palkan_tula palkan Ruby VM Gems Native extensions System tools Database Rails Components 14 Web Server Storage

Slide 21

Slide 21 text

palkan_tula palkan Ruby VM Gems Native extensions System tools Database Rails Components 14 Web Server Storage Queue

Slide 22

Slide 22 text

palkan_tula palkan Ruby VM Gems Native extensions System tools Database Rails Components 14 Web Server Storage Queue API

Slide 23

Slide 23 text

palkan_tula palkan Ruby VM Gems Native extensions System tools Database Rails Components 14 Web Server Storage Queue API ??? !

Slide 24

Slide 24 text

palkan_tula palkan Framework = Abstractions + Infrastructure 15 Pure code Pluggable dependencies

Slide 25

Slide 25 text

palkan_tula palkan Good Framework = (Abstractions) + (Infrastructure) 16 Pure code Pluggable dependencies

Slide 26

Slide 26 text

palkan_tula palkan Ruby VM Gems Native extensions System tools Web Server Database Queue Storage API Rails Components 17

Slide 27

Slide 27 text

palkan_tula palkan Ruby VM Gems Native extensions System tools Web Server Database Queue Storage bundler active_storage rack active_record active_job API net/http Rails Components 17

Slide 28

Slide 28 text

palkan_tula palkan bundler active_storage rack active_record active_job net/http Rails Components 18 Abstractions Not Abstractions

Slide 29

Slide 29 text

palkan_tula palkan bundler active_storage rack active_record active_job net/http Rails Components 18 Abstractions Not Abstractions Just patch them! "

Slide 30

Slide 30 text

palkan_tula palkan Rails architecture design and Ruby openness make Rails on Wasm possible 19

Slide 31

Slide 31 text

palkan_tula palkan 20 rubyonrails.org

Slide 32

Slide 32 text

palkan_tula palkan 20 rubyonrails.org your browser

Slide 33

Slide 33 text

palkan_tula palkan rails new Demo: ReviewCon 21 Build a new full stack app

Slide 34

Slide 34 text

palkan_tula palkan rails new rails g scaffold ... Demo: ReviewCon 22 Build a new full stack app Scaffold models, controllers, views

Slide 35

Slide 35 text

palkan_tula palkan rails new rails g scaffold ... bolt.new Demo: ReviewCon 23 Ask AI to polish UI # Build a new full stack app Scaffold models, controllers, views

Slide 36

Slide 36 text

palkan_tula palkan wasmify:install Demo: ReviewCon 24 Rails on Wasm toolkit

Slide 37

Slide 37 text

palkan_tula palkan wasmify:install wasmify:pwa wasmify:pack Demo: ReviewCon 25 Rails on Wasm toolkit Generate a boot app (JS) Compile the whole app into app.wasm

Slide 38

Slide 38 text

palkan_tula palkan wasmify:install wasmify:pwa wasmify:pack on_wasm? Demo: ReviewCon 26 Compile the whole app into app.wasm Rails on Wasm toolkit Generate a boot app (JS) Make application Wasm-aware

Slide 39

Slide 39 text

palkan_tula palkan Running an entire Rails application within your browser is reality! 27 $ reviewcon2024.netlify.app

Slide 40

Slide 40 text

palkan_tula palkan Running an entire Rails application within your browser is reality! 27 Tell me more! ! $ reviewcon2024.netlify.app

Slide 41

Slide 41 text

palkan_tula palkan Ruby VM Gems Native extensions System tools Web Server Database Queue Storage bundler active_storage rack active_record active_job API net/http Rails Components 28

Slide 42

Slide 42 text

palkan_tula palkan sqlite.wasm Database 29 Database active_record config/database.yml database.yml default: &default adapter: sqlite3 production: # ... wasm: adapter: sqlite3_wasm development: <<: *default test: <<: *default database: storage/test.sqlite3 1 2 3 4 . 20 21 22 23 24 25 . 28 29 30 31

Slide 43

Slide 43 text

active_record/connection_adapters/sqlite3_wasm_adapter.rb sqlite3_wasm_adapter.rb module ActiveRecord module ConnectionAdapters class SQLite3WasmAdapter < SQLite3Adapter class ExternalInterface # ... def exec(...) JS.global[js_interface].exec(...) end end end end # ... end 1 2 3 4 . 20 21 22 23 24 25 . 229 230 Most functionality is inherited Query execution is delegated to a JS object

Slide 44

Slide 44 text

src/active_record.js active_record.js export function registerSQLiteWasmInterface(worker, db, opts = {}) { const name = opts.name || "sqlite4rails"; worker[name] = { exec: function (sql) { let cols = []; let rows = db.exec(sql, { columnNames: cols, returnValue: "resultRows" }); return { cols, rows, }; }, changes: function () { return db.changes(); }, }; } 1 2 3 4 5 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

Slide 45

Slide 45 text

palkan_tula palkan sqlite.wasm pglite Database 32 Database active_record config/database.yml database.yml default: &default adapter: sqlite3 production: # ... wasm: adapter: pglite development: <<: *default test: <<: *default database: storage/test.sqlite3 1 2 3 4 . 20 21 22 23 24 25 . 28 29 30 31

Slide 46

Slide 46 text

palkan_tula palkan Ruby VM Gems Native extensions System tools Web Server Database Queue Storage bundler active_storage rack active_record active_job API net/http Rails Components 33

Slide 47

Slide 47 text

palkan_tula palkan Service Worker Serve HTTP 34 pwa/rails.sw.js rails.sw.js // ... const initVM = async (progress, opts = {}) => { if (vm) return vm; if (!db) { await initDB(progress); } vm = await initRailsVM("/app.wasm"); return vm; }; const rackHandler = new RackHandler(initVM}); self.addEventListener("fetch", (event) => { // ... return event.respondWith( rackHandler.handle(event.request) ); }); . 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 Web Server rack Write your Rack-compatible server in JS

Slide 48

Slide 48 text

src/rack.js rack.js export class RackHandler { async process(request) { let vm = await this.vmSetup(); const railsURL = request.url; const railsHeaders = {}; for (const [key, value] of request.headers.entries()) { railsHeaders[`HTTP_${key.toUpperCase().replaceAll("-", "_")}`] = value; } try { const command = ` request = Rack::MockRequest.env_for( "${railsURL}", JSON.parse(%q[${JSON.stringify(railsHeaders)}]).merge( method: :${request.method} ) ) response = Rack::Response[*Rails.application.call(request)] status, headers, body = *response.finish {status:, headers:, body:} `; let res = vm.eval(command).toJS(); let { status, headers, body } = res; const resp = new Response(body, { headers, status, }); return resp; } catch (e) { this.logger.error(e); return new Response(`Application Error: ${e.message}`, { status: 500, }); } } } 1 2 3 4 5 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

Slide 49

Slide 49 text

src/rack.js rack.js export class RackHandler { async process(request) { let vm = await this.vmSetup(); const railsURL = request.url; const railsHeaders = {}; for (const [key, value] of request.headers.entries()) { railsHeaders[`HTTP_${key.toUpperCase().replaceAll("-", "_")}`] = value; } try { const command = ` request = Rack::MockRequest.env_for( "${railsURL}", JSON.parse(%q[${JSON.stringify(railsHeaders)}]).merge( method: :${request.method} ) ) response = Rack::Response[*Rails.application.call(request)] status, headers, body = *response.finish {status:, headers:, body:} `; let res = vm.eval(command).toJS(); let { status, headers, body } = res; const resp = new Response(body, { headers, status, }); return resp; } catch (e) { this.logger.error(e); return new Response(`Application Error: ${e.message}`, { status: 500, }); } } } 1 2 3 4 5 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 Boring code that translates requests/ responses from JS to Ruby and back !

Slide 50

Slide 50 text

palkan_tula palkan HTTP: challenges Serving files Handling multipart form data / uploads Managing cookies 36 Web Server rack Base64 and custom Rack middleware Cookie Store API FormData and Data URIs

Slide 51

Slide 51 text

palkan_tula palkan HTTP: the future wasi:http/proxy (WASI 0.2) 37 Web Server rack Implement incoming_handler right in your framework

Slide 52

Slide 52 text

palkan_tula palkan Ruby VM Gems Native extensions System tools Web Server Database Queue Storage bundler active_storage rack active_record active_job API net/http Rails Components 38

Slide 53

Slide 53 text

palkan_tula palkan Other Storage (uploads, assets) Background threads Outgoing HTTP Image processing 39 Storage active_storage Queue active_job API net/http File System API, OPFS Broadcast Channel API (multiple VMs) fetch() now, wasi/http future wasm-vips, magic-wasm

Slide 54

Slide 54 text

palkan_tula palkan Guides Recipes Ideas Wasmbook % 40 https://writebook-on-wasm.fly.dev How to get started with Wasm How to solve particular problems Why and when you may need it

Slide 55

Slide 55 text

Why?

Slide 56

Slide 56 text

palkan_tula palkan Rails on Wasm: Why? Joy of problem solving Pushing boundaries Practical applications 42 Of both Rails and Wasm Programming for happiness You can probably make some money out of it

Slide 57

Slide 57 text

palkan_tula palkan TLR=Try-Learn-Reproduce Offline-aware apps email clients, task boards Data-paranoid apps truly own your data Local-first desktop apps music players, notes 43 Rails on Wasm: Why? Tutorials, online playgrounds, bug reproductions Better offline experience Better data control Bring web frameworks productivity to desktop dev

Slide 58

Slide 58 text

palkan_tula palkan Leave the server bubble, go wherever you want with Wasm! 44

Slide 59

Slide 59 text

Thank you Demo: reviewcon2024.netlify.app Slides: evilmartians.com/events Twitter: @palkan_tula, @evilmartians