OpalによるJavaScriptとRubyの連携

OpalはRubyコードの中にJavaScriptを埋め込むことができ、微妙に融合・連携させることができる。ただ、JavaScriptを書かずにOpalをわざわざ使う人がいるとしたら、それはおそらくJavaScriptを書きたくないというのが理由であって、Opalで書けるからと言ってJavaScriptを混ぜ込んでコードを書いていたら何のためにOpalを使っているのかわからなくなる。このへんは本家RubyとC拡張ライブラリの関係と似ていて、Cを書くのは一部のライブラリ作者だけでよいのと同様に、JavaScript混在コードを書くのは本来であればOpal用ライブラリを作る人だけでよいはずだ。
俺はOpalを使ってRubyでゲームを作る、ではなく、Opalを使ってRubyだけでゲームを作れる環境を構築する、という立場なので、必然的にJavaScript混在コードの書き方から入る必要がある。
今回はこのへんの癖というかOpalの作りというか、まあ、そのあたりについて考える。

どのように表現されているか

JavaScriptは素人なのでよくわかっていないのだが、基本的にはRubyオブジェクトはJavaScriptオブジェクトで表現されている。インスタンス変数は@を外した名称のプロパティが定義され、メソッドは$を追加した名称で定義されるようだ。ローカル変数は共有。従って、Rubyのローカル変数にJavaScriptのオブジェクトを格納することはできるが、RubyからJavaScriptオブジェクトのメソッドを呼び出すことはそのままではできない。プロパティもJavaScriptオブジェクトをselfにすることができないので参照できない。
Ruby上のスコープや継承、ブロック渡しなどは巧妙に細工された構造によって作り出されていて、このへんはJavaScript混在コードを書く上では必要無いと思うので特に調べない。

JavaScriptからRubyメソッドを呼ぶ

Opal独自のnative_aliasを使うとRubyメソッドから$をはずした名称のメソッドを定義してくれるようで、これを使うとJavaScriptライブラリからのコールバックをRubyで記述することができそうだ。
それ以外に、$付きのメソッドを直接呼び出すことも可能だ。TryOpalでコードを書いてみると簡単に実験できる。

RubyからJavaScriptのメソッドを呼ぶ

$付きのメソッドをJavaScript側で作ってやれば、それをRubyから呼ぶことはできる。
また、NativeメソッドでJavaScriptオブジェクトをRubyオブジェクト化できるので、これに対しては$なしのメソッドをRubyから呼ぶことができる。
しかしいちいちNativeするのは面倒だし、Nativeオブジェクトに対する呼び出しはmethod_missing経由になるようなので速度が期待できない。

実験

たとえばdocumentオブジェクトのcreateElementでcanvasを作りたい、とする。普通に考えたら`document.createElement("canvas")`のようにJavaScript埋め込みで書くことになるが、これをNativeしてRubyから使うか、それ以後のメソッド呼び出しもJavaScript埋め込みで書くかの2択になる。
OpalなのにJavaScript書くのかよと思うし、Nativeあんまり使いたくないなあとも思うが、だからと言ってdocumentやcanvasをラップしたRubyのクラスを地道に作るのも面倒である。
そこで、documentのcreateElementを取得して、$createElementと言う名前でメソッドを生やしてやることで、Rubyから呼べるようにしてみる。
やってみた。
返ってきたcanvasのメソッドを呼ぶこともできると嬉しいのでHTMLCanvasElementにもメソッドを生やしてみる。こんな感じ。
でもうまく行かない。素人だから理解できてないのか。

おしまい

まあ、全メソッド地道に書いていてはキリがないので適当に定義メソッドを引っこ抜いて$付けて再定義するようなコードを書けば、一通りのメソッドが使えるようになるのではないかという気もするのだが、なんせ膨大なメソッドがあるので実行に時間がかかりそうである。
それと、この方法ではメソッドの再定義はできるがプロパティができないので、そっちのほうも考える必要がある。
最後に、なんでこういうことを考えているかというと、既存のJavaScript用ゲームライブラリをOpalから簡単に使う手段になりそうだからである。

追記

canvas.widthとheightはプロパティだからこの方法だとうまくいかないのではないかと思った。