Ruby a rejoint les rangs des langages capables de cibler WebAssembly avec sa dernière version 3.2. Cette mise à jour apparemment mineure pourrait être la plus grande chose qui soit arrivée au langage depuis Rails, car elle permet aux développeurs Ruby d’aller au-delà du back-end. En transférant leur code sur WebAssembly, ils peuvent l’exécuter n’importe où : sur le front-end, sur des appareils intégrés, en tant que fonctions sans serveur, à la place de conteneurs ou en périphérie. WebAssembly a le potentiel de faire de Ruby un langage universel.
Qu’est-ce que WebAssembly ?
WebAssembly (couramment abrégé en Wasm) est un format d’instruction binaire de bas niveau qui s’exécute sur une machine virtuelle. Le langage a été conçu comme une alternative à JavaScript. Son objectif est d’exécuter des applications sur n’importe quel navigateur à des vitesses quasi natives. Wasm peut être ciblé à partir de n’importe quel langage de haut niveau comme C, Go, Rust, et maintenant aussi Ruby.
Wasm est devenu un standard du W3C en 2019, ouvrant la voie à l’écriture d’applications performantes pour le Web. La norme elle-même évolue toujours et son écosystème se développe. Actuellement, cette technologie reçoit beaucoup d’attention de la Cloud Native Computing Foundation (CNCF), avec plusieurs projets en cours de développement.
La conception de Wasm repose sur deux piliers : portabilité et sécurité. Le binaire Wasm peut fonctionner sur n’importe quel navigateur moderne, même les appareils mobiles. Pour des raisons de sécurité, les programmes Wasm s’exécutent dans une machine virtuelle en bac à sable et sécurisée en mémoire. En tant que tels, ils ne peuvent accéder à aucune ressource système : ils ne peuvent pas modifier le système de fichiers ni accéder au réseau ou à la mémoire.
WebAssembly fait passer la portabilité au niveau supérieur
Supposons que vous souhaitiez créer une application ciblant de nombreux systèmes, par exemple Linux, Windows et macOS. Quelles sont vos options ?
Vous pouvez utiliser un langage compilé comme C et créer un binaire pour chaque cible.
Ou, si vous pouvez compter sur l’installation du runtime approprié, vous pouvez choisir un langage interprété comme JavaScript ou un langage qui se compile en bytecode comme Java.
Que se passe-t-il si vous avez un environnement d’exécution de conteneur dans le client ? Dans ce cas, vous pouvez créer une image Docker pour chaque type de plate-forme.
Pour les développeurs Ruby, historiquement, la seule option était de distribuer le code. Cela signifiait que les utilisateurs devaient installer l’interpréteur Ruby (ou que les développeurs devaient empaqueter l’interpréteur avec l’application) pour exécuter l’application.
Tous ces mécanismes offrent une portabilité, mais à un coût : vous devez créer, tester et distribuer de nombreuses images. Parfois, vous devez également fournir un environnement d’exécution approprié avec la version ou demander à l’utilisateur de l’installer indépendamment.
WebAssembly (abrégé en Wasm) fait passer la portabilité au niveau supérieur : il vous permet de créer UN binaire et de l’exécuter dans n’importe quel navigateur moderne.
La possibilité d’exécuter du code à une vitesse native a permis aux développeurs de créer des sites comme Figma et Google Earth ou même d’exécuter Vim dans le navigateur.
Ruby ajoute la prise en charge de WebAssembly
La dernière version de Ruby est livrée avec un portage Wasm de l’interpréteur. Par conséquent, nous pouvons exécuter le code Ruby directement dans le navigateur sans avoir besoin d’un backend.
Comme vous pouvez le voir dans l’exemple ci-dessous, il suffit de quelques lignes pour démarrer avec le port Ruby Wasm. Le script se télécharge ruby.wasm
et instancie l’interpréteur dans le navigateur. Après cela, il prend le texte de text/ruby
tapez et l’alimente dans le programme WebAssembly.
<html>
<script src="https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@0.5.0/dist/browser.script.iife.js"></script>
<script type="text/ruby">
puts "Hello, world!"
</script>
</html>
Vous pouvez confirmer que Ruby s’exécute depuis le navigateur, c’est-à-dire qu’il ne se connecte pas à un backend, en ouvrant les outils des développeurs. Ici, vous trouverez une fois ruby.wasm
est téléchargé, aucune autre connexion n’est nécessaire.
Traditionnellement, JavaScript a été présenté comme le meilleur langage à apprendre car vous l’avez partout. Avec WebAssembly, tout le monde peut apprendre et expérimenter Ruby à l’aide d’un navigateur. La sortie est imprimée dans la console du développeur.
Vous pouvez même voir le contenu de ruby.wasm
désassemblé au format texte dans l’onglet « Sources » :
Nous pouvons voir le fichier Wasm téléchargé dans les outils de développement Web des navigateurs.
Vous pouvez consulter le port Wasm en ligne sur le terrain de jeu Ruby.
Travailler avec le bac à sable
Comme indiqué, les programmes Wasm s’exécutent dans une machine virtuelle en bac à sable qui n’a pas accès au reste du système. Par conséquent, les applications Wasm ne pas avoir accès au navigateur, au système de fichiers, à la mémoire ou au réseau. Nous aurons besoin de code JavaScript pour envoyer et recevoir des données du bac à sable.
L’exemple suivant montre comment lire la sortie d’un programme Ruby et apporter des modifications à la page à l’aide du package NPM ruby-head-wasm-wasi :
<html>
<script src="https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@latest/dist/browser.umd.js"></script>
<script>
const { DefaultRubyVM } = window["ruby-wasm-wasi"];
const main = async () => {
const response = await fetch(
"https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@latest/dist/ruby.wasm"
);
const buffer = await response.arrayBuffer();
const module = await WebAssembly.compile(buffer);
const { vm } = await DefaultRubyVM(module);
vm.printVersion();
vm.eval(`
require "js"
luckiness = ["Lucky", "Unlucky"].sample
JS::eval("document.body.innerText="#{luckiness}"")
`);
};
main();
</script>
<body></body>
</html>
Le même package peut également exécuter du code Ruby dans un projet Node, vous permettant de mélanger Ruby et JavaScript sur le backend. Vous devrez installer le package NPM ruby-head-wasm-wasi
pour que l’exemple fonctionne :
import fs from "fs/promises";
import { DefaultRubyVM } from "ruby-head-wasm-wasi/dist/node.cjs.js";
const main = async () => {
const binary = await fs.readFile(
// Tips: Replace the binary with debug info if you want symbolicated stack trace.
// (only nightly release for now)
// "./node_modules/ruby-head-wasm-wasi/dist/ruby.debug+stdlib.wasm"
"./node_modules/ruby-head-wasm-wasi/dist/ruby.wasm"
);
const module = await WebAssembly.compile(binary);
const { vm } = await DefaultRubyVM(module);
vm.eval(`
luckiness = ["Lucky", "Unlucky"].sample
puts "You are #{luckiness}"
`);
};
main();
Exécution de ruby.wasm en dehors du navigateur
Alors que l’objectif principal de conception de Wasm était d’exécuter du code binaire dans le navigateur, les développeurs ont rapidement réalisé le potentiel d’un format binaire rapide, sûr et universellement portable pour la livraison de logiciels. Wasm a le potentiel de devenir aussi grand que Docker, simplifiant considérablement le déploiement d’applications pour les systèmes embarqués, les fonctions sans serveur, l’informatique de pointe ou en remplacement des conteneurs sur Kubernetes.
L’exécution d’une application Wasm en dehors du navigateur nécessite un environnement d’exécution approprié qui implémente la machine virtuelle WebAssembly et fournit des interfaces au système sous-jacent. Il existe quelques solutions concurrentes dans ce domaine, les plus populaires étant wasmtime, wasmer et WAMR.
Le référentiel Ruby fournit un exemple complet pour regrouper votre code d’application dans une image Ruby personnalisée.
Limites
Rappelons-nous que tout cela est une technologie de pointe. L’ensemble de l’écosystème Wasm évolue rapidement. À l’heure actuelle, Ruby Wasm a quelques limitations qui limitent considérablement sa convivialité dans les grands projets :
-
Pas de prise en charge des threads.
-
Les processus de frai ne fonctionnent pas.
-
Pas de support réseau.
-
Le ramasse-miettes peut créer des fuites de mémoire.
-
Les gemmes et les modules ne sont disponibles que si vous créez une image Wasm personnalisée.
L’avenir est prometteur
WebAssembly ouvre un monde de possibilités passionnantes. Il permet aux développeurs Ruby d’échapper au backend. Au fur et à mesure que les outils autour de WebAssembly s’améliorent, Ruby pourra atteindre de nouvelles frontières : le navigateur n’est plus interdit et il y aura de nouvelles opportunités d’exécuter Ruby en périphérie et en tant qu’applications sans serveur.
Avec la dernière version, les développeurs Ruby peuvent commencer à expérimenter WebAssembly. C’est la première étape, bien sûr, et il reste encore beaucoup de travail à faire avant de voir des applications Ruby complexes fonctionner avec cette technologie.
Merci d’avoir lu et bon assemblage !