jsg: un piccolo aiuto per i progetti ruby.wasm
ruby.wasm esegue CRuby nel browser, e la gem js fa da ponte tra il vostro codice Ruby e il mondo JavaScript. Avviare un progetto richiede però ancora alcuni passi manuali: una pagina HTML, lo script per il browser, un Gemfile, un comando di build e un web server locale.
Nel 2024 ho raccolto questi passi in una piccola gem chiamata jsg. Il suo gemspec la riassume in una riga: “JSG helps setting up ruby.wasm projects and comes with a nicer syntax”. Le metà sono quindi due: uno strumento da riga di comando che prepara e compila un progetto, e un file Ruby che caricate nel browser.
Un avvertimento onesto: jsg è un esperimento in fase iniziale. La versione attuale è la 0.1.2 del maggio 2024, il README su GitHub è ancora il segnaposto di Bundler e non esiste ancora una vera suite di test. Questo articolo è quanto di più vicino a una documentazione esista.
Installazione
jsg richiede Ruby 3.0 o successivo e dipende dalla gem js (~> 2.6) e da erb:
gem install jsg
Lo strumento da riga di comando
La gem include un eseguibile jsg con tre comandi:
jsg new myapp # create a project from the template, then build it
jsg build # bundle install, then build assets/ruby-app.wasm
jsg server # serve the current directory with WEBrick
jsg new myapp copia il template in una nuova cartella myapp. Ottenete:
index.html, generato da un template ERB con il nome del progetto come titolo e intestazionesrc/main.rb, il posto per il vostro codiceassets/browser.script.iife.js, lo script per il browser di ruby.wasm, configurato per caricareassets/ruby-app.wasm- un
Gemfileconruby_wasm,jsejsg - un
.gitignoreche ignora le cartellebuild/erubies/
Subito dopo la copia entra nella cartella ed esegue la build. jsg build fa esattamente due cose: bundle install e rbwasm build -o assets/ruby-app.wasm. Il commento in src/main.rb del template spiega l’idea: il vostro codice va lì e, dopo jsg build, finisce all’interno del file .wasm.
jsg server (o jsg serve) avvia WEBrick sulla porta 8080 e passa alla porta successiva se la 8080 è già occupata. La cartella degli esempi usa invece il one-liner integrato in Ruby, che funziona altrettanto bene:
ruby -run -e httpd . -p 8080
Entrambi si basano su WEBrick, che da Ruby 3.0 non fa più parte della libreria standard; potrebbe quindi servire prima un gem install webrick.
La “sintassi più comoda”
La seconda metà è jsg.rb. Definisce un modulo JSG con alcune scorciatoie e aggiunge una serie di metodi a JS::Object. Questo è il Ruby eseguito dalla pagina di esempio nel repository:
require 'jsg'
$window = JSG.global
$d = $window.document
$d.getElementById("spinner").style.display = "none"
$d.createElement("h2").tap {|e|
e.innerText = "Examples"
$d.body.appendChild(e)
}
Invece di JS.global[:document] scrivete $window.document, e chiamate i metodi del DOM come fareste in JavaScript. Ecco cosa aggiunge il sorgente:
- Scorciatoie su
JSG:JSG.window(aliasJSG.w) per l’oggetto globale,JSG.document(aliasJSG.d) eJSG.querySelectorAll(aliasJSG.q). Tutto il resto, comeJSG.global, viene inoltrato al moduloJS. - Conversione in tipi Ruby: un metodo
to_rbtrasforma i numeri JavaScript in Float, le stringhe in String, i booleani intrue/false, i bigint in Integer e gli array in Array Ruby. Gli altri oggetti restanoJS::Object. - Accesso alle proprietà e chiamate di metodi:
method_missinglegge le proprietà e chiama le funzioni, convertendo il risultato conto_rb. Un nome che termina con=imposta una proprietà, e un nome che termina con?restituisce un booleano Ruby. - Confronti:
==confronta contrue,falseenildi Ruby, quindi non serve scrivereJS::True. Ci sono anchenil?,undefined?etypeof?(:string). - Array:
to_ausaArray.fromdi JavaScript, così oggetti simili ad array come unaNodeListdiventano Array Ruby, eeachitera sugli array JavaScript.
L’index.html del template mostra anche un trucco per lo sviluppo: ridefinisce require_relative per caricare i file via HTTP tramite JS::RequireRemote. Il commento spiega che serve solo se volete usare require_relative invece di jsg build.
Stato
jsg è piccola e ancora un po’ grezza. Se la provate e qualcosa non funziona, il sorgente è breve: un file Ruby per il lato browser e uno script in exe/jsg. Issue e pull request sono benvenute su GitHub. La licenza è MIT.
ruby.wasm compare anche in un altro mio progetto: il port di Hackety Hack dietro clogs ha una build per browser che esegue CRuby compilato in WebAssembly.