火曜日, 6月 12, 2007

[JavaScript][Ext]ソート可能なテーブルを実装する

絶対お勧め!JavaScriptでテーブルソート「Table Sorter」


普通のテーブルタグに対して、ヘッダ部分はtheadタグとthタグで作るだけでいい。データ部分は全体をtbodyタグで囲んで、tdタグで記述するだけだ。


Extを使っても同様の方法でソート可能なテーブルを実装できます。
以下のサンプルからExt.grid.TableGridクラスをもらってきて使うだけです。

From Markup Grid Example

Script
var grid = new Ext.grid.TableGrid('the-table');
grid.render();


ロード時にページ内のテーブル全てに適用したければ下記のような感じでOKです。

Ext.onReady(function() {
Ext.select('table').each(function (e) {
var grid = new Ext.grid.TableGrid(e);
grid.render();
} )
});


このGridはかなり高機能で、列クリックによるソートだけではなく、ドラッグによる列の入れ替えや、
ポップアップメニューでの表示列の切り替え、列の固定なども行えます。
インクルードファイルの数やリソースの数など設置は若干大変ですが、それさえ乗り越えればかなり手軽でよいと思います。



参考


火曜日, 5月 22, 2007

[JavaScript][Ext]Ext.jsの使い方メモ(Core機能)

Ext.jsの使い方メモです。Core機能をまとめてみようと思います。

最初に必要なライブラリを読み込みます。(ext-1.0に展開されたソースがある前提)

<script src="ext-1.0/adapter/yui/yui-utilities.js" type="text/javascript" charset="utf-8"></script>
<script src="ext-1.0/adapter/yui/ext-yui-adapter.js" type="text/javascript" charset="utf-8"></script>
<script src="ext-1.0/ext-all-debug.js" type="text/javascript" charset="utf-8"></script>


スタイルシートの読み込みます。
自分で全部定義するのは大変なので付属のものを使います。
<link rel="stylesheet" href="ext-1.0/resources/css/ext-all.css" type="text/css" media="screen" charset="utf-8" />


テーマを変更したい場合は上に加えてythema-xxx.cssを読み込むだけです。
<link rel="stylesheet" href="ext-1.0/resources/css/ythema-aero.css" type="text/css" media="screen" charset="utf-8" />


基本はExt.Elementオブジェクトです。
var elem = Ext.get('my-div');

でExt.Elementオブジェクトが返ってきます。これはHTMLElementオブジェクトをラップしたオブジェクトで、各種プロパティの設定/取得や、アニメーション、AJAX機能が利用できます。

表示非表示の切り替えは、show(), hide()メソッドでそれぞれ行います。
elem.show();
elem.hide();


デフォルトではvisibilityプロパティを変更して表示/非表示を制御します。
要素ブロックごと消したい場合はenableDisplayMode()としてからそれぞれのメソッドを呼び出すとdisplayプロパティで表示/非表示を切り替えてくれます。
elem.enableDisplayMode();
elem.hide();


イベントハンドラの設定

イベントハンドラの設定はExt.Element.onメソッドで行います。これはxt.Element.addListnerのaliasになっています。
elem.on('click', function(event, target) {
// 処理
});

ハンドラの第一引数はExt.EventObject、第2引数はイベントソースのHTMLElementオブジェクトとなります。

アニメーションエフェクト
エフェクト系はExt.Fxで定義されています。
Ext.FxのメソッドはExt.Elementのprototypeにコピー(Ext.apply)されているので、Ext.Elementオブジェクト経由なら特に意識することなく利用できます。

elem.highlight();
elem.fadeOut();
elem.fadeIn();
elem.switchOff();
elem.switchOn();
// etc...


またFx系のメソッドはElementが返ってくるので、でつないでアニメーションチェーンにすることができます。
elem.highlight().fadeOut().fadeIn().switchOff().switchOn();


