@ledsun blog

無味の味は佳境に入らざればすなわち知れず

2023年にruby.wasmにマージできたプルリクエスト

https://github.com/ruby/ruby.wasm/pulls?q=+author%3Aledsun+is%3Amerged+created%3A%3E2023-01-01 で一覧できます。 6件でした。 時系列で見ていきます。


github.com

JavaScriptのオブジェクトを、newメソッドで作れるようにしました。 それまでは、次のようにJavaScript側でnewメソッドを呼ぶ必要がありました。

JS.eval 'return new URLSearchParams(location.search)'

次のようにRubyの中でnewメソッドを呼べるようにしました。

JS.global[:URLSearchParams].new(JS.global[:location][:search])

github.com

JavaScriptのtrue, falseとRubyのtrue, falseは異なるオブジェクトです。 ですので、次の比較は常にfalseになります。

if searchParams.has('phrase') == true
  ...
end

これを簡単に書くために、JavaScriptのtrueを定数にしました。 次のように書けます。

if searchParams.has('phrase') == JS::True
  ...
end

github.com

WebAssembly.complieStreamingというwasmバイナリのダウンロードとコンパイルを同時に実行するAPIを使うようにしました。 40ms速くなりました。 ダウンロードの方が400msぐらい掛かって支配的です。 今のところはそんなに効果はないですが、将来効果が大きくなるといいですね。


github.com

ruby.wasmはビルドの途中でzlibのソースコードをダウンロードします。 zlib 1.3がリリースされたときに、旧バージョンのソースコードがダウンロードできなくなりました。 ruby.wasmのビルドも通らなくなったので、その修正です。


github.com

Kernel#require_relativeをパッチするためのメソッドとして、RequireRemote#loadを追加しました。 自作のRubyスクリプトを読み込むだけなら次のパッチで行けます。

require 'js/require_remote'

module Kernel
  def require_relative(path) = JS::RequireRemote.instance.load(path)
end

これだけだと組み込みgemでつかっているrequirue_relativeが壊れます。 もうちょっと複雑なパッチが必要です。 ruby.wasm/packages/npm-packages/ruby-wasm-wasi/example/require_relative/index.html at 6acf40356079463b9b8d545031e8562a9b3931d2 · ruby/ruby.wasm · GitHub を見てください。

require_remoteをruby.wasmのbundled gemにする計画があります。 そうしたら、このパッチもgemに入れてもいいのかもしれません。 まあ、もう少し先の話なので、一先ずはRequireRemote#loadを組み込んだ自作のアプリケーションを作ります。


github.com

ruby.wasmのnpmパッケージの名称が変わりました。 そのときのテストコードの修正漏れの対応です。

相対URLの解決

URL: URL() コンストラクター - Web API | MDN を使うと、基準になるURLからの相対パスを解決したURLが得られます。 例えば、次のように使います。

// ベース URL:
let baseUrl = "https://developer.mozilla.org";

new URL("ja/docs", baseUrl);
// => 'https://developer.mozilla.org/ja/docs'

このコンストラクターの挙動を試しているときに、次の例を考えました。

new URL('a.rb', 'http://exapmle.com/lib').toString()
// => 'http://exapmle.com/a.rb'

このとき http://exapmle.com/lib/a.rb となって、libディレクトリの中を参照して欲しいのではないでしょうか?

これはJavaScript特有の動作なのでしょうか?

Rubyでも試してみました。 RubyではURLの結合には URI.join を使います。

require 'uri'

URI.join(URI.parse('http://exapmle.com/lib'), 'a.rb')
# => #<URI::HTTP http://exapmle.com/a.rb>

やはり lib が消えます。 統一された動作です。 もしかしてこれはどこかで決まっているのでしょうか?

るりまに以下の説明がありました。

[RFC2396] の Section 5.2 の仕様に従って連結します。

というわけでRFCを見てます。 https://datatracker.ietf.org/doc/html/rfc2396#autoid-33

6) If this step is reached, then we are resolving a relative-path reference. The relative path needs to be merged with the base URI's path. Although there are many ways to do this, we will describe a simple method using a separate string buffer.

 a) All but the last segment of the base URI's path component is
    copied to the buffer.  In other words, any characters after the
    last (right-most) slash character, if any, are excluded.

base URIの最後のスラッシュ以降に何かあれば、その部分はバッファ(解決後のURLを結合するための場所)に入れないそうです。 なるほど、JavaScriptRubyもこの動きをしていそうです。

