Skip to content

Commit

Permalink
Merge pull request #329 from ruby/katei/enhance-doc
Browse files Browse the repository at this point in the history
doc: Add cheat sheet and FAQ
  • Loading branch information
kateinoigakukun authored Dec 3, 2023
2 parents 0ad0940 + d8f0e3c commit 8532165
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 1 deletion.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ It enables running Ruby application on browsers, WASI compatible WebAssembly run

Try ruby.wasm in [TryRuby](https://try.ruby-lang.org/playground#code=puts+RUBY_DESCRIPTION&engine=cruby-3.2.0dev) in your browser.

## Quick Links

- [[**Cheat Sheet**]](./docs/cheat_sheet.md)
- [[**FAQ**]](./docs/faq.md)
- [[**API Reference**]](https://ruby.github.io/ruby.wasm/JS.html)
- [[**Complete Examples**]](https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-wasm-wasi/example)
- [[**Community Showcase**]](https://github.com/ruby/ruby.wasm/wiki/Showcase)

## Quick Example: Ruby on browser

Create and save `index.html` page with the following contents:
Expand All @@ -17,7 +25,10 @@ Create and save `index.html` page with the following contents:
<html>
<script src="https://cdn.jsdelivr.net/npm/@ruby/[email protected]/dist/browser.script.iife.js"></script>
<script type="text/ruby">
puts "Hello, world!"
require "js"
puts RUBY_VERSION # => Hello, world! (printed to the browser console)
JS.global[:document].write "Hello, world!"
</script>
</html>
```
Expand Down
168 changes: 168 additions & 0 deletions docs/cheat_sheet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
[[**Cheat Sheet**]](./cheat_sheet.md)
[[**FAQ**]](./faq.md)
[[**Complete Examples**]](https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-wasm-wasi/example)
[[**Community Showcase**]](https://github.com/ruby/ruby.wasm/wiki/Showcase)

# ruby.wasm Cheat Sheet

## Node.js

To install the package, install `@ruby/3.2-wasm-wasi` and `@ruby/wasm-wasi` from npm:

```console
npm install --save @ruby/3.2-wasm-wasi @ruby/wasm-wasi
```

Then instantiate a Ruby VM by the following code:

```javascript
import fs from "fs/promises";
import { DefaultRubyVM } from "@ruby/wasm-wasi/dist/node";

const binary = await fs.readFile("./node_modules/@ruby/3.2-wasm-wasi/dist/ruby.wasm");
const module = await WebAssembly.compile(binary);
const { vm } = await DefaultRubyVM(module);
vm.eval(`puts "hello world"`);
```

Then run the example code with `--experimental-wasi-unstable-preview1` flag to enable WASI support:

```console
$ node --experimental-wasi-unstable-preview1 index.mjs
```

## Browser

The easiest way to run Ruby on browser is to use `browser.script.iife.js` script from CDN:

```html
<html>
<script src="https://cdn.jsdelivr.net/npm/@ruby/[email protected]/dist/browser.script.iife.js"></script>
<script type="text/ruby">
require "js"
JS.global[:document].write "Hello, world!"
</script>
</html>
```

If you want to control Ruby VM from JavaScript, you can use `@ruby/wasm-wasi` package API:

```html
<html>
<script type="module">
import { DefaultRubyVM } from "https://cdn.jsdelivr.net/npm/@ruby/[email protected]/dist/esm/browser.js";
const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/[email protected]/dist/ruby+stdlib.wasm");
const module = await WebAssembly.compileStreaming(response);
const { vm } = await DefaultRubyVM(module);
vm.eval(`
require "js"
JS.global[:document].write "Hello, world!"
`);
</script>
</html>
```

## Use JavaScript from Ruby

### Get/set JavaScript variables from Ruby

```ruby
require "js"

document = JS.global[:document]
document[:title] = "Hello, world!"
```

### Call JavaScript methods from Ruby

```ruby
require "js"

JS.global[:document].createElement("div")

JS.global[:document].call(:createElement, "div".to_js) # same as above
```

### Pass Ruby `Proc` to JavaScript (Callback to Ruby)

```ruby
require "js"

JS.global.setTimeout(proc { puts "Hello, world!" }, 1000)

input = JS.global[:document].querySelector("input")
input.addEventListener("change") do |event|
puts event[:target][:value].to_s
end
```

### `await` JavaScript `Promise` from Ruby

```html
<html>
<script src="https://cdn.jsdelivr.net/npm/@ruby/[email protected]/dist/browser.script.iife.js"></script>
<script type="text/ruby" data-eval="async">
require "js"
response = JS.global.fetch("https://www.ruby-lang.org/").await
puts response[:status]
</script>
</html>
```

Or using `@ruby/wasm-wasi` package API:

```html
<html>
<script type="module">
import { DefaultRubyVM } from "https://cdn.jsdelivr.net/npm/@ruby/[email protected]/dist/esm/browser.js";
const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/[email protected]/dist/ruby+stdlib.wasm");
const module = await WebAssembly.compileStreaming(response);
const { vm } = await DefaultRubyVM(module);
vm.evalAsync(`
require "js"
response = JS.global.fetch("https://www.ruby-lang.org/").await
puts response[:status]
`);
</script>
</html>
```

### `new` JavaScript instance from Ruby

```ruby
require "js"

JS.global[:Date].new(2000, 9, 13)
```

### Convert returned JavaScript `String` value to Ruby `String`

```ruby
require "js"

title = JS.global[:document].title # => JS::Object("Hello, world!")
title.to_s # => "Hello, world!"
```

### Convert JavaScript `Boolean` value to Ruby `true`/`false`

```ruby
require "js"

JS.global[:document].hasFocus? # => true
JS.global[:document].hasFocus # => JS::Object(true)
```

### Convert JavaScript `Number` value to Ruby `Integer`/`Float`

```ruby
require "js"

rand = JS.global[:Math].random # JS::Object(0.123456789)
rand.to_i # => 0
rand.to_f # => 0.123456789
```
25 changes: 25 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[[**Cheat Sheet**]](./cheat_sheet.md)
[[**FAQ**]](./faq.md)
[[**API Reference**]](https://ruby.github.io/ruby.wasm/JS.html)
[[**Complete Examples**]](https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-wasm-wasi/example)
[[**Community Showcase**]](https://github.com/ruby/ruby.wasm/wiki/Showcase)

# FAQ

## Where my `puts` output goes?

By default, `puts` output goes to `STDOUT` which is a JavaScript `console.log` function. You can override it by setting `$stdout` to a Ruby object which has `write` method.

```ruby
$stdout = Object.new.tap do |obj|
def obj.write(str)
JS.global[:document].write(str)
end
end

puts "Hello, world!" # => Prints "Hello, world!" to the HTML document
```

## How to run WebAssembly in Ruby

Use [`wasmtime` Ruby gem](https://rubygems.org/gems/wasmtime).

0 comments on commit 8532165

Please sign in to comment.