セレクター
CSSセレクターで要素を選択するにはExt.selectメソッドを使用します。
var elems = Ext.select('.myclass');

戻り値はExt.CompositeElementオブジェクトです。こちらは名前のとおりCompositeパターンに
なっているので、Ext.Elementのメソッドを呼び出すと透過的に全てのオブジェクトに対して
メソッドが呼び出されます。
// myclassクラスが設定されている全ての要素に対してclickイベントハンドラを設定
elems.on('click', function() { ... } );


要素を一つずつ処理する場合はeachを使います。途中でbreakしたい場合はfalseを返します。
continueは単にreturnです。
elems.each(function (elem) {
if ( condition ) {
// ... なんらかの処理
return false; // breakする
} else {
// ... なんらかの処理
return; // continueする
};
// ...
});


XPathセレクター

XPathで要素を選択するにはExt.queryメソッドを使います。こちらはDomQuery.selectのaliasです。
こちらの戻り値はExt.CompositeElementではなくHTMLElementのArrayオブジェクトとなります。
おそらくExt.queryの実装であるDomQueryクラスがExtに依存しないようにするためだと思われます。

Ext.Elementオブジェクトに対してselect, queryを行うと、その要素の子要素以下に限定して
要素を選択します。
elem.select('.myclass') // same as '#' + elem.dom.id + ' .myclass';
elem.query('.myclass') // same as '#' + elem.dom.id + ' .myclass';

ただselectの方はグループ化に対応していない(1.0.1a)らしく最初のセレクターにしかidを追加して
くれないようです。(queryの方は上手くいく)

Mindomoマインドマップにしてみました。

次はAJAX周りで遊んで見る予定。

参考


木曜日, 4月 19, 2007

[JavaScript]Ext.js 1.0が正式リリース

リッチなJavaScriptライブラリのExt.jsが正式リリースされたようです。

サンプルのようにかなりカッコイイコンポーネントが用意されています。ドキュメントも充実していて結構使いやすい印象です。

最近話題のApolloでも、サンプルアプリケーションで使われていたりします。
Fresh Reader

また超リッチJavaScriptコンポーネント集「Ext」がprototype.jsに対応にもあるようにCoreとなるライブラリを切り替えられるというのもユニークな点かと思います。

さらに全てをダウンロードしなくても、必要な部分だけをbuildしてダウンロードさせてくれるサービスも公開されています。
コアライブラリを選択し必要なコンポーネントを選択すると、その分だけのコードをまとめてダウンロードできます。ただしcssや画像などは別途ダウンロードしておく必要があるようです。(zip中のresourcesを使う)

UIコンポーネント以外でもDomQueryも便利そうです。XPath、CSSセレクターで、CSS3レベルのセレクターをほぼサポートしていて、しかもかなり高速とのこと。CSSセレクターの実装はブラウザによって結構差がありますが、これを使えばそういった心配もいらないので助かりそうです。

月曜日, 1月 29, 2007

[Testing][Perl]Test::WWW::SeleniumでPerlからSelenium RCを操作する

Selenium IDEを使うとhtmlの各種プログラミング言語用のテストコードも出力してくれます。
Perlの場合はTest::WWW::Seleniumモジュールを利用します。

前記事「Selenium RCとSelenium IDEでWEBアプリのUIテストを簡単自動化」の操作で出力されるPerlコードは以下のようになります。

Selenium IDEによって出力されるコード

use strict;
use warnings;
use Time::HiRes qw(sleep);
use Test::WWW::Selenium;
use Test::More "no_plan";
use Test::Exception;

my $sel = Test::WWW::Selenium->new( host => "localhost",
                                    port => 4444,
                                    browser => "*firefox",
                                    browser_url => "http://localhost:4444" );

$sel->open_ok("/");
$sel->type_ok("q", "Perl");
$sel->click_ok("btnG");
$sel->wait_for_page_to_load_ok("30000");
$sel->is_text_present_ok("Perl の検索結果");