この記事を書いている途中で気がつきました。 URLは文字列で、ファイルシステムではありません。 http://exapmle.com/lib がファイルかディレクトリかという区別は、文字列から読み取るしかありません。 すると

  • /で終わるのが、ディレクト
  • /の後ろに文字列が続いていたらファイル

みたいな、単純な方法で区別するしかない気がしてきました。 libディレクトリに見えるのは、背景知識があるから人間に判別できているっぽいです。

RubyJavaScript、ファイルとURLの間を行ったり来たりしていると、自分がどこにいるのか、よく見失います。

Playwrightでリダイレクト後のHTTPリクエストをMockできない

ruby.wasmのテストコードを書いていました。

PlaywrightでHTTPリクエストをMockしているスクリーンショット

cdn.jsdriver.netへのリクエストをMockして、レスポンスの内容をローカルファイルに置き換えています。

リダイレクトしたあとはMockできていないスクリーンショット

前述のスクリーンショットと、同じURLに対するリクエストですが、302レスポンスでリダイレクトしたあとはMockできません。

調べてたら、次のコメントを発見しました。

tests for request event and interception with redirects by tjenkinson · Pull Request #3994 · microsoft/playwright · GitHub

The network interception in Playwright is implemented on the Browser -> Network stack boundary. Once the request is in the network stack, it is going to handle the redirects and report them, but not allow intercepting them.

Playwrightが割り込んでいるのは、ブラウザとネットワークスタックの間だそうです。

fetchメソッドはデフォルトで、リダイレクトレスポンスを自動的に追いかけます。 なるほど!この動きはネットワークスタックに含まれていそうです。

ruby.wasmのビルドの並列オプションの効果

ruby.wasmのコミットのなかで、make を並列実行する -j オプションを追加するコミットがありました。

build: use -jN option for "make install" in crossruby · ruby/ruby.wasm@02e84be · GitHub

本当に並列化されているのか観察してみました。

Rubyをビルドしてるときのスクリーンショット

RubyをビルドしているときはCPUが全部動いています。 並列化が効いているようです。

OpenSSLをビルドしてるときのスクリーンショット

OpenSSLをビルドするときはCPUは大分休んでいます。 何かしら理由があって1つだけに絞っているようです。 https://github.com/ruby/ruby.wasm/blob/3122466a0f12e97ec453f70949b5c0117f92926c/lib/ruby_wasm/build/product/openssl.rb#L83 で明示的に1を指定しています。

wasmをビルドしてるときのスクリーンショット

wasmをビルドしているときもCPUが全部動いています。 並列化が効いているようです。

全部で830秒、13分でした。 コアの沢山あるCPUでビルドするともっと速くなるのでしょうか?興味深いです。

Playwrightでリダイレクトの動作をテストで確認する

PlaywrightのNewwork mockingをつかってみる - @ledsun blog ではテスト用のUIを使ってリダイレクトの動作を確認しました。 テストコードで確認してみましょう。

import { test, expect } from '@playwright/test';

test.beforeEach(async ({ context }) => {
  await context.route(/redirect_to/, route => route.fulfill({
    status: 301,
    headers: {
      location: 'https://playwright.dev/'
    }
  }));
});

test('has title', async ({ page }) => {
  await page.goto('https://playwright.dev/');

  const url = await page.evaluate(async () => {
    const response = await fetch('https://example.com/redirect_to')
    return response.url
  })

  expect(url).toBe('https://playwright.dev/')
});

page.evaluateをつかって、ページ内でJavaScriptを実行します。 fetch('https://example.com/redirect_to')を実行して、Response#urlがリダレクト先のURLになっているか確認します。

前回と比べて次の点が変わっています

  • 意図しないファイルをリダイレクトしないように、リダイレクト用のキーワードをredirect_toに変更
  • httpsのページからfetchするために、fetchの宛先をhttpsに変更

Playwrightでローカルファイルを読み込む

次のようなindex.htmlを用意します。

<html>
  <title>Playwright</title>
</html>

テストは次のように書きます。

import path from 'path';
import { test, expect } from '@playwright/test';

test('has title', async ({ page }) => {
  const htmlPath = `file://${path.join(__dirname, `./index.html`)}`;
  await page.goto(htmlPath);

  // Expect a title "to contain" a substring.
  await expect(page).toHaveTitle(/Playwright/);
});

