ruby.wasmでJavaScriptのオブジェクトをnewメソッドで初期化する - @ledsun blogで、JS::Object#newを実装しました。 これを Create a JavaScript object with the new method. by ledsun · Pull Request #246 · ruby/ruby.wasm · GitHub にしました。
マージするまでに学びがあったので、感想戦を行って記憶に定着させます。
修正内容
new
メソッドを定義Reflect.construct
メソッドを使うto_js
をデータクラスの責務とする
以上の修正で「JS::Object#new
という1行メソッドを実装するだけ」という、やりたい事をまんま実装するだけというシンプルな拡張になりました。
newメソッドを定義する
method_missing
の中でメソッド名で分岐していました。
def method_missing(sym, *args, &block) if sym == :new # new で呼び出されたら、コンストラクタとして呼び出す。 JS.eval("return #{self.to_construct(args)}") elsif self[sym].typeof == "function" # 関数として定義されていたら、関数として呼び出す。 self.call(sym, *args, &block) else super end end
単にnewメソッドをとして定義すれば良いのでした。
def new(*args) JS.eval("return #{self.to_construct(args)}") end
条件分岐が減ってシンプルになりました。
Reflect.constructメソッドをつかう
JavaScriptのnew構文を作ってインスタンス生成していました。
JS.eval("return 'new URLSearchParams()'")
JavaScriptのReflect.construct
関数*1をつかうと、new構文と同じことが出来ます。
JS.global[:Reflect].construct(self, args.to_js)
これには2つのメリットがあります。
- 関数オブジェクトから関数名をとる処理が不要
- 関数名を持たない無名関数からもインスタンスか生成出来る
to_jsの実装
Reflect.construct
はJavaScriptの配列を引数にとります。
コンストラクタの引数を指定するには、Rubyの配列をJavaScriptに配列に変換する必要があります。
変換用のクラスを作りました。 実装は少し長いので、興味のある人だけリンク先をご覧ください。 https://github.com/ledsun/ruby.wasm/blob/2e24c567f2adddd540832fbc1e00141aa51669d6/ext/js/lib/js.rb#L166-L201
Array#to_js
メソッドを定義することにしました。
class Array # Convert Ruby array to JavaScript array def to_js new_array = JS.eval("return []") self.each do |element| # NOTE: This method call implicitly convert element to JS object by to_js new_array.push element end new_array end end
各データクラスがJavaScriptのオブジェクトを返す責務果たせば良いのでした。
まとめ
- before: https://github.com/ruby/ruby.wasm/pull/246/commits/16e72653e0cb6b94c363db748bcc5baf7a87f74c
- after: https://github.com/ruby/ruby.wasm/pull/246/files
見比べると、なかなか興味深いです。
- 変更行数はほとんど変わりません。
- 変更ファイル数は2 → 9で大きく増えています。
ふりかえってみると「既存コードへの理解がすくなく、どこを変更したらいいのかよくわかっていなかった」ように思えます。 おそらく「知っているファイルだけで修正を済ませたい」心理が働いて良そうです。 「大多数の知らないファイルについて調べないで済ませる」省エネ発想だと思います。
必ずしも悪い発想ではないと思います。 未知の物を完全に理解するのは時間がかかります。 やりたい事と動くことを示して、アドバイスをもらう方が早く進められそうです。
仕事でやるなら「事前に相談して設計方針を固めてから進める」方が早いです。 けど、それはそれでハッキングぽくなくて楽しくないですよね。
とりあえず今回修正したファイル群の理解は進みました。 次以降の修正の参考になると期待しています。
*1:JavaScriptに、こんな便利な関数があるなんて知りませんでした。