NSString、NSArray、NSDictionary で Ruby のメソッドを使えるようにした

RubyCocoa 0.13.0 の新機能について。

いままでの RubyCocoa では、テキストフィールド上の文字列を操作するために、

s = @textField.stringValue  # NSString を取得
s = s.to_s                  # いったん Ruby String に変換
s.gsub!(/\s/, '')           # s をいろいろ操作
@textField.stringValue = s  # NSString に自動変換して代入

のように書いていた。

これには、Cocoa の NSString がわりとシンプルで、それほど機能が多くないので、いったん Ruby の String にしたほうが楽に操作できるからという理由があった。(たとえば NSString には正規表現がない)

これを解決するために、String、Array、Hash のメソッドを NSString、NSArray、NSDictionary に全部移植することにした。そうすることで、Ruby の便利なメソッドをそれらのクラスでも使えて、さらに変換の手間やコストも省けるという一石二鳥になる。

Array と Hash については、すんなりと完了。

String については、Ruby の String が byte index なのに対して、NSString は character index という違いはあるが、途中まではスムーズに進んだ。ここらへんは、ruby 1.9 の m17n の動きと JRuby が参考になった。

問題は正規表現ruby 1.8 内蔵の正規表現UTF-16 を扱えないので、マッチした結果を character index で返すことができない。これができないと、=~ と match を実装することができない。

そこで、Oniguruma を取り込み、UTF-16 でマッチして character index を返すところまで作ってみたけど、$~ に MatchData クラス以外のオブジェクトを代入できないことがわかって挫折。MatchData は ruby の内部でしか作れないように、継承しても new できないようになっているらしい。

そうすると、残る方法は ruby 内蔵の正規表現でマッチして、返ってきた MatchData の全メソッドを特異メソッドで上書きして byte index → character index の変換をするくらいだろうけど、この方法はさすがに効率が悪そうなのであきらめることに。ruby 1.9 では、うまく解決できるだろう。