page.goto の引数に file:// で始まるファイルのパスを指定します。 index.htmlのtitleタグの要素を変えると、テストが失敗します。 期待通りにローカルファイルを読み込めているようです。

参考

Playwrightでローカルファイルを読み込む #TypeScript - Qiita

Amazon Linuxに関する調査メモ

Amazon Linux 2では、Node.jsの新しいバージョンをインストールできなかったり、少しずつ時代遅れ感が出ています。 では、「Amazon Linux 2023にすればいいのか?」というと、どうもそう簡単ではないようです。

これは、アップデートに挑戦して苦戦した話ではなく、アップデートするまえにどんな感じなのか軽く調べてみたメモです。

Amazon Linux 2023を触ってみて質問がありそうなことをまとめてみました。 | ソフトウェア開発のギークフィード

Fedoraの複数バージョンのコンポーネント+アルファをAWSがメンテして一般公開版を出したということになりますので、独自ディストリビューションの公開と維持に、よっぽどの稼働とコストがかかっている

自分の場合は、AWS EC2を使ってもありきたりなWebサーバーを起動したいだけです。 頑張って Amazon Linux を使うべきなのでしょうか? RedHat系のディストリビューションに乗り換えた方が良いのではないでしょうか?

今まで、開発環境では Ubuntu/Debian を使って生ききたので、RedHat系のディストリビューションには詳しくありません。 RedHat系の無料ディストリビューションと言えばCentOSですが、来年で終了するようです。

CentOS Linux のサポート終了について | Red Hat

CentOS Linux 7 のサポート期限は、2024 年 6 月 30 日に終了 (EOL) となります。

後継となるディストリビューションには何があるのか調べてみると、Alma LinuxとRocky Linuxがあるようです。 どうやらAlma Linuxの方が、少し人気があるように思えます。 理由は「企業ではなく非営利団体がメンテナンスしているため、急な方針転換が少ないだろう」でした。 では、Alama Linuxを選んでおけば良いかというと・・・

AlmaLinux と Rocky Linux の比較メモ(2023年8月版) | あぱーブログ

2023年6月 Red Hat は git.centos.org リポジトリの更新を停止したため、このリポジトリからソースコードを取得してビルドしていた AlmaLinux や Rocky Linux など、RHELクローンと呼ばれる Linux ディストリビューションは、ソースコードの取得先の変更を求められています。そのため、今後の各社の動きに注目しておく必要があります。

まだもうちょっと様子を見た方が良いようです。 個人的には急いでいないですし、Amazon Linux 2023でもよいので、もう少し様子を見ます。

参考

名古屋に行った

名古屋出張があったので、翌日を休みにして名古屋駅周辺を半日散歩しました。

名古屋といえばナナちゃん人形

大きな教会がありました。

在日大韓基督教教科書という建物らしいです。 サムネイルのモアレがすごいです。

ノリタケの森

無料で入れました。 公園や美術展示がありました。

お昼ご飯も食べました。

食器はノリタケ製です。 200万円するティーセットも売っていました。どんな方が買うんでしょうね?

その他

名古屋のモード学園

新宿のコクーンタワーの兄弟。 こっちもすごいです。

コレクトマーク

ダクトが美しいです。

PlaywrightのNewwork mockingをつかってみる

Playwrightをつかってみる - @ledsun blog でPlaywrightが最低限動く環境ができました。 リダイレクトレスポンスを扱うテストを書こうと思います。

Playwrightには Netwrok mockingという機能があります。 実行するテストからのHTTPリクエストをモックして書き換えることができます。

サンプルのコードは次のように動きます。

sequenceDiagram
    participant Client
    participant Server

    Client->>Server: https://playwright.dev/
    Server->>Client: 200

これにモックを挟んでみます。

sequenceDiagram
    participant Client
    participant Server

    Client->>Server: Request http://exapmle.com/redirect
    Server->>Client: 301
    Client->>Server: https://playwright.dev/
    Server->>Client: 200 OK

最初の宛先を http://exapmle.com/redirect に変更します。 このリクエストをモックして301 レスポンスを返します。 https://playwright.dev/ にリダイレクトします。

test.beforeEach(async ({ context }) => {
  await context.route(/redirect/, route => route.fulfill({
    status: 301,
    headers: {
      location: 'https://playwright.dev/'
    }
  }));
});

リクエストの宛先に redirect を含むときにモックします。 route.fulfill を使ってレスポンスを書き換えます。

テストも次のように書き換えます。

test('has title', async ({ page }) => {
  await page.goto('http://exapmle.com/redirect');

  // Expect a title "to contain" a substring.
  await expect(page).toHaveTitle(/Playwright/);
});

開くURLを http://exapmle.com/redirect に変更します。 テストを実行してみましょう。 npx playwright test --project=chromium --uiでテストを起動します。

PlaywrightのUIを起動した直後のスクリーンショット

次のように選択してテストを実行します。

  • テスト: has title
  • Networkタブ

テスト実行完了後のスクリーンショット

Networkタブをみます。

  1. /redirect にリクエストして 301が返る
  2. / にリクエストする

と、期待通りに動いたことが確認できました。

よく見ると https://playwright.dev/js/redirection.js へのリクエストもリダイレクトされています。

Playwrightをつかってみる

Installation | Playwrightの手順に従って進めます。

npm init playwright@latest

いまはnpm initにプロジェクトテンプレートをつくる機能が組み込まれているようです。

npm-init | npm Docsによると

initializer in this case is an npm package named create-, which will be installed by npm-exec, and then have its main bin executed -- presumably creating or updating package.json and running any other initialization-related operations.

create-<initializer> というパッケージが実行されるそうです。 つまり、create-playwright - npmインストーラーのようです。

これでプロジェクトが作成されます。 次のコマンドで、サンプルのテストコードが実行出来ます。

npx playwright test

僕の環境ではエラーが出ます。

  4 failed
    [firefox] › example.spec.ts:3:5 › has title ────────────────────────────────────────────────────
    [firefox] › example.spec.ts:10:5 › get started link ────────────────────────────────────────────
    [webkit] › example.spec.ts:3:5 › has title ─────────────────────────────────────────────────────
    [webkit] › example.spec.ts:10:5 › get started link ─────────────────────────────────────────────
  2 passed (2.0s)

どうやらブラウザを実行するためのライブラリーが足りないようです。 Chromiumに限定して実行してみましょう。

npx playwright test --project=chromium

成功しました。

Running 2 tests using 2 workers
  2 passed (1.5s)

To open last HTML report run:

  npx playwright show-report

サンプルのテストケースは2つあるようです。 npx playwright show-report コマンドでテスト結果をHTML形式で見れるようです。 成功時はあんまり必要無いですね。 ちなみに、失敗時はデフォルトでHTML形式で表示されます。

リダイレクトレスポンス対応版 require_relativeを組み込んだruby.wasmを実際に動かして、問題を切り分ける

ruby.wasmでrequire_relativeしたときのリダイレクトレスポンスの扱い方 - @ledsun blog に書いたように、HTTPリクエストのリダイレクトレスポンスでURLが変わったら、変更後のURLをロード済みとして扱いたいです。

リダイレクトレスポンス対応版 require_relativeの実装

ruby.wasmのrequire_relativeではfetch APIを使います。 fetch APIからリダイレクト後のURLがとれば良いはずです。

Response.url - Web API | MDN には

url プロパティの値は、あらゆるリダイレクトの後に得られる最終的な URL になります。

と、あります。 これを使えば良さそうです。 次のようなコードを書きました。

response = JS.global.fetch(location.url).await

if response[:status].to_i == 200
  return false if @loaded_urls.include?(response[:url].to_s)

  code = response.text().await.to_s
  eval_code(code, location)

  @loaded_urls << response[:url].to_s
  true
else
  raise LoadError.new "cannot load such url -- #{location.url}"
end

@loaded_urlsresponse.url から取得したURLを保存しておきます。 次のfetchの response.url が、@loaded_urls に含まれていたら、Rubyスクリプトを実行せずにリターンします。 これで良さそうに思います。

実際に動かしてみる

しかし、E2Eテストでは失敗します。 失敗した結果 ruby.wasmのE2Eテストをデバッグする - @ledsun blog をしていました。 また、Response.urlの挙動を確認する - @ledsun blog で、Response.url単体では期待通りに動くことが確認できました。 ということは、ruby.wasmに組み込んでも期待通りに動きそうです。 確かめてみましょう。

次のようなサーバーを用意します。

require 'webrick'

root = File.expand_path 'public' # 静的ファイルが置かれるディレクトリ
server = WEBrick::HTTPServer.new :Port => 3000, :DocumentRoot => root

