💎

Ruby WebAssemblyをさわってみた

2022/10/27に公開

https://rubykaigi.org/2022/presentations/kateinoigakukun.html#day1

Ruby が ブラウザで動く!
ということで、せっかくなので自分でもさわってみたい!ということで、
今までWasmを全然しらなかったのですが、色々調べながらさわってみました。

そもそも Wasm とは?

WebAssembly の略称。
モダンなウェブブラウザーで動作し、新たな機能と大幅なパフォーマンス向上を提供する新しい種類のコード。
基本的に直接記述ではなく、C、C++、Rust 等の低水準の言語にとって効果的なコンパイル対象となるように設計されているとのこと。

[参考] https://developer.mozilla.org/ja/docs/WebAssembly/Concepts

Wasm の特徴

JavaScriptに比べて高速で、高効率である

インタプリタ型のJavaScriptと比べると、コンパイル済みのバイナリコードを利用するので計算処理は早い。
(ただし完全に同じことができるわけではないので、JavaScriptに置き換わるわけでもない)

プラットフォームに依存せず、ブラウザさえ動けばどこでも動く

例えばRubyのプログラムを動かすためにはどうしてもRubyをインストールする必要があるが、
WebAssembly形式であれば環境構築が不要!

とりあえず試してみる

https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-wasm-wasi/example

こちらのレポジトリにすべてが詰まっているので、クローンすればOK
index.htmlを簡単に準備して、

index.html
<html lang="ja">
  <head>
    <title>RubyWasm</title>
    <meta charset="utf-8"/>
  </head>
  <script src="https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@0.3.0-2022-09-06-f/dist/browser.script.iife.js"></script>
  <body>
    <div id="content">
      <button id="button">ボタン</button>
      <input type="text" id="input"></input>
      <div id="message"></div>
    </div>
  </body>
  <script type="text/ruby" charset="UTF-8">
    // ここにrubyコードが書ける!
  </script>
</html>

webrickでサーバーを立てれば準備完了!

console
ruby -run -e httpd . -p 8000

というわけで早速scriptの中身を書いてみます。

script
p "Hello World!"

hoge = [1,2,3].sample
p hoge

[実行結果]

おー ちゃんとputsされていますね。

さらにJavaScriptを使うこともできます。
require 'js' で JavaScriptを呼んでみます。

script
  require "js"
  document = JS.global[:document]
  content = document.getElementById 'content'
  text = ["Hello World!", "hoge", "fuga"].sample
  content[:innerText] = text

[実行結果]

ちゃんとDOMを書き換えられてますね!
こんな感じでJavaScriptのAPIを使うことができちゃいます。
EventListenerも受け取れるので、こんな感じでボタンクリックをブロックで受け取って
DOMを操作するなんてこともできます。

script
  require "js"

  document = JS.global[:document]
  button = document.getElementById 'button'
  message = document.getElementById 'message'

  button.addEventListener "click" do |e|
    message[:innerText] = Time.now.to_s
  end

[実行結果]

Rubyっぽくていいですね!

puts や sample といった標準メソッドを使えるのはもちろん、
なんとメソッドを定義することもできます!

というわけで、メールアドレスの判定メソッドを定義して、
フォームに入力された値の形式が正しいかどうかを判定してみます。

script
  def validate_email(text,error)
    regexp = text.to_s.match(/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i)
    error[:innerText] =
      if regexp
        "OK"
      else
        "形式が不正です。Emailの形式で入力してください。"
      end
  end

  require "js"

  document = JS.global[:document]
  input = document.getElementById 'input'
  error = document.getElementById 'message'

  input.addEventListener "change" do |e|
    validate_email(e[:target][:value],error)
  end

[実行結果]

よくJavaScriptで作るやつですが、Rubyで書けるのは嬉しいですね。
引数にDOMを受け取ることもできるので、メソッド内でDOMの書き換えをしたりもできます。

まぁこれぐらいの簡単なものであれば、Wasmの読み込みの分を考えるとJavaScriptで作ったほうが効率的ですね・・
もっと複雑な計算になってくるとどうなのでしょう。

RubyKaigi2022のkateiさんさんのkeynoteでは、現状のCRubyから比べると遅いものの、MRubyと比べると同じぐらいの速さにはなっているとのことでした。

次回はもう少し複雑な計算をやってみたいと思います!

Discussion