スクリプトはutf8で保存します。browser_urlがlocalhostになってしまうようなのでwww.google.co.jpに変更します。

スクリプト実行前にSelenium RCを起動しておきます。
java -jar selenium-server.jar


続いてテストスクリプトを実行します。
ok 1 - open, /
ok 2 - type, q, Perl
ok 3 - click, btnG
ok 4 - wait_for_page_to_load, 30000
not ok 5 - is_text_present, Perl の検索結果
# Failed test 'is_text_present, Perl の検索結果'
# at test_utf8.t line 19.
1..5
# Looks like you failed 1 test of 5.

最後のテストで失敗してしまいました。verboseモードを使って実行手順をトレースしてみまると以下のようなコードを送信しているようでした。
---> Requesting http://localhost:4444/selenium-server/driver/?cmd=isTextPresent&1=Perl%20%C3%A3%C2%81%C2%AE%C3%A6%C2%A4%C2%9C%C3%A7%C2%B4%C2%A2%C3%A7%C2%B5%C2%90%C3%A6%C2%9E%C2%9C&sessionId=1170055300866
Got result: OK,false


日本語の部分がおかしいようなのでコードを追ってみると以下のようにutf8フラグ付を前提としているようです。
URI::Escape::uri_escape_utf8(shift @args);

テストスクリプトにuse utf8をつけて再度実行してみます。
ok 1 - open, /
ok 2 - type, q, Perl
ok 3 - click, btnG
ok 4 - wait_for_page_to_load, 30000
ok 5 - is_text_present, Perl の検索結果
1..5

上手く実行できました。

このようにプログラムからテストを実行すると、テストに必要なデータの準備から後始末までを自動化できます。例えばユーザーの新規登録フローをテストするといったケースでは登録しようとするユーザーのデータがないことが前提になりますので、まずテストユーザのデータを削除してからブラウザを起動して登録フローのテストを実行することで繰り返しテストすることが可能です。DBがトランザクションをサポートしているものであれば最後にrollbackしてしまえば不要なデータも残らず快適です。

このあたりのノウハウは前項でも紹介したWEB+DB Pressで詳しく紹介されています。Perlは無いですが。。。


最終的なコード
use strict;
use warnings;
use Time::HiRes qw(sleep);
use Test::WWW::Selenium;
use Test::More "no_plan";
use Test::Exception;
use utf8;

my $sel = Test::WWW::Selenium->new( host => "localhost",
                                    port => 4444,
                                    browser => "*firefox",
                                    browser_url => "http://www.google.co.jp",
                                     );

$sel->open_ok("/");
$sel->type_ok("q", "Perl");
$sel->click_ok("btnG");
$sel->wait_for_page_to_load_ok("30000");

$sel->is_text_present_ok("Perl の検索結果");


木曜日, 1月 18, 2007

[Perl]Benchmarkモジュールの小ネタ

PPerlのベンチを取りたくて、timeコマンドとかでも充分差は出るのですが、繰り返し実行される際の差を取りたかったのでBenchmarkモジュールでやろうとしたら少しおかしな結果になりました。

ちなみにPPerlとはスクリプトをコンパイルしてデーモンとして常駐させることで、起動時のオーバーヘッドをなくして高速化するというモジュールです。これもPerl Hacksに載ってます。

ベンチ対象(hello.pl)

use strict;
use Template;

my $tmpl = <<__TMPL__;
str = [% str %]
__TMPL__

my $tt = Template->new();
$tt->process(\$tmpl, { str => 'hello' });

print "hello\n"だけでやるとプロセス間通信のオーバーヘッドの方が大きいのかかえって遅くなったので、適当に大きめなモジュールを使えということでTemplateを使用。

で、ベンチのプログラムですが、以下のようにSYNOPSISどおりに実行すると
#!/usr/local/bin/perl
use strict;
use Benchmark qw(timethese);

