what does it mean?

WITH release of version 3.2 Ruby has expanded the list of programming languages ​​that support WebAssembly. A seemingly small update could be the biggest change to the language since Rails, as developers can now work with more than just the backend. After porting the code to WebAssembly, it can be run anywhere and any way – on the frontend, embedded devices, as serverless functions, instead of containers, or in edge computing. WebAssembly can turn Ruby into a general-purpose programming language. Details under the cut, and practice on the web – on our course on Fullstack Python Development.

WebAssembly is a binary low-level instruction format that runs on a virtual machine. This language was conceived as an alternative to JavaScript to run applications in any browser with maximum speed. Compile targets can be languages ​​such as C, Go, Rust, and now Ruby.

In 2019 Wasm entered the W3C standard, which allowed developers to write high performance applications for the web. The standard itself is still evolving, and its ecosystem is growing. Now the Cloud Native Computing Foundation (CNCF) has paid close attention to WebAssembly. Under the auspices of CNCF a number of projects are being developed.

Wasm’s design is based on two principles: portability And security. The Wasm binary runs in any modern browser, including mobile devices. For security reasons, Wasm programs run in an isolated, memory-safe virtual machine. Consequently, such programs cannot access system resources: they cannot change the file system or access network or memory.

WebAssembly takes portability to the next level

Let’s say you want to create a cross-platform application for Linux, Windows, and macOS. How can I do that?

You can use a compiled programming language, such as C, and create binary code for each OS.

The code is compiled in three formats: ELF for Linux, PE for Windows and Mach for macOS.  We have one source code and three binary options.

The compiler creates several executable files.

You can work with a suitable code execution environment and choose between an interpreted language like JavaScript or a language that compiles to bytecode like Java.


The code is compiled into a single universal bytecode format that is executed on the platform through the runtime. The diagram shows the compilation process for Java and JRE on various platforms

The code is compiled into an intermediate representation – bytecode. Such a system requires a runtime or virtual machine on the client device.

What if the client device has a containerized runtime? Then you can create a Docker image for each platform.


Container workflow diagram. The code is built in Docker. Three images are being formed: for Linux, for Windows and for ARM. The runtime on the client device selects the desired image and runs it

The code is compiled into platform-specific images. The client device must have a container runtime that automatically selects the correct image.

Previously, Ruby developers had to submit code to users. To run the application, customers needed to install a Ruby interpreter or get one from the developer.


Developers submit code. Clients need to install the appropriate interpreter in order to run the application

Users get the code itself, and to run it, you need to install an interpreter.

Such approaches provide portability, but you have to pay for it – the developer needs to collect, test and send various images to clients. Sometimes you need to include the appropriate runtime in a release or indicate that you need to install it separately.

WebAssembly (Wasm for short) takes portability to the next level: with Wasm, you can create the only one binary and run it on any modern browser.


Demonstration of WebAssembly. The code compiles to a single Wasm binary. It can be launched from any browser. The single binary runs on Linux, macOS and Windows. Ruby WebAssembly works the same way.

WebAssembly compiles code into low-level assembler for the web; the same Wasm binary can be run without changes on any platform, even mobile.

The ability to run code at native speed has allowed developers to create Figma and Google Earth, even run in Vim browser.

Ruby adds support for WebAssembly

The latest release of Ruby contains an interpreter ported to Wasm: you can run Ruby code in a browser without a backend, directly.

Ruby ported to Wasm only needs a couple of lines of code to get started. The script is downloading ruby.wasm and instantiates the interpreter in the browser. After that the text with type text/ruby sent to the WebAssembly program.

<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>

Open developer tools to see that Ruby is running from a browser with no backend. After download ruby.wasm there are no connections.


The open browser console says “Hello, world!” This code is running in Ruby WebAssembly

JavaScript is considered the best language to learn because it is everywhere. But with WebAssembly, people can learn and experiment with Ruby in the browser. The result is output to the console.

On the “Sources” tab, you can even see the contents ruby.wasmdisassembled into text format:


The developer tools in the browser are open on the “Sources” tab. The list of sources includes the Wasm folder with the downloaded Ruby interpreter. The displayed text is a port of the Ruby WebAssembly interpreter

Wasm file is visible in developer tools.

Wasm port available in Sandbox Ruby.

sandbox king

As already mentioned, Wasm programs run in sandbox mode in a virtual machine that does not have access to other parts of the system. So, the application on Wasm does not have access to the browser, file system, memory or network. To receive or send data from the sandbox, you need JavaScript code.

This example shows how to read the output of a Ruby program and modify the page using the npm package. 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>

The same package is used to run Ruby code in a Node project, which allows you to mix Ruby and JavaScript on the backend. For this example to work, you need to install the npm package ruby-head-wasm-wasi:

import fs from "fs/promises";
import { DefaultRubyVM } from "ruby-head-wasm-wasi/dist/node.cjs.js";

const main = async () => {
  const binary = await fs.readFile(
    //  Подсказка: при необходимости замените бинарный файл на информацию об отладке, если вам требуется символизированная трассировка стека
    //  (пока что только в ночной версии)
    //  "./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();

Running Ruby WebAssembly outside the browser

Wasm’s main goal is to execute binary code in the browser, but developers quickly realized the potential of a fast, secure, and portable binary format for delivering software to any device. Wasm could become as important as Docker. With it, you can greatly simplify the deployment of applications on embedded systems, in serverless functions, computing on the periphery (edge ​​computing, also edge computing). You can use Wasm as Kubernetes container replacements.

Running an application on Wasm outside of a browser requires an appropriate runtime environment with a WebAssembly virtual machine and interfaces to the underlying system. There are several solutions, the most popular of which are wasmtime, wasmer And WAMR.

The Ruby repository contains complete example wrapping application code into a custom Ruby image.

Limitations of Ruby WebAssembly

Don’t forget that Ruby WebAssembly is a very recent development. The Wasm ecosystem is evolving very fast. And today Ruby Wasm has a number of disadvantages that significantly limit its use in large projects:

  • No thread support.
  • Spawning of child processes does not work.
  • Network not supported.
  • The garbage collector can create memory leaks.
  • Gems and modules are only available when creating a custom Wasm image.

The future is great

WebAssembly opens up an exciting world. With Wasm Ruby, developers can move away from the backend. As WebAssembly advances to new frontiers, Ruby will also open up opportunities for using the language in edge computing and serverless applications.

After the release of the latest version of Ruby, developers can start experimenting with WebAssembly. Of course, this is only the first step; There is still a lot of work to be done before complex Ruby applications can be run with Wasm.

Thank you for your attention and happy assembling! And in our courses you will find useful theory and interesting practice:

Brief catalog of courses

Data Science and Machine Learning

Python, web development

Mobile development

Java and C#

From basics to depth

And

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *