In this blog post, I'll give you a very quick introduction to write your own Ruby scripts and run on browsers using WASM.
At first, please have a look at this Demo page.
Once you click the button, you'll get a number randomly from 1 to 6. This is a very quick simulation of rolling a dice 🎲.
The Ruby script is faily simple, to make this demo as simple as possible. However, I'd like to test some features like class
and calling functions, so this example is not a simple "Hello, world" either.
class Dice
def initialize(items)
@items = items
end
def result
@items.sample
end
end
def sample()
items = %w(1 2 3 4 5 6)
dice = Dice.new(items)
dice.result
end
# the evaluated last line will be returned
sample
Here is a fun part.
WASM ports of Ruby (in this example, CRuby) is achieved by github.com/ruby/ruby.wasm. This enables running Ruby applications on any environments that is WASI compatible WebAssembly runtimes. That sounds amazing.
In order to run it on browsers, ruby-head-wasm-wasi
NPM package is distributed.
At first, you'll need a default Ruby VM. You can get it also from one of distributed package on the CDN as follows.
<head>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/browser.umd.js"></script>
</head>
<script>
const { DefaultRubyVM } = window["ruby-wasm-wasi"];
</script>
Next, you need to fetch and initialize a VM instance using ruby.wasm
. In order to fetch ruby.wasm
, await fetch()
is used so the main function should be defined as an async function.
const main = async () => {
// Download WebAssembly port of CRupy with WASI.
// https://www.npmjs.com/package/ruby-head-wasm-wasi
// original: https://github.com/ruby/ruby.wasm
const response = await fetch("https://cdn.jsdelivr.net/npm/[email protected]/dist/ruby.wasm")
// initialize a VM instance
const buffer = await response.arrayBuffer()
const module = await WebAssembly.compile(buffer)
const { vm } = await DefaultRubyVM(module)
}
Let's debug that it's working as expected. Print the current VM version. This should output the version string and see if that's what you're expecting at.
// debug
vm.printVersion()
ruby 3.2.0dev (2022-04-09) [wasm32-wasi]
Now the most exciting part. Write your own Ruby script in string, and evaluate that with vm.eval()
as follows.
const sourceCode = `
class Dice
def initialize(items)
@items = items
end
def result
@items.sample
end
end
def sample()
items = %w(1 2 3 4 5 6)
dice = Dice.new(items)
dice.result
end
# the evaluated last line will be returned
sample
`
// eval the source code and get the result
const result = vm.eval(sourceCode)
Now the script is evaluated and the return is passed to result
const variable. You can do whatever you want with this, for example, writing to the console with console.log(result.toString())
. In this demo, the result string is inserted into the div element.
// append the result and give back the feedback to users
const resultDiv = document.getElementById('result')
resultDiv.innerText = result.toString()
This is really amazing that you can run most of Ruby scripts on WASI-compatible runtimes, meaning most of modern browsers. Can't wait to see what we can achieve by this technology.