2013年3月20日水曜日

ガントチャートを捨てろ(4) タスクボードとバーンダウンチャート

前回(→こちら)はバーンダウンチャートとバーンアップチャートの併記によるメリットについて述べました。じゃあそのバーンダウンチャートとバーンアップチャートはどうやって作ればいいの?ということで、今回はタスクボードについて紹介したいと思います。

ということで早速、実物を見ていただこうと思います。


これがタスクボードです。一部を伏字にしていますが、実際に仕事で使ったものです。

このプロジェクトでは、Redmineのチケットを一つの単位として管理していました。チケット番号を左端に記入し、チケット題名を記入します。このチケット題名ですが、なるべく主語を明確にします。例えば、「サイト訪問者として○○を選択できるようにする」などです。このように主語を明確にして利用者の立場から機能要件を表現したものを「ユーザーストーリー」と呼びます。つまり上の図ではRedmineとの連携を意識して「チケット題名」と書いていますが、ここは「ユーザーストーリー」と書いてもいいのです。

その横の「SP」は「ストーリーポイント」です。「ストーリポイント」とは、ストーリーの大きさを表す相対値です。相対値なので、あるストーリーを5と決めたら、その2倍の大きさのストーリーは10となります。別にこれを10と20にしてもいいわけです。

そして、各チケット(のストーリー)をタスクに分解し、各タスクの時間を見積ります。
上の図では、細かく分解してありますが、実際には最初から詳細にタスク分解する必要はありません。例えば、最初の時点では

・ 仕様・タスクを定義する
・ 実装
・ 調整
・ テスト実施、バグ修正を行う

の4つのみに分解しておき、タスクをこなしながら実装方式検討や実装の細かい分類のタスクなどに細かく分解していけばいいのです。その際、ストーリーごとの見積時間の合計が変わらないようにしておけば、全体として整合性が保たれます。

ここで、ストーリーポイントとそのストーリー中のタスクの見積り時間の合計がほぼ対応するようにします。例えば上の図では、各チケット(のストーリー)のタスクについての見積時間の合計は、「ストーリーポイント×4」になっています。厳密に合わせなければいけないわけではないですが、だいたい合わせるようにします。

例えば上の図でチケット#30は「実装(処理)」が5時間、「実装(権限制御)」が5時間となっていますがもともとは「実装」の1つで10時間と見積もっていたものです。これを作業をしながら前述の2つに分けたのです。

各タスクについて、現在の状態に応じて「未着手」「作業中」「完了」の欄のどれかに印をマークします。また、着手した際には「着手日」に、完了した際には「完了日」に日付を記録します。

「残り時間」「完了時間」「進捗率」はExcelの機能で自動計算することができます(「完了」列にマークされているかどうかを判断して計算します)。

タスクボードの下部には「見積時間」「残り時間」「完了時間」「進捗率」の合計値がExcelの機能で自動的に算出されるようになっています。

タスクボードからのバーンアップ・バーンダウンチャートの作成は簡単です。
1日ごとに「残り時間」の合計を記録すればバーンダウンチャートになります。
また、「見積時間」の合計と「完了時間」の合計を記録すればバーンアップチャートになります。

このタスクボードは、ガントチャートとセットで使われるWBSと以下の点で違いがあります。
  • 最初から全てを分解する必要がない
    WBSの場合は、作業を分解すること自体が目的でした。それに対してタスクボードは進捗把握が目的です。分解の粒度は進捗を把握するのに必要なだけなので、作業にとりかかる直前でも構わないのです。
  • 順番を意識しなくてよい
    WBSの本来の定義では順番は関係ないのですが、WBSは殆どの場合ガントチャートとセットになるため順番を意識せざるを得ません。タスクボードは残り時間と完了時間の合計値さえ計算出来れば良いので順番は関係ありません。
  • 同時並行作業もタスクとして書ける
    WBSの場合、やはりガントチャートに対応させる関係上、分解したタスク同士は同時並行で作業を進める前提にはできません。タスクボードの場合は残り時間と完了時間の合計値が計算できればいいだけなのでタスクを同時並行して進めても一向に構いません。
ということで、4回にわたってお伝えして参りました「ガントチャートを捨てろ」シリーズですがいかがでしたでしょうか。これをお読みの皆様の中で1人でもお役に立てましたら幸いです。

2013年3月2日土曜日

ガントチャートを捨てろ(3) バーンダウンチャートとバーンアップチャートを併記する

前回(→こちら)はガントチャートを捨てて、代わりに「バーンダウンチャート」を使うことを提案しました。その具体的メリットと、どんなプロジェクトに向いているかもまとめました。

そこで、バーンダウンチャートの弱点としてグラフの傾きが鈍くなった際に「開発が進んでいない」のか、「開発は進んでいるがタスクの総量が増えてタスク残量が減らなくなった」のかの区別がつかないことを挙げ、それは「バーンアップチャート」を併用することにより克服できると書きました。

ではその「バーンアップチャート」とは何なのか、さっそく以下の図をご覧ください。


これが「バーンアップチャート」です。バーンダウンチャートと同じく横軸に時間を取りますが、縦軸はタスクの量として見積時間合計と完了時間合計の2つのグラフを書きます。

上記の例では赤い線が見積時間合計、すなわち期限までにこなすべきタスク全体の合計量を表しています。青い線は完了時間合計、すなわちその時点までに完了したタスクの合計量を表しています。赤い線青い線交わったら完了です。

ちなみに、この赤い線青い線距離は残タスクの合計、すなわちバーンダウンチャートになるのですね。

このバーンアップチャートがバーンダウンチャートと違う点は、タスク総量の変化を表現できることです。タスク総量が変化するのは、例えば急な仕様変更が入った場合や、タスクに要する時間の見積りを間違えていて修正した場合などが考えられます。なお、そういうことがなければ見積時間合計の線は横一直線になります。

例えば上記の例は前回用いたの同じく私が実際に仕事で記録した例ですが、1/19に見積時間合計が増加しています。これは、実際に開発を進めたところ、当初のタスク見積よりもタスクが多かったため、見積時間を多く修正したのです。

そして、純粋な開発速度そのものを表現できるのも強みです。完了時間合計の線の傾きを見れば、タスクをこなすスピード自体がわかります。この傾きが一定であれば、同じペースで仕事をこなせているということです。

さて、ここまでお読みになると「バーンダウンチャートとバーンアップチャートを状況に応じて使い分ければいいのかな」と思われるかもしれません。しかし使い分けるよりも、バーンダウンチャートとバーンアップチャートを併記すると面白くなります。例えば以下の図をご覧ください。



左右の図は別の角度から同じ状況を表現したものです。しかしここから、バーンダウンチャートだけではわからなかった事実を次のように読み取ることができます。

  • バーンダウンチャートを見ると、1/16~1/19にかけて傾きが鈍くなり、開発スピードが落ちているように見える。特に、1/19は全く開発が進んでいないように見える
  • 一方バーンアップチャートを見ると、1/19には完了時間合計はむしろ急に増えており、開発は進んでいないどころか、頑張ってかなり進めたことが分かる。そしてこの日に見積時間合計がこなしたタスクと同じくらい増えていた。これは甘かった見積りの修正であった。
  • つまり途中でつまづいていたわけではなく、この部分で取り掛かっていたタスクの見積りが甘かったことが原因だった、ということが読み取れる。