# リダイレクトの設定
server.mount_proc '/redirect.rb' do |req, res|
  res.status = 302
  res['Location'] = 'app.rb'
end

trap 'INT' do
  server.shutdown
end

server.start

このサーバーはpublicディレクトリにあるファイルを静的配信します。 さらに /redirect.rb にきたリクエストにはリダイレクトレスポンスを返し app.rb に誘導します。

publicディレクトリに、自分でビルドしたruby.wasmを実行する環境をつくる - @ledsun blogの要領で、browser.script.iife.jsruby+stdlib.wsmを用意します。

require_relativeの動作確認する - @ledsun blog の要領でrequire_relativeを使うindex.htmlを用意します。

<html>
  <script src="browser.script.iife.js"></script>
  <script type="text/ruby">
    require 'js/require_remote'

    module Kernel
      alias original_require_relative require_relative

      # The require_relative may be used in the embedded Gem.
      # First try to load from the built-in filesystem, and if that fails,
      # load from the URL.
      def require_relative(path)
        caller_path = caller_locations(1, 1).first.absolute_path || ''
        dir = File.dirname(caller_path)
        file = File.absolute_path(path, dir)

        original_require_relative(file)
      rescue LoadError
        JS::RequireRemote.instance.load(path)
      end
    end
  </script>
  <script type="text/ruby" data-eval="async">
    p require_relative 'redirect'
    p require_relative 'app'
  </script>
</html>

ただし次のようにリダイレクトを交えたrequire_relativeを実行します。

p require_relative 'redirect'
p require_relative 'app'

リダイレクト後の app.rb がロード済みになっているはずです。 その後 app.rb をロードしようとしてもスキップされて欲しいです。 つまり、開発コンソールに、最初にtrueつぎにfalseが表示されてほしいです。 サーバーを起動して動作を確認します。

ruby sever.rb

http://localhost:3000/ を開いて開発コンソールを確認します。

開発コンソールのスクリーンショット

期待通りです。 最初にtrueつぎにfalseが表示されました。

まとめ

わかったことをまとめます。

  1. response.urlはfetchを単体で動かしたときリダイレクト後のURLが取得できる
  2. response.urlをruby.wasm版 require_relative に組み込むと、期待通りに動く
  3. E2Eでは期待通りに動かない
  4. 特にresponse.urlの値がリダイレクト前のまま

以上から、今後はE2Eテストの実行環境を重点的にしらべていくと良さそうです。

ruby.wasmでrequire_relativeしたときのリダイレクトレスポンスの扱い方

require_relativeでリダイレクトレスポンスをサポートしたいと思っています。 たとえば次のようなシナリオです。

  1. require_relative "a" を実行する
  2. fetch('a.rb') を実行する
  3. a.rb にGETリクエストを送る
  4. サーバーから302レスポンスでb.rbが返ってくる
  5. b.rb にGETリクエストを送る
  6. Rubyスクリプトの取得する
  7. Kernel.evalRubyスクリプトを実行する

シーケンス図で次のようになります。

sequenceDiagram
    participant Client
    participant Server

    Client->>Client: require_relative "a"
    Client->>Client: fetch('a.rb') 
    Client->>Server: GET request './a.rb'
    Server->>Client: 302 Response with './b.rb'
    Client->>Server: GET request for './b.rb'
    Server->>Client: Ruby script transfer
    Client->>Client: Kernel.eval Ruby script

4~5のリダイレクトレスポンスに追随してGETリクエストを送る動作はfetch APIがサポートしています。 特に追加で何かする必要はありません。

気にするのは'a.rb'をロードしたつもりが、実際は'b.rb'を読み込んだシチュエーションです。 このあと require_relative "b" を実行したときにRubyスクリプト b.rb を再び実行するかどうかです。

結論としては b.rb を読込済みとし、再び実行しません。 二重読込を防止したいのは、Rubyスクリプトの実体だと考えています。 同じRubyスクリプトを別の名前で require_relaitve したときに、二度実行するのは、ユーザーの意図した動作ではないと考えます。

require_relativeの動作確認する

自分でビルドしたruby.wasmを実行する環境をつくる - @ledsun blog で、ruby.wasmのカスタムビルドを動かせるようになりました。 実装中の require_relative が動くか確かめてみましょう。

次のような index.hml を作成します。

