Expanding Ruby’s Reach with WebAssembly: Portable Coding Across Platforms

Code is compiled into three formats: ELF binary for Linux, PE binary for Windows, and Mach binary for macOS. We have one source code and three binaries.

In the world of programming, new opportunities are constantly arising that challenge the outdated ways we once perceived as norms. One such prospect is WebAssembly (Wasm), a binary low-level instruction format that runs on a virtual machine and is designed as an alternative to JavaScript. The main advantage of Wasm lies in its ability to run applications with near-native speeds on any browser. Initially, languages such as C, Go, and Rust were targeted, but now Ruby has joined these ranks in the latest 3.2 release.

The Power of Portable

The main advantage of WebAssembly is its portability. Say you’re trying to build an application targeting multiple operating systems like Linux, Windows, and macOS. Traditional options would have been a compiled language like C or an interpreted programming language that compiles to bytecode such as Java. Another option is Docker container which builds a Docker image for each platform.

Compiler portability creates multiple executable files

The cost of these mechanisms is the need to build, test, and distribute multiple images and releases. Sometimes, it’s even required to have a suitable runtime shipped with the release or tell users to install it independently.

The diagram shows Java compilation and the JRE running on each platform

Enter WebAssembly. It compiles code into a low-level assembly that every modern browser can execute. No multiple images. No multiple binaries. One Wasm binary can run, unmodified, on every platform (including mobile).

WebAssembly compiles into a low-level assembly

Ruby Meets WebAssembly

The latest Ruby release ships with a Wasm port of the interpreter. This means we can now run Ruby code directly in the user’s browser without requiring a backend. Even complex applications such as Vim can be run in the browser.

<script src="ruby.wasm" type="text/ruby">
puts "Hello, world!"
</script>

The possibilities don’t end there. With the use of a sandbox, you can even experiment and learn with Ruby right from your browser.

<script type="module">
import { DefaultRubyVM } from "ruby-head-wasm-wasi";

async function runRubyCode() {
    const response = await fetch("https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@latest/dist/ruby.wasm");
    const wasmBinary = await response.arrayBuffer();
    const webAssemblyModule = await WebAssembly.compile(wasmBinary);
    const { instance } = await DefaultRubyVM(webAssemblyModule);
    instance.eval(`puts "You are #{["Lucky", "Unlucky"].sample}"`);
}

runRubyCode();
</script>

The Flip Side: Limitations

As with any new technology, there are kinks that need to be worked out. At present, Ruby Wasm has a few limitations which limit its usability in big projects:

  • No support for threads.
  • Network support is absent.
  • Spawning processes does not work.
  • The garbage collector can create memory leaks.
  • Gems and modules are unavailable unless you build a custom Wasm image.

Despite these current drawbacks, the potential offered by WebAssembly for Ruby developers is vast and exciting. The browser is no longer off-limits for Ruby developers and there are new frontiers to explore like running Ruby on the edge and serverless applications. The future is indeed bright and exciting.

Happy coding!

Tags: #WebAssembly, #Ruby, #Wasm, #Portability

Reference Link