timethese(100, {
    pperl => sub {
        my $ret = `/usr/bin/pperl -w --no-cleanup hello.pl`;
        die "error" unless ($ret =~ m|hello|);
    },
    perl => sub {
        my $ret = `/usr/bin/perl -w hello.pl`;
        die "error" unless ($ret =~ m|hello|);
    },
});


こんな感じになります。
Benchmark: timing 100 iterations of perl, pperl...
    perl: 42 wallclock secs ( 0.01 usr 0.03 sys + 38.64 cusr 2.56 csys = 41.24 CPU) @ 2500.00/s (n=100)
    pperl: 6 wallclock secs ( 0.01 usr 0.03 sys + 0.34 cusr 0.38 csys = 0.76 CPU) @ 2500.00/s (n=100)


wallclockとかはちゃんと出ているんですがRateがおかしい。これでも結果はわかるといえばわかるのですが気にいらないのでいろいろ調べていると、第3引数にstyleを指定することができるというのを見つけました。これを使うと親プロセスだけの時間とか子プロセスだけの時間とか計ってくれるようです。今回は子プロセスの実行時間が欲しいので'nop'としました。

スタイルの指定
#!/usr/local/bin/perl
use strict;
use Benchmark qw(timethese :hireswallclock);

timethese(100, {
    pperl => sub {
        my $ret = `/usr/bin/pperl -w --no-cleanup hello.pl`;
        die "error" unless ($ret =~ m|hello|);
    },
    perl => sub {
        my $ret = `/usr/bin/perl -w hello.pl`;
        die "error" unless ($ret =~ m|hello|);
    },
}, 'nop');


実行結果
Benchmark: timing 100 iterations of perl, pperl...
    perl: 47.9732 wallclock secs (44.66 cusr + 3.14 csys = 47.80 CPU) @ 2.09/s (n=100)
    pperl: 7.10064 wallclock secs ( 0.35 cusr + 0.47 csys = 0.82 CPU) @ 121.95/s (n=100)

うまく出ました。それにしてもallのとき(デフォルト)は親子の合計で/sも出してくれればいいのにと思うのは私だけでしょうか?

ちなみにこれを調べている過程で':hireswallclock'を見つけました。これをuseのときに指定しておけばwallclockもTime::HiResで出してくれます。ちょっと便利。




[Book][Perl]Perl Hacks日本語版

ついに日本語版が出るようです。



すぐに使えるものから、ちょっとマニアックなものまで、Perlプログラミングに関する101のTIPSが集められています。英語版を買って全部は読みきれていないのですが印象に残ったハックを紹介。

Hack 3 Perlドキュメントをオンラインでブラウズしよう
Pod::Webserverモジュールが紹介されています。
Pod::Webserver::Sourceも組み込むとソースコードも閲覧できて便利です。ただいずれも日本語が上手く表示できないのが難点。

Hack 27 反復子から複数の値を引き出そう
wantarrayよりも豊富なcontext情報を提供するWantモジュールが紹介されています。
リストの数や、HASHかどうかまで判定できたりまします。

Hack 47 メソッド引数の自動宣言
B::Deparseモジュールの応用例です。CODEREFからCODEテキストを取得してそれを置換処理して$selfを自動宣言するという力技です。

Hack 74 使われているすべてのモジュールをトレースしよう
Devel::TraceUse
モジュールのuse順をトレースしてくれます。
当ブログの[Perl]useなしでどこでもDump(@INCにオブジェクトを格納するパターン)はここで知った知識を使っています。
でもこのTraceUseモジュール、じつはrequireのところでエラーになっていて本当にrequireするのにかかった時間を計っているわけではないようです。バグな気がしたので直そうとしたのですがrequireのネストが上手く処理できず直そうとしても上手くいきませんでした。。。

Hack 78 サブルーチンの中身を覗こう
Bモジュールのsvref_2objectを使うとCODEREFからシンボル名などさまざまな情報が取得できます。Bモジュール面白いですね。