<html>
  <script src="browser.script.iife.js"></script>
  <script type="text/ruby">
    require 'js/require_remote'

    module Kernel
      def require_relative(path) = JS::RequireRemote.instance.load(path)
    end
  </script>
  <script type="text/ruby" data-eval="async">
    require_relative 'app'
  </script>
</html>

現在は、ruby.wasmで Kernel#require_relative 自体を改変しない方針を採っています。 代わりに、ブラウザ上で Kernel#require_relative のように動く、JS::RequireRemote#load *1を作成しています。

ローダー周りのメソッドはパッチを当てられがちです。*2 ユーザーは別の目的で Kernel#require_relative にパッチを当てたいかも知れません。 パッチを当てるかどうかの判断をユーザーに委ねるためです。

require_relativeの読込先として、次のような app.rb を用意します。

puts 'Hello, World!'

前回と同じように Hello, world! が表示されます。

ブラウザの開発コンソールにHello, world!が表示されているスクリーンショット

ちなみに上記の Kenerl#require_relative のパッチは、既存の動作を壊しています。 たとえば、require 'csv'を実行すると次のエラーが起きます。

require 'csv'を実行した時のエラー

csv gemの中のrequire_relative "csv/fields_converter" *3の動作が変わって起きたエラーです。 既存の動作を壊さないさないようにするには、次のように、もう少し複雑なパッチを当てる必要があります。

module Kernel
  alias original_require_relative require_relative

  def require_relative(path)
    caller_path = caller_locations(1, 1).first.absolute_path || ''
    dir = File.dirname(caller_path)
    file = File.absolute_path(path, dir)

    original_require_relative(file)
  rescue LoadError
    JS::RequireRemote.instance.load(path)
  end
end

最初にオリジナルの Kernel#require_relative を実行して、見つからなかったときだけ JS::RequireRemote#load を実行します。

自分でビルドしたruby.wasmを実行する環境をつくる

ブラウザからruby.wasmを実行するときは https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@2.1.0/dist/browser.script.iife.js を使うことが多いです。 browser.script.iife.jshttps://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@2.1.0/dist/ruby+stdlib.wasmをダウンロードして実行します。 便利なスクリプトです。 ただ、自分がビルドしたruby.wasmを動かすには向きません。

そこで browser.script.iife.js をビルドします。 ruby.wasmを取得するコードは https://github.com/ruby/ruby.wasm/blob/b37b90ca139a3c1c1e710faa2f9072ecb750a0b2/packages/npm-packages/ruby-wasm-wasi/src/browser.script.ts#L7-L9 にあります。

  const response = fetch(
    `https://cdn.jsdelivr.net/npm/${pkg.name}@${pkg.version}/dist/ruby+stdlib.wasm`,
  );

任意のパスに書き換えます。 例えば、browser.script.iife.js と同じディレクトリに ruby.wasm を置く場合は、次のように修正します。

  const response = fetch(
    `ruby+stdlib.wasm`,
  );

browser.script.iife.jsをビルドします。

rake npm:ruby-head-wasm-wasi

ruby.wasm自体のビルドが終わっていれば、npmパッケージのビルドだけが実行されます。 30秒くらいで終わります。 packages/npm-packages/ruby-head-wasm-wasi/dist/browser.script.iife.jsruby+stdlib.wasmが出来ます。*1

