Ruby on Rails on WebAssembly Bring your Rails application right into the browser for the win! Vladimir Dementyev

  • Move Introduction
    Open Introduction

    Intro to Rails on Wasm

    Ruby on Rails is a famous “batteries included” framework for the rapid development of web applications. Its full-stack promise comes in a server-oriented, or HTML-over-the-Wire flavor: a server oversees everything from database interactions to your application UI/UX. And whenever there is a server involved, the network and its unpredictability come into play.

    No matter how much you enjoy developing with Rails, it would be hard to achieve the same level of user experience as with client-side and especially local-first frameworks. And here comes Wasm. With the help of WebAssembly, we can do a radical shift—bring the Rails application right into your browser, and make it local-first or at least offline-ready!

    This handbook contains recipies, ideas, and learning materials on how to leverage Wasm in Rails applications.

    Introduction 132 words
  • Move Just enough Wasm for a Rubyist
    Open Just enough Wasm for a Rubyist

    Just enough Wasm for a Rubyist

    Just enough Wasm for a Rubyist
  • Move Wasm in a nutshell
    Open Wasm in a nutshell

    Wasm in a nutshell

    wasm-in-a-nutshell.png

    Wasm in a nutshell 6 words
  • Move What is Wasm
    Open What is Wasm

    What is Wasm

    Wasm, or WebAssembly is a binary instruction format for a stack-based virtual machine. In other words, it's a custom, language- and platform-agnostic byte-code representation of your code.

    wasm-expolorer.png

    An example Wasm module bytes and instructions (via Wasm Code Explorer)

    Whether you write in C, C++, Rust, Go, Ruby—almost any programming language—you can compile your code into a .wasm binary module and execute the program within a Wasm runtime.

    The major benefits of using Wasm are:

    • Speed. WebAssembly instructions are closer to machine code and could be optimized efficiently.

    • Portability. You can compiler your code into a Wasm module once and use everywhere.

    • Safety. Wasm runtimes are sandbox execution environments.

    Let's talk about runtimes for a bit.

    Wasm runtimes

    The original and the most popular Wasm

    What is Wasm 240 words
  • Move What is WASI
    Open What is WASI

    What is WASI

    Another important component of wasmification is WASI. WASI, or WebAssembly System Interface, is a group of standard API specifications for Wasm programs and runtimes. In others words, WASI specifies how your Wasm-compiled program can communicate with the outer world (i.e., which system calls are availables) if this outer world complies with particular WASI standards.

    WASI makes Wasm modules truly portable: your code shouldn't no about whether it's about to be executed in the browser or in some other Wasm runtime. It only requries for specific API to be implemented.

    For example, WASI defines how to work with the underlying filesystem, random number generators, etc.

    WASI is still a young initiative. There were just two releases: so-called Preview 1 and Preview 2 (not they're called WASI 0.1 and WASI 0.2 respectively).

    Preview 1 provided only basic syscall APIs while Preview 2 consists of multiple modules (clocks, http, cli, etc.) and, more importa

    What is WASI 229 words
  • Move Meet ruby.wasm
    Open Meet ruby.wasm

    Meet ruby.wasm

    Finally, let's talk about Ruby and its wasm-ability.

    One of the highlights of the Ruby 3.2 release was the WASI based WebAssembly support. What that meant is that from then on, the CRuby VM could be compiled into a ruby.wasm module and run in the browser!

    You can check this yourself using the following HTML/JS code:

    <html>
      <script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.2/dist/browser.script.iife.js"></script>
      <script type="text/ruby">
        require "js"
    
        JS.global[:document].write "Hello, world, from #{RUBY_VERSION}!"
      </script>
    </html>
    

    Of course, not every Ruby code can be executed within a browser (or another Wasm runtime). Remember WASI? The current stable version of ruby.wasm supports only Preview 1, so no sockets, HTTP, and other cool stuff. Threads are also yet to come to Wasm (they only reached [Phase 1](https://was

    Meet ruby.wasm 268 words
  • Move Wasmify Rails
    Open Wasmify Rails

    Wasmify Rails

    Wasmify Rails
  • Move The whys and nots of Rails Wasm-ification
    Open The whys and nots of Rails Wasm-ification

    The whys and nots of Rails Wasm-ification

    Before we start digging deeper into technical details of running Rails on Wasm, let's talk for a moment why, beyond the joy of problem solving, you might want to do that.

    The cases of client-side Rails

    There are many popular Rails applications out there in the wild (check, for example, Using Rails). They all embrace the client-server paradigm with the server responsible for processing requests from different clients (sometimes, tons of clients) and communicating with other systems and services. You cannot do all of that if you manage to launch a Rails server within your browser.

    However, there are some scenarios when it could be beneficial.

    Let's start with the obvious one—this application.

    You can turn your network connection off and continue reading the book, even though it's backed by the Rails application. Try right now!*

    * Might now work in some browsers, sorry :( Try visiting the <a href=

    The whys and nots of Rails Wasm-ification 658 words
  • Move The hows of Rails Wasm-ification, or wasmify-rails
    Open The hows of Rails Wasm-ification, or wasmify-rails

    The hows of Rails Wasm-ification

    Compiling a full-featured Rails application into a Wasm module can be challenging. Why? Because a typical Rails application depends on much more than just a Ruby VM. Just take a quick look:

    rails-parts.png

    What do we have on our plate?

    • We heavily depend on native extensions, some of which require specific system libraries (hello, Nokogiri!)
    • A Rails app is hardly imaginable without a database
    • Oh, we need an actual web server to serve HTTP requests!
    • ...Not only plain text requests, but file uploads (and don't forget about image transformations)
    • What about queues for our background jobs?
    • Should mention cables (real-time features) or it's enough?

    You may think that solving all these problems doesn't worth the effort. Let's just run Rails on servers. But don't give up too quickly! We have something that could help you get started with Rails on Wasm with as little burden as possible.

    The hows of Rails Wasm-ification, or wasmify-rails 159 words
  • Move bin/rails wasmify
    Open bin/rails wasmify

    Introducing wasmify-rails

    Most of the steps required to make a Rails application Wasm-compatible are the same for all Rails applications (at least those we worked with). That means, there should be a way to automate or at least simplify the process. And here comes wasmify-rails.

    wasmify_install.png

    Wasmify Rails is a collection of tools and extensions to speed up the process of compiling a Rails application into a Wasm module as well as the starter project to run Rails in the browser.

    Read the project's Readme for the full instructions set. Let me recall the basics here:

    • Install the wasmify-rails gem

    • Run the bin/rails wasmify:install command to preconfigure your applicaiton add the wasm environment

    • Run the bin/rails wasmify:pack command to compile the app into a Wasm module (it's likely gonna fail from the first time, you need to tweak your configuration and iterate)

    • (Optionally) Genera

    bin/rails wasmify 211 words
  • Move Deep dive into Rails on Wasm
    Open Deep dive into Rails on Wasm

    Deep dive into Rails on Wasm

    Deep dive into Rails on Wasm
  • Move Bundler on Wasm
    Open Bundler on Wasm

    Bundler on Wasm

    Building Wasm modules is quite to creating Docker images: you start with the foundation, Ruby itself, then you add your dependencies via the bundle install command.

    In ruby.wasm, we have the rbwasm build command that performs a similar task: generates a Wasm module with Ruby and all the specified dependencies from the Gemfile.

    The command pulls the Ruby (MRI) source code, all the dependencies, and tries to compile and link them together into a single module. Why "tries"? Many dependencies rely on native (usually, C) extensions. Good news is that ruby.wasm can compile C extensions into WebAssembly. Not-so-good news is that not every extension is Wasm-compatible yet (e.g, nokogiri).

    Sometimes non-compilable extensions are optional transitive dependencies of the libraries you need (e.g., actioncable depends on nio4r, and rails depends on actioncable). To deal with this situation, wasmify-rails provides a custom task, wasmify:build, which wraps rbwasm build an

    Bundler on Wasm 364 words
  • Move Shims and stubs

    Shims and stubs

    TBD (excluding extensions, stubbing missing libraries)

    Shims and stubs Coming soon
  • Move Active Record on Wasm

    Active Record on Wasm

    TBD (sqlite3_wasm, pglite, nulldb)

    Active Record on Wasm Coming soon
  • Move Rack on Wasm

    Rack on Wasm

    TBD (data URIs, http "servers")

    Rack on Wasm Coming soon
  • Move Assets on Wasm

    Assets on Wasm

    TBD (precompilation, caches, OPFS)

    Assets on Wasm Coming soon
  • Move Untitled

    Active Storage on Wasm

    TDB (null processing, database backend, OPFS backend)

    Untitled Coming soon
  • Move HTTP APIs on Wasm

    HTTP APIs on Wasm

    TDB (faraday js, sniffer)

    HTTP APIs on Wasm Coming soon
  • Move Active Job on Wasm

    Active Job on Wasm

    TBD (workers, broadcast channel)

    Active Job on Wasm Coming soon
  • Move Action Cable on Wasm
    Open Action Cable on Wasm

    Action Cable on Wasm

    Let's think about how we can implement a real-time communication functionality for the app running locally, without any real-time server involved. What the use of live updates in such an isolated environment? Apart from the case of running multiple copies of the application (e.g., browser tabs), there is a more significant scenario of performing some work in the background. In other words, signaling the client (JavaScript) about the operations completed outside of the request-response loop is still important.

    Luckly, we can leverage Action Cable for that even under such unusual circumstances.

    NOTE: All approaches described in this chapter have two requirements: 1) next-gen Action Cable architecture (see actioncable-next) to support non-WebSocket clients; 2) usage of AnyCable JS SDK instead of the Rails one because of the ability to implement cus

    Action Cable on Wasm 715 words
  • Move Resources
    Resources
  • Move Code examples

    Code examples

    Most of these code examples are extracted from this application, Writebook on Wasm, which is open-sourced, so we cannot just publish it as is.

    Rails PWA integration

    TBD

    Database persistence

    TDB

    Storing Wasm modules in the browser

    Code examples Coming soon
  • Move Posts, videos, links
    Posts, videos, links 101 words