そのほかにもvimでパッケージ名(::も含めて)をコード補完したりだとかデバッガをカスタマイズしたりなど使えるマニアックなハックが満載です。

原著


水曜日, 1月 10, 2007

[Testing]Selenium RCとSelenium IDEでWEBアプリのUIテストを簡単自動化

WEB+DB PressでSelenium特集が掲載されていたので試してみました。



Seleniumとはブラウザからのテストを自動化するツールです。テストケースをHTMLのtableタグで記述するため、比較的簡単にテストケースを作成できます。Selenium-Coreのみではテストケースをテスト対象アプリケーションがあるサーバと同じ場所に置かなければならない為若干面倒ですが、Selenium RCを使うとアップロードが不要になる為非常に便利です。

インストール
Selenium Remote Control
現時点での最新バージョンは0.90ですが-htmlSuiteオプションが使えないとのことなのでバージョン0.81を使用します。解凍して適当なところに置いてください。

Selenium IDE
Firefoxエクステンションです。バージョン0.86を使用しました。

テストに必要なファイル
作成するファイルは以下のとおりになります。
TestCase.html ・・・ 個々のテストケース
TestSuite.html ・・・ テストケース一式をまとめたファイル。個々のテストケースへのリンクを貼る

少量のテストであればテキストエディタでも作成できますが、Selenium IDEを使うとブラウザの操作を記録してテストケースとなるhtmlファイルを作成してくれます。使い方はIDEを起動して記録ボタンを押した後、テスト対象となるアプリケーションを操作するだけです。

サンプル動画(Flash)
のように操作すると以下のようなhtmlファイルが生成できます。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>TestCase1</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">TestCase1</td></tr>
</thead><tbody>
<tr>
    <td>open</td>
    <td>/</td>
    <td></td>
</tr>
<tr>
    <td>type</td>
    <td>q</td>
    <td>Perl</td>
</tr>
<tr>
    <td>clickAndWait</td>
    <td>btnG</td>
    <td></td>
</tr>
<tr>
    <td>assertTextPresent</td>
    <td>Perl の検索結果</td>
    <td></td>
</tr>

</tbody></table>
</body>
</html>



続いてTestSuiteファイルを作成します。こちらはテストケースを一覧したhtmlとなります。

<html>
<head><title>Test Suite</title></head>
<body>
  <table cellpadding="1" cellspacing="1" border="1">
    <tbody>
      <tr><td><b>Test Suite</b></td></tr>
      <tr><td><a href="./TestCase1.html">TestCase1</a></td></tr>
    </tbody>
  </table>
</body>
</html>



テストの実行
Selenuim RCを起動してテストを実行します。
コマンドの構文は
java -jar selenium-server.jar -htmlSuite <ブラウザ> <テスト対象サイトURL> <テストスイートファイル> <テスト結果ファイル>

です。

上記TestSuiteの場合は以下のようにします。
java -jar selenium-server.jar -htmlSuite "*firefox" "http://www.google.co.jp" tests/TestSuite.html tests/result.html

とします。

以上で自動的にブラウザが起動してテストが実行され、結果がresult.htmlファイルに出力されます。

ただこのままですテストが終わると即ブラウザが終了してしまうため、いちいちresult.htmlを開いて見ないと結果がわからないのが面倒です。そこで
[Selenium]assertEvalの活用方法(ブレイクポイント、終了通知)
を参考にJavaScriptが使えることを利用してalertするテストケースを用意しておき、TestSuite.htmlの最後に追加してやると、テストの最後にalertが出るので即結果が確認できます。

テスト実行結果


Seleniumではブラウザを会してテストを行う為JavaScriptの実行結果もテストできます。Selenium RCを使うとコマンド一つでブラウザの起動からテスト結果の保存までが行えるので回帰テストが非常に楽になると思います。

参考URL
Selenium 0.7利用手順書(前編)
これはすごい! Web案件必須 Selenium - 人気急上昇中自動テストツール