jsg: ein kleiner Helfer für ruby.wasm-Projekte
Mit ruby.wasm läuft CRuby im Browser, und das js-Gem ist die Brücke zwischen Ihrem Ruby-Code und der JavaScript-Welt. Bis ein Projekt steht, sind trotzdem einige Handgriffe nötig: eine HTML-Seite, das Browser-Script, ein Gemfile, ein Build-Befehl und ein lokaler Webserver.
2024 habe ich diese Schritte in ein kleines Gem namens jsg gepackt. Die Gemspec fasst es in einem Satz zusammen: jsg hilft beim Aufsetzen von ruby.wasm-Projekten und bringt eine angenehmere Syntax mit. Es besteht also aus zwei Teilen: einem Kommandozeilen-Tool, das ein Projekt anlegt und baut, und einer Ruby-Datei, die Sie im Browser laden.
Vorweg ein ehrlicher Hinweis: jsg ist ein frühes Experiment. Die aktuelle Version ist 0.1.2 vom Mai 2024, das README auf GitHub ist noch der Platzhalter von Bundler, und eine richtige Testsuite gibt es noch nicht. Dieser Beitrag ist das, was einer Dokumentation am nächsten kommt.
Installation
jsg benötigt Ruby 3.0 oder neuer und hängt vom js-Gem (~> 2.6) und von erb ab:
gem install jsg
Das Kommandozeilen-Tool
Das Gem bringt ein jsg-Programm mit drei Befehlen mit:
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 kopiert die Vorlage in einen neuen Ordner myapp. Darin finden Sie:
index.html, aus einer ERB-Vorlage erzeugt, mit dem Projektnamen als Titel und Überschriftsrc/main.rb, den Platz für Ihren eigenen Codeassets/browser.script.iife.js, das Browser-Script von ruby.wasm, so eingerichtet, dass esassets/ruby-app.wasmlädt- ein
Gemfilemitruby_wasm,jsundjsg - eine
.gitignore, die die Ordnerbuild/undrubies/ausschliesst
Direkt nach dem Kopieren wechselt der Befehl in den Ordner und startet den Build. jsg build macht genau zwei Dinge: bundle install und rbwasm build -o assets/ruby-app.wasm. Der Kommentar in src/main.rb erklärt die Idee: Ihr Code gehört dorthin und landet nach jsg build in der .wasm-Datei.
jsg server (oder jsg serve) startet WEBrick auf Port 8080 und weicht auf den nächsten Port aus, falls 8080 schon belegt ist. Der Beispielordner verwendet stattdessen den eingebauten Einzeiler von Ruby, der genauso gut funktioniert:
ruby -run -e httpd . -p 8080
Beide brauchen WEBrick, das seit Ruby 3.0 nicht mehr zur Standardbibliothek gehört. Unter Umständen ist also zuerst ein gem install webrick nötig.
Die «angenehmere Syntax»
Der zweite Teil ist jsg.rb. Die Datei definiert ein Modul JSG mit einigen Abkürzungen und ergänzt JS::Object um eine Reihe von Methoden. Das ist der Ruby-Code, den die Beispielseite im Repository ausführt:
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)
}
Statt JS.global[:document] schreiben Sie $window.document und rufen DOM-Methoden so auf, wie Sie es aus JavaScript kennen. Das ergänzt der Quellcode:
- Abkürzungen auf
JSG:JSG.window(AliasJSG.w) für das globale Objekt,JSG.document(AliasJSG.d) undJSG.querySelectorAll(AliasJSG.q). Alles andere, etwaJSG.global, wird an das ModulJSweitergereicht. - Umwandlung in Ruby-Typen: Eine Methode
to_rbmacht aus JavaScript-Zahlen Floats, aus Strings Strings, aus Booleanstrue/false, aus BigInts Integers und aus Arrays Ruby-Arrays. Andere Objekte bleibenJS::Object. - Eigenschaften und Methodenaufrufe:
method_missingliest Eigenschaften und ruft Funktionen auf und wandelt das Ergebnis mitto_rbum. Ein Name, der auf=endet, setzt eine Eigenschaft; ein Name, der auf?endet, liefert einen Ruby-Boolean. - Vergleiche:
==vergleicht mit Rubystrue,falseundnil, sodass Sie nichtJS::Trueschreiben müssen. Dazu kommennil?,undefined?undtypeof?(:string). - Arrays:
to_averwendetArray.fromaus JavaScript. Array-ähnliche Objekte wie eineNodeListwerden so zu Ruby-Arrays, undeachiteriert über JavaScript-Arrays.
Die index.html der Vorlage zeigt ausserdem einen Kniff für die Entwicklung: Sie definiert require_relative neu, sodass Dateien über HTTP mit JS::RequireRemote geladen werden. Laut Kommentar ist das nur nötig, wenn Sie require_relative statt jsg build verwenden möchten.
Stand
jsg ist klein und hat noch Ecken und Kanten. Wenn Sie es ausprobieren und etwas nicht funktioniert: Der Quellcode ist kurz, eine Ruby-Datei für den Browser und ein Script in exe/jsg. Issues und Pull Requests sind auf GitHub willkommen. Die Lizenz ist MIT.
ruby.wasm taucht auch in einem anderen Projekt von mir auf: Die Hackety-Hack-Portierung hinter clogs hat einen Browser-Build, in dem CRuby als WebAssembly läuft.