いかがでしょうか?このようにバーンダウンチャートとバーンアップチャートを併記すれば、後から振り返った場合に、どういう状況だったのか分析しやすくなります。次の開発に向けて反省点を見つけやすくなるのです。

上記で挙げた例は見積りが甘かった例ですが、これがもし仮に仕様変更によるタスク増加だった場合、仕様変更が原因で開発が苦しくなることを明確に説明できるツールになります。なぜなら、バーンダウンチャートの傾きが鈍くなったとき、バーンアップチャートの完了時間合計の傾きが一定で見積時間合計が増えているならば、開発速度は本来落ちていないのに、仕様変更により遅れが発生したことが明確にわかるからです。これは仕様変更に対抗する武器になりえます。

前回と今回にわたってバーンダウンチャートとバーンアップチャートの説明をしたわけですが、これらのグラフを描くには、残タスクの量あるいは完了タスクの量を計測することが必要です。それを計測するためのツール、すなわちバーンダウンチャートとバーンアップチャートを描くためのツールに「タスクボード」があります。タスクボードについての詳細は記事を改めて書こうと思います。(→こちら

2013年2月8日金曜日

ガントチャートを捨てろ(2) バーンダウンチャートを使う

さて、前回(→こちら)はガントチャートを捨てろと言い出したわけですが、代わりに「バーンダウンチャート」を使っていると書きました。

とりあえず実物を見ていただいたほうが話が早いので、さっそく以下の図をご覧ください。


あれだけガントチャートを批判しておいて、どんな凄い物が出てくるんだと期待した方には拍子抜けするかもしれませんが、至ってシンプルなグラフです。

しかしこの単純なグラフこそ、ソフトウェア開発者にとってガントチャートよりも実践的な、現実に即した進捗管理ツールなのです。

バーンダウンチャートでは縦軸に残タスクの量、横軸に時間を取ります。上記の例では縦軸に残タスクの見積もり所要時間合計、横軸に日付を取っています。

横軸の範囲はプロジェクトの進め方に応じて決めればよいのですが、上の例では次のバージョン(公式リリース用ではなく内部管理用のバージョン)の完成予定日までにしています。

毎日帰る前に、残タスクの見積り時間の合計をグラフに記録していきます。
残り時間合計が減っていき、グラフが一番下まで行って横軸と交わったら完了です。

このバーンダウンチャートを使うと、次のように前回挙げたガントチャートの欠点を回避できます。

  • 順番を意識しないでいい
    ガントチャートは作業の順番に依存した描き方でしたが、バーンダウンチャートは残り時間の合計を記録していくので、途中で作業の順番を入れ替えようが関係ありません
  • 見積りの変更に強い
    仕様変更や作業時間の見積もりが間違っていて工数を変更した場合でも、グラフを変更したあとの数値で続けて書いていけばいいだけです。書き直しは発生しません
  • 開発スピードが分かる
    これが一番のメリットでしょう。グラフの傾きで一目で分かります。しかも結構初期の段階で分かります。

    そして突発的トラブルで遅れているのか、開発スピード自体に問題があるのかも判別をつけやすいです。突発的トラブルなら、グラフの傾きが突然鈍くなるはずですが、そもそも開発スピード自体が遅いなら、グラフの傾きは最初から鈍いはずです。

    グラフの傾きを見ることで、頑張れば挽回できるのか、それとも無理なのかの判断材料にすることが出来るのです。
ちなみに上記のグラフは実際に僕が仕事で記録したデータそのものです。推移を見ていくと、

・1/16(水)~1/18(金)辺りで何らかの事情によりペースが落ち、
・遅れを取り戻すため、やむなく土日出勤し、
・次の週、頑張って追い込みをかけ、
・スパートかけた甲斐あって最後の1日は余裕を持って終了

といった生々しい状況(笑)を読み取ることができます。このような状況をあとから時系列に沿って振り返ることも、ガントチャートでは不可能ですがバーンダウンチャートなら可能です。

また、バーンダウンチャートはExcelで簡単に作れます。こんな感じで表を作ってグラフにしていました。



なお、表とグラフ横軸の土日はもともと存在せず、あとから足しました(もちろんです)。単純に表の行を足せば自動的にグラフに反映されるので変更は簡単でした(Excelグラフのいいところですね)。開発状況の方は大変でしたが(苦笑)。

ちなみにこの「残り時間合計」をどうやって計算するのかというと、実はバーンダウンチャートと組になるもう一つのツール「タスクボード」を使うのです。タスクボードについては別の記事で改めて取り上げます。(→こちら

さて、色々とメリットがあるバーンダウンチャートですが、もちろん万能ではありません。

  • 横軸が一定以上長くなると使いづらい
    例えば横軸を1目盛1日に取る場合、1週間~4週間くらいまでが使いやすい範囲といえるでしょう。それ以上になるとかえって見にくくなってきます。

    その意味でバーンダウンチャートはアジャイル開発手法には最適です。アジャイルでは繰り返しの単位を1週間~4週間で設定することが多いからです。そもそも僕はアジャイル開発の書籍でバーンダウンチャートを知りました。

    反復型開発やウォーターフォールモデルなどでバーンダウンチャートを使う場合、設計・実装・テストの各工程ごとに別々のグラフを描く良いでしょう。それでも期間が長いようなら、更に各工程内部で幾つかに分割するなどの工夫が考えられます。
  • タスク総量が増加したことと開発が滞っていることの区別がつかない
    前述のように突発的トラブルが発生したこと自体は、グラフの傾きが急に鈍くなることから分かります。しかし、そのトラブルの内容が「開発が進んでいない」のか、「開発は進んでいるがタスクの総量が増えてタスク残量が減らなくなった」のかの区別はつきません。
2点目のタスク総量の変化の読み取りについては、「バーンアップチャート」を併用することにより克服できます。僕も実際はバーンダウンチャートとバーンアップチャートを併用しています。こちらも詳しくは別記事に書こうと思います(→こちら)。

2013年2月3日日曜日

ガントチャートを捨てろ

ソフトウェア開発者は、スケジュール管理の視覚化というとまず「ガントチャート」を思い浮かべる人が多いのではないでしょうか。

では、ガントチャートを選んだ理由は?と聞かれると、「標準的に使われているから」などの理由しかなくて、ガントチャートを使うことの利点を意識していたわけではないと思います。

僕はもう丸1年以上ガントチャートを使っていません。代わりに「バーンダウンチャート」を使っています。(バーンダウンチャートについては、別の記事で改めて書こうと思います→こちら

ガントチャートを使っていると、次のような点で「ソフトウェア開発にそもそも向いていない」と感じます。

  •  順番を意識しすぎる
    ガントチャートはそもそも、建築や製造など、原則後戻りをしない作業工程からなるプロジェクトの進捗状況を把握することに向いています。作業の順番を強く意識した描き方になっているからです。

    しかしソフトウェア開発は計画した順番通りに作業を進められる場合ばかりではありません。作業の順番を入れ替えた際にガントチャートは大幅に描き直さなけれなならず、管理の手間が掛かり過ぎます。

    逆に作業順序をガントチャート通りにやろうとして却って非効率になってしまっては本末転倒です。
  •  見積もりの変更に弱い
    順番だけでなく、それぞれの作業工程の工数が、仕様変更やそもそも見積が間違っていた場合などで変更された時にも、大幅に描き直さなければなりません。
  •  開発スピードが分からない
    ソフトウェア開発におけるマネジメントの最大の目的の1つは、期限までに間に合うかどうかを常に把握することです。

    ここはソフトウェア開発の難しいところですが、「だいたいこのペースで進む」という「スピード」の予測は非常に立てづらいのです。スピードは仕様や開発環境、メンバーの経験・能力・心理・健康状態など様々な要因を受けて変わりやすく、事前の計画段階で正確に予測するのは事実上不可能と言っていいと思います。

    さて、ガントチャートで把握できるのは「遅れているか、進んでいるか」だけです。例えば2日分遅れがあるとしましょう。一日の作業時間を7時間とすれば、14時間分の遅れです。しかし、その遅れが、開発の「スピード」自体が遅くて少しずつ積み重なって現れた14時間なのか、それともスピード自体は問題なかったものの、突発的にトラブルが発生したので現れた14時間なのか区別がつきません。

    突発的トラブルならば、その14時間の遅れはプロジェクトの残りの期間も(別のトラブルが発生しない限り)同じであると予想出来ます。

    しかし、スピード自体が間に合っていないなら、14時間の遅れはプロジェクトの残りの期間でさらに20時間、30時間と拡大していってしまうかもしれません。

    ここを把握できるかどうかはまさに致命的です。突発的トラブルによる遅れなら、頑張れば挽回できるのかもしれません。しかしスピード自体が頑張っても無理なほど足りないなら、すぐに作業分担の変更か、増員か、期限の延長か、仕様の削減を検討するべきでしょう。

    しかしガントチャートでは「スピード」を把握することが出来す、遅れにどう対処すべきかの判断材料としては得られる情報が不足しているのです。

このようにガントチャートはそもそもソフトウェア開発に不向きな点があります。僕はより現実的なスケジュール管理視覚化のツールとして「バーンダウンチャート」を使っており、別の記事で改めて書こうと思います(→こちら)。

2012年10月20日土曜日

Eclipse EGit でのリベースのやりかた

Gitで便利な機能の1つ、リベース。
リベースの機能や動作自体については、各所で解説されているのでここでは説明しません。

便利なリベースですが、普段 Eclipse を用いて開発を行い、EGit プラグインのお世話になっていますから、リベースもぜひ EGit でやりたいでしょう。

しかし、EGit上でのリベース手順の情報が少なく、迷うことがあるでしょう。
ということで、今回は、Eclipse プラグイン EGit を用いてリベースを行う手順について説明します。

まず、リベースを行いたいブランチをチェックアウトします。

リベースを始める前に、ヒストリーを出しておくといいでしょう。
「プロジェクトのルートディレクトリを 右クリック → チーム → ヒストリーに表示」で出しておきます。

それではリベースを開始しましょう。
「プロジェクトのルートディレクトリを 右クリック → チーム → リベース...」と選択します。



リベースをどのブランチの上に行うか選択するダイアログが表示されます。


任意のブランチを選択し、「リベース」をクリックします。


リベースが始まります。表示させておいたヒストリーを見ると、リベースが行われる様子が表示されるので面白いです。

途中、リベースで適用される差分とそのコミットでの差分が衝突すると、ダイアログが表示されます。


そのままマージを開始するので「Start Merge Tool to resolve conflicts」が選択されているのを確認し「OK」をクリックします。

するとマージ方法を聞かれます。


ここでは「Use the workspace version of conflicting files」の方を選択しておきます。

マージ・ツールが起動します。
マージ・ツールではリベース前のコミットに記録されているファイルが右側、リベース対象のマージ中のファイルが左側に表示されるの出ます。左側を編集して、意図する形にします。(右側はそもそも編集不可になっています)

衝突を起こしているファイルは赤い両矢印のマークが表示されるので、それらのファイルを全て正しく編集することになります。衝突箇所が<<<<<<<,=======,>>>>>>>で区切られているのはGitコマンドラインツールと同じで、そのあたりはGit本家の解説をご参照ください。

衝突を解決しているときに、HEADと比べてみると参考になります。マージ・ツールではリベース前のコミットのファイルが表示されますが、衝突解決中のHEADはリベースが適用できた最後のコミットのファイルになっているので、比較することで衝突解決後の(リベースされたブランチ上での)変化がわかるからです。

衝突を解決できたと思った時点で、そのファイルについて「ファイルを右クリック → チーム → 比較 → HEAD」と選択して比較してみましょう。


さて、衝突を解決できたらそのファイルを Add します。Add すると、赤い両矢印の衝突マークが消えて、アスタリスクのステージングマークが表示されます。

全てのファイルの衝突を解決し Add  し終わったら、プロジェクトのルートディレクトリに表示されていた赤い両矢印の衝突マークが消えて、アスタリスクのステージングマークが表示されるでしょう。

その状態になったら、「プロジェクトのルートディレクトリを 右クリック → チーム → リベース → Continue Rebase」を選択します。


これでリベース処理が再開されます。

衝突が起きるたびに上記手順を繰り返し、最後に完了メッセージが表示されれば、リベースは完了です。

2012年9月22日土曜日

[改造版] jQueryのiPod風メニュープラグイン『jQuery iPod-style Drilldown Menu』をちょっと改造

jQueryを利用したメニューを実現するプラグインで、
iPod風に深い階層をスライドして表現するプラグインがあります。
『jQuery iPod-style Drilldown Menu』 http://filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/ 

結構有名なプラグインのようで、「jQuery menu ipod」あたりで検索すると
多くの紹介ページがヒットします。 

しかしながら、実際に利用しようとした際にバグがあったり、
手を入れたいと思った部分がありました。
そこで、バグ修正および若干の機能追加を行ったものを作成しました。
変更点は以下の通りです。

  • サブメニュー項目数が親メニューより多い場合に、Backボタンの表示が崩れるのを修正
    (本家ページのDenis Shaposhnikov氏のコメントより)
  • 速いスピードでメニューを開閉すると、表示が崩れることがあるのを修正
    (本家ページのtim氏のコメントより)
  • ウィンドウサイズを変更した際、メニューの位置がずれるのを修正
  • 初期化時のcontentオプションにjQeuryオブジェクトを指定できるようにした
  • メニュー内のボタン以外の部分をクリックした際、メニューが閉じないようにした
  • オプションの"backLinkText"がパンくずリストを使用しない時に反映されなかったのを修正

初期化時のcontentオプションにjQueryオブジェクトを指定できるようにした理由は、
メニュー内部の要素にイベントを設定していた場合、元コードではメニューはcontentオプションに指定した要素内のHTMLをコピーして作成しており、イベントはコピーされず意図した動作にならない可能性があるためです。
contentオプションにjQueryオブジェクトを指定することにより、イベントを維持したままメニューを構成できます。

メニュー内のボタン以外の部分をクリックした際、メニューが閉じないようにした理由は、
例えばラジオボタンや入力欄などのinput要素をメニュー内に設置した時に、クリックした瞬間にメニューが閉じられては困るからです。

※Bloggerの仕様変更なのか、この投稿内でのサンプルは動かなくなったので削除しました。

ダウンロードはGitHubに本家からforkしたものがあるのでそちらからどうそ。
https://github.com/takaaki-kasai/jQuery-Menu

2012年8月26日日曜日

Eclipse PDT でコメント切り替えショートカットキーが効かなくなるのを直す

Eclipse 4.2 Juno にアップデートしたら、Ctrl+/によるコメントアウトの切り替えが動作しなくなることがありました。

どうやら、エディタを複数開くと2つ目以降でCtrl+/が効かなくなるものがあるようです。
調べるうちに、同じ現象での相談がフォーラムに投稿されているのを見つけました。

http://www.eclipse.org/forums/index.php/m/894630/

解決方法はPDTプラグインの中身を直接いじるというもの。jarファイルを展開、編集して再度アーカイブするように書いてあります。

展開後再アーカイブする方法を試してみましたがエラーが出るなどうまくいきませんでした。しかし、再アーカイブではなくアーカイブ中の対象ファイルのみを更新する方法でうまくいきましたのでその方法を書きます。

jar を展開する

まず、編集対象のファイルを取り出すため、対象のjarファイルを展開します。
Eclipse Pleiades All in Oneでは、
eclipse\dropins\PDT\eclipse\plugins\org.eclipse.php.ui_3.0.1.v201201110400.jar
にありました。 元のjarをバックアップした後、一時フォルダにコピーします。

その後コマンドプロンプトを起動し、一時フォルダに移動してjarを展開します。
> jar xvf org.eclipse.php.ui_3.0.1.v201201110400.jar

plugin.xmlを書き換える

展開されたファイルのうち、plugin.xmlを書き換えます。書き換える箇所は612行目付近の次の箇所です。

書き換え前:
<!-- overriding WST's Handlers with the same 'commanId' -->
<handler class="org.eclipse.php.internal.ui.actions.PHPToggleLineCommentHandler"
    commandId="org.eclipse.wst.sse.ui.toggle.comment">
    <activeWhen>
    <or>
        <reference definitionId="org.eclipse.php.ui.phpContentType.definition" />
       <with variable="activePartId">
        <equals value="org.eclipse.php.editor"/>
       </with>
    </or>
        
    </activeWhen>
    <enabledWhen>
        <reference definitionId="org.eclipse.php.ui.phpContentType.definition" />
    </enabledWhen>
</handler>
書き換え後:
<!-- overriding WST's Handlers with the same 'commanId' -->
<handler class="org.eclipse.php.internal.ui.actions.PHPToggleLineCommentHandler"
    commandId="org.eclipse.wst.sse.ui.toggle.comment">
    <activeWhen>
        <reference definitionId="org.eclipse.php.ui.phpContentType.definition" />
    </activeWhen>
    <enabledWhen>
        <reference definitionId="org.eclipse.php.ui.phpContentType.definition" />
    </enabledWhen>
</handler>

jarを更新

書き換えたplugin.xmlをjarに注入して更新します。
> jar -uf org.eclipse.php.ui_3.0.1.v201201110400.jar plugin.xml
更新したjarで元ファイルを上書きし、eclipseを-cleanオプション付きで起動します。
これでエディタを複数起動した時も2つ目以降でCtrl+/が効くようになります。

2012年8月22日水曜日

Eclipse Pleiades All in One PHP 版でタスク一覧から特定のフォルダを除外する

Eclipse Pleiades All in One PHP 版でタスク一覧から特定のフォルダを除外する方法です。
(確認したバージョンはEclipse 4.2 Juno)
  1. まず公式の「Eclipse Web 開発者ツール(Web Developer Tools)」をインストール
    メニューの「ヘルプ→新規ソフトウェアのインストール...」を選び、Eclipseのリリースサイトからインストールします。
  2. プロジェクトのプロパティーに「検証」が出現
    PHPエクスプローラでプロジェクトのルートディレクトリを右クリック→プロパティ
  3. 「検証」のバリデーター一覧のうち、PHPTaskValidatorの設定で除外したいフォルダを指定する。
    「プロジェクト固有の設定を可能にする」チェックボックスをオンにし、 PHPTaskValidatorの設定ボタンをクリックします。
    出てきたダイアログで「除外グループの追加..」ボタンをクリックし、追加された除外グループを選択して「ルールの追加...」をクリックします。
    ダイアログの指示に従い除外したいフォルダを指定します。
これでタスク一覧から指定したフォルダが除外されます。「Eclipse Web 開発者ツール」を入れないとプロジェクトのプロパティーに「検証」メニューが出てこないので注意してください。

2012年8月12日日曜日

Symfonyを使った開発で機能テストはlimeかSeleniumか

Webアプリの開発をテスト駆動で行おうとするとき、機能テストを自動化するためのテスティングフレームワークの選択は重要であると思います。

lime vs. Selenium


機能テストの自動化にSymfony付属のlimeとSeleniumのどっちを使うか、と思ってたら同じ事考えてる人がいました。

http://stackoverflow.com/questions/2777913/selenium-vs-phpunit-lime

上記リンク先では用途によって使い分けるべし、という論調でたしかにその通りなのですけれど、問題は「機能テスト目的としては両方とも使えてしまう」ことです。たしかにlimeでしかできないこと・Seleniumでしかできないことはあります。しかし機能テストのほとんどの部分はどちらでもできてしまうことなので、やっぱり選択は迷うことになります。

自分の結論:lime


で、ともかく自分でやってみようということで両方使ってみました。
結果、現在のところ自分はlimeのみ使っています。その理由は、
  • 速い
  • テストが書きやすい
です。

まず実行速度は当然といえば当然ですが圧倒的にlimeの方が速いです。これはテスト駆動開発を行う上で何度もテストを実行する必要が有ることを考えると、開発効率にかなりの影響を与えます。Seleniumではブラウザで実際にアクセスするのでどうしても時間がかかります。

また、テストファーストで開発を進めるのに、limeならPHPですから条件分岐やループなどの制御構造をテストに組み込むのも全く難なく出来ます。Seleniumでは制御構造をテストに組み込むにはプラグインを導入する必要があったり、書き方もHTMLを使った独特の文法ですので若干テストが書きづらいです。(PHPUnitと連携できるみたいですが環境構築が大変。やろうとして途中でやめました。)

Javascriptのテストはどうする?


ちなみにJavascirptの単体テストにはqUnitを使っています。limeによるテストではJavascirptの機能は反映されませんので、Javascriptに依存する機能は別途qUnitでテストを行うことになります。
逆に言うとlimeによるテストがJavascirptなしでも可能になるようにコードを書く必要があります。

現状、lime+qUnitという構成で自動テストの環境はほぼ十分です。最後はブラウザから操作して確認することが必要ですが、これは自動テストに抜け漏れがなかったかを意識するとよいと思います。

2012年8月9日木曜日

同じ点を複数回通れる場合の「パターンロック」のパターン数


Androidなどに採用されていてる、並んだ点同士を指でなぞって結ぶことにより、パスワードの代わりとする認証方法があります。正式名称は知らないのですが、とりあえずここでは「パターンロック」と呼ばせていただくことにします。

Androidのパターン数については既に計算している方がいます。
http://blog.goo.ne.jp/nihongi/e/d0cfde12d440b92c181b6afd2b7dc4cb
http://beust.com/weblog2/archives/000497.html

Androidでは一度通った点は再度通れないことになっています(上を通過するのはOK)。しかし、独自に認証を搭載したアプリで、同じ点を何回でも通れるようなパターンを設定できるものがありました。これのパターン数を知りたいという方がいましたので、数えてみました。
条件は、
  • 隣接する上下・左右・斜め方向の動きで点を結べる
  • 将棋の「桂馬」の動きのような結び方はなし
  • 一度通った点も再度通れる
  • 使える点は3x3の9個
です。

3x3の点で同じ点を複数回通れる場合の「パターンロック」のパターン数

結ぶ点の数パターン数
19
240
3200
4952
54624
622272
7107648
8519552
92509056
1012113920
1158492928
12282425344

結ぶ点の数が12個までを計算しましたが、同じ点を複数回通れるので結ぶ点の数はいくらでも増やせます。

Androidで一度通った点は再度通れないようにしているのは、UI上で通った点の軌跡を表示する都合上、同じ軌跡を2度描けないからだと思います。

私が見たアプリではUI上で通った点の軌跡を表示せず、通り方そのものを覚える方式であるため、複数回同じ軌跡を描くような通り方も設定可能になっていました。

たしかにUI上で軌跡を表示すればグラフィカルに記憶できますが、制限を外して自由にパターンを書けるようにしたほうがかえって覚えやすい場合もありますので一長一短ですね。

参考:Rubyによるプログラムのソース

以下に3x3の点がある場合のパターン数を計算するRubyスクリプトを示します。 実行時の引数に結ぶ点の数を指定するとパターン数を出力します。
#!/bin/ruby
# assume dots such as:
# a b c
# d e f
# g h i

class PatternLock
  def initialize
    @dots = {
      'a' => ['b', 'd' ,'e'],
      'b' => ['a', 'c' ,'d', 'e' ,'f'],
      'c' => ['b', 'e' ,'f'],
      'd' => ['a', 'b' ,'e', 'g' ,'h'],
      'e' => ['a', 'b' ,'c', 'd' ,'f', 'g' ,'h' ,'i'],
      'f' => ['b', 'c' ,'e', 'h' ,'i'],
      'g' => ['d', 'e' ,'h'],
      'h' => ['d', 'e' ,'f', 'g' ,'i'],
      'i' => ['e', 'f' ,'h'],
    }
  end

  protected
  def countPattern(level, dot)
    if (level <= 1) then
      return 1;
    else
      count = 0
      @dots[dot].each do |x|
        count += self.countPattern(level - 1, x)
      end
      return count
    end
  end

  public
  def getCount(length)
    count = 0
    @dots.each do |k, v|
      count += countPattern(length, k)
    end
    return count
  end
end

pl = PatternLock.new
puts pl.getCount(ARGV[0].to_i)
PatternLockクラスのインスタンス変数@dotsは、パターンに使える点をキーとし、次に結べる点の集合の配列を値に持つ連想配列です。上記例では各点にアルファベット1文字を割り当てています。@dotsを変更することで点の数が異なる場合や将棋の桂馬の動きを許可する場合なども計算できます。

2012年8月1日水曜日

yumでCentOSのバージョンをダウングレードする(6.3=>6.2)

yum update でOSごとアップデートしてしまった時、元に戻したい時はどうすればいいか試してみました。
検証用に以下のCentOS6.2のVMware用イメージを使わせて頂きました。
http://www.thoughtpolice.co.uk/vmware/#centos6.2
これをyum updateで6.3 にアップデートした状態。ここから6.2に戻してみます。

まずリポジトリの参照先を戻したいバージョンに強制します。
(参考 → http://d.hatena.ne.jp/stakizawa/20100522/t1

# cd /etc/yum.repos.d
# cp CentOS-Base.repo CentOS-Base.repo.orig
# vim CentOS-Base.repo
:%s/$releasever/6.2/g
:wq
# yum clean all

で、yum distribution-synchronization を実行。このコマンドはリポジトリ上の全てのパッケージをまとめてインストールあるいは更新し、バージョンもリポジトリと同じになるように試みます。例えばすでにインストールされているバージョンよりリポジトリ上のバージョンが古い場合はダウングレードを行います。

# yum distribution-synchronization

これですんなり成功してくれればいいですが、エラーが出ました。

Transaction Check Error:
file /usr/lib/python2.6/site-packages/system_config_keyboard/__init__.pyc from install of system-config-keyboard-1.3.1-3.el6.x86_64 conflicts with file from package system-config-keyboard-base-1.3.1-4.el6.x86_64
file /usr/lib/python2.6/site-packages/system_config_keyboard/__init__.pyo from install of system-config-keyboard-1.3.1-3.el6.x86_64 conflicts with file from package system-config-keyboard-base-1.3.1-4.el6.x86_64
file /usr/lib/python2.6/site-packages/system_config_keyboard/keyboard.pyc from install of system-config-keyboard-1.3.1-3.el6.x86_64 conflicts with file from package system-config-keyboard-base-1.3.1-4.el6.x86_64
file /usr/lib/python2.6/site-packages/system_config_keyboard/keyboard.pyo from install of system-config-keyboard-1.3.1-3.el6.x86_64 conflicts with file from package system-config-keyboard-base-1.3.1-4.el6.x86_64
file /usr/lib/python2.6/site-packages/system_config_keyboard/keyboard_models.pyc from install of system-config-keyboard-1.3.1-3.el6.x86_64 conflicts with file from package system-config-keyboard-base-1.3.1-4.el6.x86_64
file /usr/lib/python2.6/site-packages/system_config_keyboard/keyboard_models.pyo from install of system-config-keyboard-1.3.1-3.el6.x86_64 conflicts with file from package system-config-keyboard-base-1.3.1-4.el6.x86_64

エラーの発生したパッケージを削除し、作業後に依存パッケージとともに再インストールすることにします。

# yum remove system-config-keyboard

依存性のため一緒に削除されるパッケージがあるのでメモしておきます(この場合anacondaとfirstbootが一緒に削除された)

再度トライ
# yum distribution-synchronization
今度は成功。

先ほど削除したパッケージをインストールします。
# yum install system-config-keyboard anaconda firstboot

古い情報が残っていて再インストールに失敗しました。rpmコマンドで直接対象のパッケージをアンインストールします。
# rpm -e system-config-keyboard-base-1.3.1-4.el6.x86_64
再度トライ。
yum install system-config-keyboard anaconda firstboot
これでOK。

カーネルの新しいバージョンのパッケージが残っているのでyumコマンド実行時に依存パッケージ欠如で怒られますが、ダウングレードしたバージョンで起動するので特に気にならないのであればこのままでも問題ないでしょう。気になるようなら削除すればいいと思います。

2012年7月12日木曜日

クライアントサイドJavascript UIの「擬似」テスト駆動開発

Web アプリケーションのテスト駆動開発において、クライアントサイドJavascript による UI の開発は最もテスト駆動にすることが難しい部分の1つだと思います。今回は、クライアントサイドJavascript による UI の開発において、テスト駆動開発のメリットを採り入れるにはどうするか、試行錯誤しつつ今こうやっていますよ、という方法を書こうと思います。

UIのテストは間接的に行う

そもそも UI のテスト駆動開発は難しいと言われています。数値や文字列が出力結果となるような他のプログラムと違って、UI はグラフィカルな変化が出力結果となるため、自動化テストにおけるアサーションを書きにくいからです。

といっても、確認する方法がないわけではありません。クライアントサイドJavascriptにおいて、画面上の位置、背景の色、幅、高さ、透明度など、見た目を構成する要素の多くはCSSプロパティとして取得可能です。

直接見た目が正しいかを確認できなくても、これらCSSプロパティを通じて間接的に確認することができます。自動化テストのアサーションもCSSプロパティの値を対象に行えばよいでしょう。

また、クラスを付与・削除することによってCSSの適用範囲を変えて視覚的効果を実現する場合もあります。その場合はクラスの存在の有無を確認すればよいでしょう。

ではそれでテスト駆動で開発がスタートできるかというと、そうは問屋が卸しません。

外部ライブラリがJavascriptのテスト駆動開発を困難にする

Javascript のテスト駆動開発を困難にしているもう1つの原因は外部ライブラリです。 時間と労力の節約のため、先人の知恵を用いるのは有効な方法です。jQueryやその上で動くプラグイン等を始めとして先人の知恵の結晶であるライブラリが数多く存在します。しかしながら、テスト駆動開発においては「得られる結果を先に決める」ことが重要になるため、外部ライブラリはその点で不利です。

UI 上で、ある効果を実現したいと思った時、採用することにした外部ライブラリがどのCSSプロパティの値をどう変化させるのかコーディング前に正確に把握することは一般的に困難です。ライブラリのAPIドキュメントを読んでもわからない場合が多いです。この状態ではテストを先に書くことができません。テストファーストで開発を進める最初の1歩が難しいのです。

「擬似」テスト駆動開発による解決策

そこで、本来のテスト駆動開発の流儀からは外れますが、まず目的の効果を部分的に得られる段階まで、テスト無しで実装を行なってしまいます。 これを「試験的な実装」と呼ぶことにします。

そして、試験的な実装がどのようにCSSプロパティやクラスを変化させるか分かったら、それらの変化を自動化テストのアサーションとして記述します。ここで書くテストは、試験的な実装を全てカバーするようにします。そうすることでテストコードを伴わない実装をなくします。この段階でテストは全てグリーンです。

次に、テストコードを本来満たすべき仕様に合わせて変更します。試験的な実装を行ったおかげでCSSプロパティやクラスの変化の仕方はわかっているはずですので、書くべき自動化テストコードがわかるはずです。もしまだ分からないのなら、分かるようになるまで試験的な実装を進めて、そのぶんテストコードを書き足してから、上記作業を行います。

この時点でテスト結果はレッドです。ここから先は、通常のテスト駆動開発になります。レッド→グリーン→リファクタリングのサイクルを繰り返していきます。

開発を進めていくと、またテストファーストが難しい状態になるかもしれません。その際には再度試験的な実装を行い、試験的な実装を全てカバーするテストを書いてグリーンとし、そのテストを本来の仕様に合わせてレッドにします。そこから再びレッド→グリーン→リファクタリングのサイクルをスタートします。

このように、完全にテストファーストではありませんが、試験的な実装をうまく使うことで擬似的にテスト駆動開発を行うことができます。

まとめ

  • UI 開発では視覚的効果を直接自動化テストで確認するのは困難だが、クライアントサイドJavascriptにおいてはCSSプロパティや クラスの変化を確認することで間接的にテスト可能。
  • 「テスト無しの試験的な実装 → グリーンなテスト付きの試験的な実装 → 本来の仕様に変更しレッドとなったテスト」というプロセスを最初及び適宜途中に挟むことによって、擬似的にテスト駆動開発を行う。

2012年6月22日金曜日

Eclipse EGit によるタグの作成とプッシュ

バージョン管理システムにおけるタグとは、特定の時点でのリポジトリの状態です。一般的にソフトウェア開発ではリリースしたバージョンをタグとして保存することが多いようです。私の場合現在仕事でアジャイルで開発しており、イテレーションごとにタグを作成する方針を採っています。

Eclipse でEGitプラグインを使っていますので、タグの作成方法について書こうと思います。(使用しているのはEGit 1.3.0.201202151440-r です。)

Gitにおけるタグの種類

まず、Git のタグには3種類あります。(参考・・・Git公式サイト:2.6 Git の基本 - タグ
  • Lightweight (軽量)タグ
    特定コミットへの単なるポインタです。 
  • Annotated (注釈付き)タグ
    チェックサムが付き、タグを作成した人の名前・メールアドレス・作成日時・タグ付け時のメッセージを保持します。 
  • Signed (署名付き)タグ
    Annotated (注釈付き)タグにさらにGPG秘密鍵による署名を付与します。対応する公開鍵により検証を行うことができます。 

EGitがサポートするタグ

これらのうち、EGit でサポートされているのはAnnotated (注釈付き)タグのみです。とはいえ、公式ドキュメントでも Lightweight (軽量)タグよりもAnnotated (注釈付き)またはSigned (署名付き)タグの利用が推奨されていますし、GPGで署名を検証する必要がある場面は少ないと思いますので、Annotated (注釈付き)タグのみで十分な場合が多いでしょう。

なお、Lightweight (軽量)タグの作成は出来ませんが読み取りは可能です。Signed (署名付き)タグについてはサポート外とされています。(参考:Light-weight and Signed Tags | EGit/User Guide

タグの作成

タグを作成するにはプロジェクトを右クリック→チーム→拡張→タグ...とメニューをたどり、タグ名と注釈を入力して「OK」ボタンをクリックすればタグが作成されます。


タグを作成した後再度タグ作成のダイアログを開いてみると、作成したタグが追加されています。なお、アイコンに黄色い人のマークが付いているものが注釈付きタグ、黄色い人のマークがないものは軽量タグです。


ちなみに軽量タグが存在する理由は、もともとリポジトリをSVNサーバから移植した際に、SVNサーバ上のタグが自動的に軽量タグに変換されたからです。(前述のとおりEGit上では軽量タグを作成できません。)

タグのプッシュ

デフォルト設定ではローカルリポジトリに作成したタグはリモートにプッシュしないようになっています。これはEGitだからというわけではなくそもそもGit自体そういう仕様のようです。

設定は以下のメニューから変更します。プロジェクトを右クリック→チーム→リモート→アップストリームからプッシュ構成…(ちなみにどうでもいいですが「アップストリームからプッシュ構成…」って誤訳ですよね。正しくは「アップストリームへプッシュ構成…」だと思います。)

出てきた「プッシュ構成」ダイアログで「参照マッピング」の「編集…」ボタンをクリックします。


「Push Ref Specifications」ポップアップが出ます。「Add All Branches Spec」ボタン、「Add All Tags Spec」ボタンをクリックして設定を追加し、「完了」ボタンをクリックします。


参照仕様が追加されているのを確認し、「保管およびプッシュ」ボタンをクリックします。
これでローカルリポジトリに作成したタグがリモートにプッシュされます。

2012年6月17日日曜日

[アジャイル] 不確実性コーンのモデル式とベロシティ予測

現在仕事で担当しているプロジェクトではアジャイル開発を採用しています。

アジャイル開発では繰り返し単位であるイテレーションにおいてどのくらいの進捗があったかを表す値をベロシティと呼び、このベロシティの見積りがスケジュールを立てる、あるいは進捗の予測を立てる上で非常に重要です。

マイク・コーン著 「アジャイルな見積りと計画づくり」(安井力、角谷信太郎 翻訳) によると、ベロシティの見積りには不確実性コーンを活用できる、とあります。
(不確実性コーン自体の説明はこちら→ アジャイルな見積もりと計画づくりに学ぶ 不確実性のコーン | Act as Professional - hiroki.jp by HIROCASTER

その方法は実施したイテレーション数に基づき、ベロシティの予測値に幅を持たせるための下限係数と上限係数を不確実性コーンによって決めるというものです。具体的な数値も示されています。

実施したイテレーション数下限係数上限係数
10.601.60
20.801.25
30.851.15
4以上0.901.10
(出典:「アジャイルな見積りと計画づくり」p.194 表16.1)

例えば実施したイテレーション数が2の場合、下限係数0.80、上限係数1.25ですので、仮にベロシティの実測平均値を10とすれば、ベロシティの予測は8~12.5の範囲ということになります。

しかしイテレーション数で決めるのもいいですが、それだと全イテレーション数の違いを計算に含めることができません。例えば、実施したイテレーション数が2の場合、全体のイテレーション数が8の時は進捗率25%ですが、全体のイテレーション数が15の時は進捗率13%です。

進捗率が違えば、本来なら不確実性コーンによる下限係数と上限係数も違わなくてはなりません。そこで不確実性コーンのモデルを近似式で作って進捗率から下限係数と上限係数を計算できるようにできないか、と考えました。

というわけで作ったモデル式がこちらです。

下限係数log10(63+進捗率×937)/3
上限係数2-log10(16+進捗率×984)/3
(0 <= 進捗率 <= 1)

Excel でグラフを出すとこんな感じになります。

不確実性コーンのモデル式とグラフ
不確実性コーンのモデル式とグラフ

対数関数を選んだのは単純に不確実性コーンに合わせやすいと思ったのが理由です。

進捗率は「実施したイテレーション数/全イテレーション数」でもいいですが、「完了ストーリーポイント/全ストーリーポイント」のほうが正確だと思います。後者の方が実際の状況を表していますので。

ここで「アジャイルな見積りと計画づくり」でも指摘されているように、プロジェクトの進捗が進んでくると不確実性コーンの右の方では下限係数・上限係数共に1に近づくため、ベロシティの幅を持たせるには範囲が狭すぎるという問題があります。上限係数の方は下がっても問題ないかもしれませんが、下限係数の方を上げ過ぎるとベロシティが落ちるリスクを少なく見積もりすぎてしまうので危険です。

したがって下限係数による予測はプロジェクトの進捗にともなって実績値による指標に置き換える必要があります。「アジャイルな見積と計画づくり」ではベロシティの実績値による指標として

  • 直近8イテレーションの平均値
  • ワースト3の平均値
  • 最後のイテレーションの値

の3つを挙げています。これらのうち最初の2つが統計的意味を持つので、私は次のようにしています。

平均予測
ベロシティ
直近8イテレーションの平均値(小数点以下四捨五入)
(実施したイテレーション回数が8より少ない場合は全ての平均値)
最高予測
ベロシティ
平均予測ベロシティ×不確実性コーンの上限係数(小数点以下四捨五入)
最低予測
ベロシティ
平均予測ベロシティ×不確実性コーンの下限係数(小数点以下切り捨て)

ワースト3の平均値(小数点以下切り捨て)
のうち、値の小さい方

上記のようにすることにより、プロジェクト中盤~終盤にかけては下限係数による予測よりもワースト3平均のほうが小さくなるため、実測に基づくより安全な値を採用することができます。

2012年6月16日土曜日

Symfony1.4でログフォーマット変更方法の公式ドキュメントが間違っている件


Symfony1.4 でのログフォーマットの変更方法が書かれた公式ページは次のリンク先にあります。
http://www.symfony-project.org/gentle-introduction/1_4/ja/16-Application-Management-Tools

そこではアプリケーションの factories.yml を次のように変更すると書かれています。

all:
  logger:
    param:
      sf_file_debug:
        param:
          format:      %time% %type% [%priority%] %message%%EOL%
          time_format: %b %d %H

しかしこれをそのまま書いても効きません。正しくは、

all:
  logger:
    param:
      loggers:
        sf_file_debug:
          param:
            format:      %time% %type% [%priority%] %message%%EOL%
            time_format: %b %d %H

となります。追加した “loggers:” 以降の行のインデントは1段深くします。

以下、発見の経緯。

公式どおり設定しても効かないからおかしいと思って色々調べると、 factories.yml のペルプに行き着いて、形式の違いに気づきました。
http://www.symfony-project.org/reference/1_4/ja/05-Factories#chapter_05_logger

念のため、Symfony のパッケージ内部を覗いてデフォルト設定を確認すると、
(lib/vendor/symfony/lib/config/config/factories.yml)

all:
  logger:
    class: sfAggregateLogger
    param:
      level: debug
      loggers:
        sf_web_debug:
          class: sfWebDebugLogger
          param:
            level: debug
            condition:       %SF_WEB_DEBUG%
            xdebug_logging:  false
            web_debug_class: sfWebDebug
        sf_file_debug:
          class: sfFileLogger
          param:
            level: debug
            file: %SF_LOG_DIR%/%SF_APP%_%SF_ENVIRONMENT%.log

となっています。
なるほど確かに “loggers:” が足りない、と言うことで前述の通り設定して解決しました。

2012年6月15日金曜日

Eclipse EGit で作業ファイルの変更を個別に元に戻す方法

ファイルをいじっていて、前回コミットした状態まで戻したくなることってありますよね。

git コマンドを使う方法なら検索すると色々出てきますが、せっかく Eclipse を使っているので、ここは EGit プラグインから GUI 上で操作をしたいところ。

これが Subversion の場合だと Subversive プラグインには「ファイルを右クリック → チーム → 戻す」メニューがありましたが、EGit では「チーム」メニューにはそれらしきものがありません。

さらに検索すると、Stack Overflow で同様の質問をしている人を発見。
http://stackoverflow.com/questions/6788881/undo-single-file-local-uncommitted-change-in-egit-e-g-svn-revert
Right click on the file -> Replace With -> File in Git Index
そっちのメニューにあったのか。

Pleiades で日本語化している場合は例えばHEADに戻す場合は
ファイルを右クリック → 置換 → HEAD改訂
とやればできます。

いやはや、EGitではファイル1つのためにもハードリセットしかないのかと一瞬思いましたが、さすがにそんなことはないって話です。

2012年6月13日水曜日

Symfonyのlimeで使えるCSSセレクタのまとめ

Symfony でユニットテストやファンクショナルテストをする際、組み込みのテストフレームワークである lime を使うことが多いと思います。

しかしながら lime はネット上の情報源が少なく、特にテスト駆動開発をしようと思うと結構苦労します。結局 Symfony パッケージ内部のソースコードを見て調べることもあるでしょう。

ファンクショナルテストにはレスポンス内容を検査する sfTesterResponse::checkElement() メソッドの使用頻度が多いので、sfTesterResponse::checkElement() で使えるCSSセレクタをソースコードを見て調べてみました。対象バージョンはSymfony 1.4 です。

sfTesterResponse::checkElement() から CSSセレクタによってノードを選択する際、最終的に sfDomCssSelector::getElementsForNode() およびsfDomCssSelector::matchCustomSelector()
が呼び出されています。その中で正規表現を用いて CSSセレクタの機能を実現しているようです。

以下、ソースに記述のあったCSSセレクタを列挙します。

IDセレクタ及びクラスセレクタ

#id
指定されたIDを持つ
.class
指定されたクラスを持つ

属性セレクタ

[attr]
attr 属性を持つ
[attr=”val”]
attr 属性を持ち、値が val に一致する
[attre^=”val”]
attr 属性を持ち、値が val で始まる
[attr$=”val”]
attr 属性を持ち、値が val で終わる
[attr*=”val”]
attr 属性を持ち、値に val が含まれる
[attr~=”val”]
attr 属性を持ち、値をスペースで区切ったうちの1つに val が一致する
[attr|=”val”]
attr 属性を持ち、値が val または val- で始まる

範囲を限定するセレクタ

elem
要素セレクタ(elem 要素)
elem1 elem2
子孫セレクタ(elem1 要素の孫要素である elem2 要素)
elem1 > elem2
子セレクタ(elem1 要素の直下の子要素である elem2 要素)
element1 + element2
兄弟セレクタ(elem1 要素の直後の兄弟要素である elem2 要素)

擬似クラス

:contains(“val”)
文字列 val を含むもの
:nth-child(n)
親要素のn番目の子要素であるもの(n は 1 から始まる番号)
:first-child
親要素の最初の子要素であるもの
:last-child
親要素の最後の要素であるもの
:lt(n)
インデックス番号 n より前の要素(n は 0 から始まる番号、n 自体は含まない)
:gt(n)
インデックス番号 n より後の要素(n は 0 から始まる番号、n 自体は含まない)
:nth(n) または :eq(n)
インデックス番号 n の要素(n は 0 から始まる番号)
:odd
奇数番目の要素
:even
偶数番目の要素
:first
最初の要素
:last
最後の要素

注意点

lime の CSSセレクタには ID セレクタ及びクラスセレクタと属性セレクタを同時に使うと意図通りマッチしないというバグがあります(Symfony1.4.10で確認)。
これを回避するには、 ID 指定やクラス指定も属性セレクタと同様に [id=”foo”] や [class=”bar”] の形式にします。

例:
input#foo[checked] → 意図通りマッチしません。
input[id=”foo”][checked] → 意図通りマッチします。
input.bar[checked] → 意図通りマッチしません。
input[class=”bar”][cheched] → 意図通りマッチします。

ハマりやすいポイントだと思いますので注意してください。(私はハマりました・・・)

また、擬似クラス指定の :nth-child(n) の引数に指定できるのは整数のみです。

それから属性指定の [attr|=”val”] は本来 attr 属性の値が val そのものか、val- で始まるものを指定するはずですが、ソースコードを見る限り val で始まるものも許容しています。

参考

http://simonwillison.net/static/2003/getElementsBySelector.html
http://trac.symfony-project.org/ticket/6691

2012年6月9日土曜日

Symfony の url_for() の書き方でどれが速いか

Symfony でurl_for()やlink_to()にはいくつかの書き方がありますが、どれが一番速いのかと思っていたら公式ページに記述がありました。 なお、バージョンはSymfony 1.4 です。

速い順に、

  • url_for('ルート名', パラメータの連想配列)
    例: url_for('article_by_id', array('id' => $article->getId()))
  • url_for('@ルート名+パラメータのクエリ文字列')
    例: url_for('@article_by_id?id='.$article->getId())
  • url_for('モジュール/アクション+パラメータのクエリ文字列')
    例: url_for('article/read?id='.$article->getId())
となっているようです。

まず、モジュール/アクションの形式よりもルート名指定のほうが速いのは、リンクにマッチするルーティングルールを見つけるためにすべてのルールを探す必要がないという理由です。
さらに、パラメータをクエリ文字列ではなく連想配列で指定すれば、余分なパースが不要となり高速化できます。


参考
http://www.symfony-project.org/gentle-introduction/1_4/ja/09-Links-and-the-Routing-System#chapter_09_sub_9c0012b84a321294dad618cb44debc4004bf29c0

2012年6月8日金曜日

SVNリポジトリをGitに移行して中央リポジトリとして使う

現在関わっているプロジェクトでバージョン管理システムを Subversion から Git に移行したのでその時の手順をまとめてみました。

Subversion を移行する Git リポジトリは中央リポジトリとしてSSH経由でアクセスするものとし、develpoers グループに所属するユーザにアクセスを許可することにします。

また、Subversion との併用ではなく、Subversion リポジトリは残すものの基本的には Git メインで使っていくことを想定しています。

まず、Git リポジトリ用のディレクトリを作成します。
# mkdir /var/git
# cd /var/git
# mkdir repos-name.git
Git リポジトリを初期化する前に、 ディレクトリの所有者を共有用グループに変更し、グループ書き込み権限を与えます。
# chown root:developers repos-name.git
オプションに --bare と --shared=group を指定して Git リポジトリを初期化します。
# cd repos-name.git# git init --bare --shared=group
ここで作るリポジトリは中央リポジトリとして使うので、作業ファイルは不要なため --bare オプションを指定しています。また、 --shared=group オプションを指定すると、 グループでの共有に適した権限を付与してくれます。(グループ書き込み権限やディレクトリのsetgidなど。)

データを実際に移行する前に、設定ファイルを編集します。先ほどの初期化コマンドでディレクトリ内に config という名前で設定ファイルが生成されています。
# vi config

[core]
repositoryformatversion = 0
filemode = true
bare = true
sharedrepository = 1
[receive]
denyNonFastforwards = true
[svn-remote "svn"]
url = http://example.com/svn/repos-name
fetch = trunk:refs/heads/master
branches = branches/*:refs/heads/*
tags = tags/*:refs/tags/*
追加したのは赤字で示した部分です。

さらに、コミット履歴のユーザ情報を名前+メールアドレスに変換するためのファイルを用意します。
# vi ../users.txt

username = Mail Address <mail.address@example.com>
nanashi = Gonbe Nanashi <gonbe.nanashi@example.com>
ここまでできたら、あとは Subversion リポジトリからデータを取得して完了です。
# git --bare svn fetch -A ../users.txt
これでコミット履歴も含めて Git リポジトリにデータを引き継ぐことができます。


参考にしたサイト
http://blog.practical-scheme.net/shiro?20110226-sourceforge-git-migration
http://jarp.does.notwork.org/diary/200902b.html#20090213