gerry++
Test::HTML::Differences
CPAN にあげました。前回書いたときのは、衝動的に書いて結構挙動が微妙だったので、ちょっとなおしました。
というのも、構造的にだいたい一緒の部分が diff として出てきても鬱陶しいだけなので、比較時にはインデントをせず比較して、表示するときにはインデントして構造がわかりやすくなるようにしました。つまり以下のようになります。
# Failed test at t/03_output.t line 47. # +----+-----------------------+----+-------------------------+ # | Elt|Got | Elt|Expected | # +----+-----------------------+----+-------------------------+ # | | * 0|<foo> * # | 0|<div class="section"> | 1| <div class="section"> | # | 1| foo | 2| foo | # | 2| <a href="/">foo</a> | 3| <a href="/">foo</a> | # | 3|</div> | 4| </div> | # | | * 5|</foo> * # +----+-----------------------+----+-------------------------+
インデント量が違う部分も、変化無しとして検出されます。
あるいは
# Failed test at t/03_output.t line 47. # +----+-------------------------+----+-------------------------+ # | Elt|Got | Elt|Expected | # +----+-------------------------+----+-------------------------+ # | 0|<div class="section"> | 0|<div class="section"> | # | 1| <div class="section"> | 1| <div class="section"> | # | 2| <p>foo</p> | 2| <p>foo</p> | # | 3| </div> | 3| </div> | # | | * 4|</div> * # | 4| <div class="section"> | 5|<div class="section"> | # | 5| <p>foo</p> | 6| <p>foo</p> | # * 6| </div> * | | # | 7|</div> | 7|</div> | # +----+-------------------------+----+-------------------------+
とかになります。
また、冒頭のスクリーンショットのように、いろつきに対応しました。これは単に use するときに -color をつけると Test::Differences::Color を使うようになるだけです。
なお、Test::HTML::Differences は HTML の構造をテストするもので、そのテストの書きやすさのために、要素内の始め・終わりの空白類を無視するようになっています。XML 的等価性を検証するテストではなく、あくまでデータの構造テスト用です。つまり、空白を含めた厳密なテストは例えば is とか別の手段でもっと最小限のテストを書くべきです。
gerry++
IE で postMessage 的実装をちゃんとやる
postMessage、便利でいいですね。ドメインを跨いでうまく連携してスクリプトを書けたときの楽しさはパズル的でこのうえない感じであります。しかしながら postMessage は古い IE で動かないので、代替実装が必要です。ググるといっぱい出てきはするのですが、自分の要求にかなったものがなかったのでひとまず書きました。
$ cat /etc/hosts
127.0.0.1 anotherhost
$ git clone git://gist.github.com/701752.git gist-701752
$ cd gist-701752
$ plackup -MPlack::App::Directory -e 'Plack::App::Directory->new({root=>"."})->to_app' -p 5000とかやっておいて、http://localhost:5000/test.html にアクセスするとなんか出るので、ボタンをクリックしたりするとどうにかなります。フレームの中と外で別ドメインです。
要件
実装するにあたって必要だったのは、まさに postMessage だったので、以下のようなことが求められました。
- メッセージのとりこぼしがない
- IE で動く
逆に、古いブラウザで動けばいいので速度はそれほど求められていません。
実装
とりあえず動いてほしいので、古くから伝わり慣れている location.hash によるドメイン間通信を用いることにしました。location.hash による通信を要約すると以下の通りです。
- データを送信したいフレームの location.hash を書きかえる
- 書きかえるだけならドメイン関係なくできます
- 読みだし側は location.hash を監視して変化を検知したら処理する
ただ、これには以下のような問題があります。
- IE の URL 長制限。
- 2038 バイト制限が存在します。
- location.hash を監視する以上の速度でデータを送信することができない
- 監視に使用するのは setTimeout/setInterval なので、監視間隔は厳密には不定です。
なので、2038バイト制限を超えるデータを送信するためにはデータのパケット化が必要なのと、データの紛失を防ぐために何らかの方法が必要ですが、要はパケロスする環境で信頼できるデータ通信を行いたいということなので TCP のやりかたをパクって実装してあります (シーケンス番号 + ACK)。
備考
window.name を使う方法であればデータ長制限がなくなるのでパフォーマンスは向上しそうですが、実装は複雑そうです。また、空のリソースを両方のドメインに求めるので、失敗確立があがります。
ちゃんとした postMessage 互換実装があったら教えてください……
Test-HTML-Differences
というのを書いてみました。
一旦 normalize してから HTML を比較し、テストに失敗した場合は Test::Differences 使ったときのようになります。
# Failed test 'test' # at t/lib/Text/Xatena/Test.pm line 40. # +----+---------------------------+---------------------------+ # | Elt|Got |Expected | # +----+---------------------------+---------------------------+ # | 0|<root> |<root> | # | 1| <div class="section"> | <div class="section"> | # | 2| <h3>head</h3> | <h3>head</h3> | # | 3| <p>foobar</p> | <p>foobar</p> | # | 4| <div class="seemore"> | <div class="seemore"> | # | 5| <p>barbaz</p> | <p>barbaz</p> | # | 6| </div> | </div> | # * 7| </div> | <div class="section"> * # * 8| <div class="section"> | <h3>head</h3> * # * 9| <h3>head</h3> | <p>foo</p> * # * 10| <p>foo</p> | </div> * # | 11| </div> | </div> | # | 12|</root> |</root> | # +----+---------------------------+---------------------------+
gerry++
Titanium Mobile (iPhone) で、アプリの WebView 内HTMLの特定スキームをハンドリングするテクニック
ローカルHTMLや、直接HTMLを書く場合、Titanium オブジェクトがHTML側から可視なので WebView 内部と外部のコミュニケーションは別段難しくない (fireEvent したら良い) のだけれど、リモートHTML (自分のウェブサービスと協調させたいときとか) の場合はそうはいかないので、別の方法が必要。
ということでカスタムスキームは使えないのか? と思ったのだけれど、それらしいAPIはない。こりゃカスタムスキームのハンドリングはできないな、と思いきや別の方法により可能であることがわかった。
前置きともかく以下のようにするとできる。ここではカスタムスキーム名を custom: としている。
var webview = Titanium.UI.createWebView();
// ローカルでもリモートでもどっちでも良い
webview.url = 'http://localhost:5000/sketch.html';
//webview.html = '<!DOCTYPE html><html><body>' +
// '<p><a href="custom:foobar">custom scheme</a></p>' +
//'</body></html>';
webview.addEventListener('error', function (e) {
// "Error Domain=WebKitErrorDomain Code=101 \"The URL can\U2019t be shown\" UserInfo=0x6872120 {NSErrorFailingURLKey=custom:foobar, NSErrorFailingURLStringKey=custom:foobar, NSLocalizedDescription=The URL can\U2019t be shown}"
if (/NSErrorFailingURLKey=custom:(\S+), /.test(e.message)) {
var data = RegExp.$1;
alert(data);
}
});
Titanium.UI.currentWindow.add(webview);コードの通りですが、WebView はハンドリングできないエラーがあると error イベントを発火するので、それをキャッチして処理してやるだけです。なので存在しないスキームしかハンドリングできないです。iPhone だと遷移も発生しないので割といい感じです。
なんか根本的に解決する方法があれば良いのですが、あれば教えて頂けると嬉しいです。
自動CPAN祭 AutoInstall.pm
モジュールが見付からなかったとき自動的に CPAN install するのはなんかあるんですが上手く動いてくれなかったので簡単なのを書きました。最近 perl ごと入れなおすハメになって面倒だったのですがだいぶ楽にいけました。
PERL5LIB パスが通ったディレクトリに以下のスクリプトを置いて、perl -MAutoInstall foobar.pl とかでいけるので便利です。
なんかもっと確実で、良い方法があったらいいのですが、知らないので困った感じです。
Snow Leopard / Lion にしたら vim の中から起動する perl がシステムデフォルトの perl に固定されてしまった。
Snow Leopard が余計なことをするようになった。なんとなく "vim path /usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin" とかでググっていたら、http://d.hatena.ne.jp/yhino/20100908/1283963661 というエントリがひっかかってくれた。
/etc/profile とか /etc/zshenv とかで /usr/libexec/path_helper というプログラムを実行してシステムのパスを先頭に追加するとかしているので、vim のような環境で、~/.zsrhrc が呼ばれない場合、システム側のしか呼ばれなくなっている。本来 PATH 通しとかは ~/.zshenv に書くべきなので、自分も悪いのだけれど、PATH の管理ぐらい自分でできるし、余計なことをしてほしくないので以下のようにした
$ lm /usr/libexec/path_helper -r-xr-xr-x 1 root wheel 50K 5 19 2009 /usr/libexec/path_helper* $ sudo chmod -x /usr/libexec/path_helper $ lm /usr/libexec/path_helper -r--r--r-- 1 root wheel 50K 5 19 2009 /usr/libexec/path_helper
単に実行権限をなくした。