ledsun@MSI:~/ruby.wasm►ls -la packages/npm-packages/ruby-head-wasm-wasi/dist/
total 104940
drwxr-xr-x 4 ledsun ledsun     4096 Jun 22 23:28 ./
drwxr-xr-x 5 ledsun ledsun     4096 Nov  5 11:23 ../
-rw-r--r-- 1 ledsun ledsun     1067 Nov 14 15:58 LICENSE
-rw-r--r-- 1 ledsun ledsun    51157 Nov 14 15:58 NOTICE
drwxr-xr-x 2 ledsun ledsun     4096 Jun 22 23:28 RubyRunner/
drwxr-xr-x 2 ledsun ledsun     4096 Jun 22 23:28 bindgen/
-rw-r--r-- 1 ledsun ledsun   560499 Nov 14 15:58 browser.cjs.js
-rw-r--r-- 1 ledsun ledsun      267 Nov 14 15:58 browser.d.ts
-rw-r--r-- 1 ledsun ledsun   560301 Nov 14 15:58 browser.esm.js
-rw-r--r-- 1 ledsun ledsun   566241 Nov 14 15:58 browser.script.cjs.js
-rw-r--r-- 1 ledsun ledsun       95 Nov 14 15:58 browser.script.d.ts
-rw-r--r-- 1 ledsun ledsun   566045 Nov 14 15:58 browser.script.esm.js
-rw-r--r-- 1 ledsun ledsun   623166 Nov 14 15:58 browser.script.iife.js
-rw-r--r-- 1 ledsun ledsun   578943 Nov 14 15:58 browser.script.umd.js
-rw-r--r-- 1 ledsun ledsun   566825 Nov 14 15:58 browser.umd.js
-rw-r--r-- 1 ledsun ledsun    46153 Nov 14 15:58 index.cjs.js
-rw-r--r-- 1 ledsun ledsun     6310 Nov 14 15:58 index.d.ts
-rw-r--r-- 1 ledsun ledsun    46083 Nov 14 15:58 index.esm.js
-rw-r--r-- 1 ledsun ledsun    48856 Nov 14 15:58 index.umd.js
-rw-r--r-- 1 ledsun ledsun    46923 Nov 14 15:58 node.cjs.js
-rw-r--r-- 1 ledsun ledsun      248 Nov 14 15:58 node.d.ts
-rw-r--r-- 1 ledsun ledsun    46887 Nov 14 15:58 node.esm.js
-rw-r--r-- 1 ledsun ledsun 34240164 Nov 14 15:58 ruby+stdlib.wasm
-rw-r--r-- 1 ledsun ledsun 53327741 Nov 14 15:58 ruby.debug+stdlib.wasm
-rw-r--r-- 1 ledsun ledsun 15505037 Nov 14 15:58 ruby.wasm

browser.script.iife.jsruby+stdlib.wasmにくわえて、次の index.html を用意します。

<html>
  <script src="browser.script.iife.js"></script>
  <script type="text/ruby">
    puts "Hello, world!"
  </script>
</html>

動かしてみましょう。 上の三つのファイルを置いたたディレクトリでHTTPサーバーを起動します。

ruby -run -e httpd .  

ブラウザで http://localhost:8080/ を開き、開発コンソールを開きます。 次のように Hello, world! が表示されていれば成功です。

ブラウザの開発コンソールにHello, world!が表示されているスクリーンショット

*1: 似た名前のディレクトリにpackages/npm-packages/ruby-wasm-wasi/dist/があります。ここには browser.script.iife.js は含まれません。

ruby.wasmのE2Eテストをデバッグする その2

ruby.wasmのE2Eテストをデバッグする - @ledsun blog で、次のように書きました。

そこで、テストケースで次のようなイベントリスナーを設定します。

page.on("console", (msg) => console.log("LOG:", msg.text()));

ruby.wasmのソースコードをみると元々組み込まれていました。 https://github.com/ruby/ruby.wasm/blob/c5052f289be1d331e340d77f8029fb7f3bc3be24/packages/npm-packages/ruby-wasm-wasi/test-e2e/support.ts#L9-L17

  if (process.env.DEBUG) {
    context.on("request", (request) =>
      console.log(">>", request.method(), request.url()),
    );
    context.on("response", (response) =>
      console.log("<<", response.status(), response.url()),
    );
    context.on("console", (msg) => console.log("LOG:", msg.text()));
  }

環境変数 DEBUG を設定すれば、そのまま使えそうです。 やってみましょう。

つぎのように、環境変数を設定してテストを実行します。

DEBUG=true rake npm:ruby-head-wasm-wasi:check

デバッグログがテスト実行画面に表示されているスクリーンショット

出てきました。 レスポンスステータスや、モックされたURLも表示されて便利です。

よく見ると、不思議な出力があります。

<< 200 https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@latest/dist/redirect_to_error_on_load_twice.rb

テスト用のプロキシサーバーに、次のような301 レスポンスを返しているつもりです。

    } else if (relativePath.match("redirect_to_error_on_load_twice.rb")) {
      route.fulfill({
        status: 301,
        headers: {
          location: "error_on_load_twice.rb",
        },
      });
   }

ログでは、200と表示されています。 リダイレクトが上手く行っていないのでしょうか?

つづくリクエストはリダイレクト先に飛んでいます。 リダイレクトに従っているように見えます。

>> GET https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@latest/dist/error_on_load_twice.rb

謎です。