さて、Rails勉強会@東京 第9回に行ってきた。昨日はLL Ringにも行ってきて、そっちのレポートもまだ書き終わってない。そっちはあとで書く。多少前後するけれども、先にRails勉強会のほうをレポートしてしまう。
今回もドリコムの東京オフィスを貸していただいて開催した。
形式は前半・後半に時間を区切ってオープンスペース形式。要ポジション・ペーパーというのも変わらず。
この形式はいいんじゃないかなと思ってる。最初、秋葉原でやっていた頃、参加者全員が集まって座れる場所を確保できなかった故に角谷さんの発案でオープンスペースになったそうだけれど(第0回)、これによって、最多で10人程度に分かれるから参加者のコミュニケーションが促進されてる。Listen Only Memberを発生しにくい仕組みになってるんじゃないかと思う。
反面、少数であるが故に全員が受け身だとひたすら沈黙になってしまうのは確か。一時期、そういう感じでいつまで経ってもセッション案が出なかったりしたけれど。でも、もろはしさんの提言で事前にセッション案を十分に出しておくというのが復活して、そういう難点は解消されつつある。
ポジション・ペーパーについてはこれはよい作用をしているのは言うまでもない。どういう人なのか、Ralsについてどういう立場なのか、どう考えているのか、どのくらいの知識があるのかがはっきり分かるから、参加者の一体感を高める役に立つし、そこからセッションが膨らむことも多々ある。
唯一ポジション・ペーパーの難点を言うならば、私が初参加のときに感じたのだけれど、一般的な手法ではないが故の敷居の高さがある。こういう集まりに参加したことがないと、何かを書いていいのか分からなくて不安だ。一応、勉強会の公式ページから参照されてる解説にも作例はあるけれど、ね。これは勉強会に参加した人が積極的に自分のベーパーを公開することで敷居を低くするしかないんじゃないかと思う。で、私も過去のレポートで何回か、公開している。
今回は出かける間際に慌てて書いたので私のポジション・ペーバーは手書きだったのだけれど、内容はこんな感じだ。
えっと、ポジション・ペーパーを見せながらの自己紹介のとき喋った内容を補足すると、
前半は3つのセッションに分かれた。
私はSeleniumセッションに出た。ちょうどSeleniumを本格的に導入しようとしているところで、私は見よう見まねで使ってるだけだったのでちゃんと勉強したいところなのであった。セッションオーナーは沢田さん。先日のGREE勉強会でSeleniumを扱って、沢田さんはそのときにSelenium IDE作者の笠谷真也さんの話を聞いてきたという。
Seleniumはブラウザによる操作テストを自動化するためのツールである。大元のSeleniumはHTMLとJavaScriptのセットで、これをテスト対象のサーバーに配置し、テストケースを書いておくと、ブラウザでSeleniumのHTMLを開いた際に記述したテストケースを自動的に実行し、その結果を検証してくれる。大元のSeleniumではテストケースはHTMLテーブルで記述することになっている。
SeleniumはJavaScriptをサポートするメジャーなブラウザには一通り対応していて、処理系依存の部分もライブラリ内でなんとかしてくれる。
テストケースの記述に使えるブラウザ操作命令やアサーションは結構豊富で、リファレンスで一覧できる。Ajaxで要素が更新されるのをしばらく待ってから検証したり、クリック位置依存の挙動を確認するために特定座標のクリックを模したり、Drag & Dropまで対応している。アサーションにはXPathを指定して内容を検証したりもできる。
テストケースをHTMLで書いてテスト仕様表として閲覧可能にしておくというやりかたは、FITみたいに受け入れテストやなんかへの適用を考えているものなのかもしれない。でも、なにしろHTMLの表組だから記述が冗長だし、いちいちSeleniumの操作コマンド名やアサーション名を覚えておいて記述するのも大変だ。
そこでSelenium IDEがある。これは株式会社KBMJの笠谷さんが開発したFirefox拡張で、テストケースを書く手間を省いてくれる。Selenium IDEの赤い「記録開始ボタン」を押すとその後のブラウザ操作はすべて記録される。その場で記録を「再生」することも可能だし、記録は設定に応じた形式で書き出すことも可能だ。書き出してコピペすれば他のブラウザでの検証に使い回せる。
これは沢田さんはよく知らないとのことだったけれど、 RCというのはRemote Controlで、Proxyとして作用してブラウザにテストケースを実行させるらしい?
そして、Rails使いとしては本命のSelenium on Railsである。これはRailsのプラグインとして提供されている。これをインストールするとSeleniumControllerというコントローラーが追加されて、これが大元のSeleniumでいうHTMLおよびJavaScriptの役割を果たしてくれる。サーバーを起動して、/selenium/にアクセスするとオリジナルのSeleniumと同じような画面が出てくるはずだ。そして、画面左にあるテストケースを実行できる。なお、SeleniumControllerはtest環境でのみ動く。サーバーを起動する際にproductionやdevelopmentで起動すると、アクセスできないので注意。
Seleniumだけでは、毎度おなじみにしてRails使いは解放されたはずの「データベースがテスト実行で汚染されて他のテストに影響してしまう」問題をどうしたものか悩む。でも、Selenium on Railsではテストケースの最初で/selenium/setupにアクセスすればfixtrueに従ってデータベースを初期化してくれるから、簡単だ。
さらに、Selenium on RailsではHTMLテーブルの代わりに次の2種類のフォーマットを利用できる。
このRubyコードをつくるのにもSelenium IDEを利用できる。Selenium IDEの出力フォーマット指定のところで"Selenium IDE 0.7 (deprecated)"を選択すればよい。deprecatedと書いてあるのが気になるし、Selenium on Railsのテストケースにはトップレベルにベタにコードを書かなければならないのに対して、IDEが出力するコードはクラス定義、メソッド定義で括られている。これは、"Selenium IDE 0.7 (deprecated)"と書いてあるのが本来はRubiniumというまた別のSelenium派生ツールのためのフォーマットだからだそうだ。でもコマンドには互換性があるので、このRubinium用テストケースのテストメソッド内だけコピペすればSelenium on Railsで利用できる。
一応、一通りの話が終わってあとはだんだん雑談へシフトした。Seleniumに限らず、標準のassert_tagであれ、Hpricot test extensionであれ、出力されたHTMLを検証するにはある種の困難がある。要素を特定するためにXPathを使ったりしてなんとか要素を表す訳だけれども、viewをいじるとXPath式がすぐに無効になってしまうからだ。
私は「でも、そういうところで悩まないようにアサーションを掛けたいような対象にはきっちりidを振っておくのは良い習慣かもしれない」と言ったけれど、「でもデザイナーさんが絡むとその理屈が通じなかったりするからなー」と言われたり。
Viewに対してのテストで、出力されたHTMLを解析してアサーションを掛けたいのだけれど、parserが遅い問屋なので何が良い? という話。
お薦めはHpricot。一部Cで実装されているので速いし、テストケースで自動的にHTMLを解析してくれてアサーションに使えるようにするHpricot Test Extensionもある。
ちなみに、Hpricot Test Extensionは一度解析したデータをずっとキャッシュしてるので、テストメソッド内で複数のリクエストが発生するIntegration Testに使うと変なことになる。私はそこを修正して動かしてるけど、あぁ、パッチ送らなきゃ。
というか、Viewが生成するHTMLに対するアサーションはFunctional TestとIntegration Testとどちらに置くのが正しいんだろう。
かずひこさんにも本にサインしてもらった。昨日からいっぱいサイン貰ってる。
後半も3つのセッションに分かれた。
私は一応has_one :throughセッションのオーナーを務めた(努めたというほうが正しい気がするが)。
先日作ったhas_one :throughには予想以上に反響があった。それでセッションのネタ出しにあたってhas_one :throughのセッションを求められたのだが、何しろあれからあまり進展してない。
まず、has_one :throughをActiveRecord本体に対するパッチとして書き直すことだけは決めてある。でも、Railsパッチの書き方に書いてある「テスト駆動だテスト駆動」「ちゃんとテストしろよ」というのに激しく頷いてテストを書こうとはしたものの、ActiveRecordのテスト用モデルの複雑さにちょっと困惑していたのであった。とりあえず整理しようと思ってクラス図もどきを書いてみたものの、さて、どうしたもんか、と。
それで、「私と一緒にhas_one :throughを作らないか」セッションならやれますよー、と言ってセッション主催となった。そして、舞波さんや瀧内さんや、実際にパッチを書いているひとたちの参加もあってとても実りあるセッションとなった。
みんなでクラス図もどきを眺めた末、Post - Categorization - Categoryモデルにおいて、Postが
has_one :main_categorization, :conditions => .... has_one :main_category, :through => :main_categorization
とかするのが一番適切かな、ということになった。
これ、別にhas_manyしてるモデルを:throughしてもいいのだけれど。
has_many :categorizations has_one :main_category, :through => :categorizations, :conditions => ...
こんな風に。でも、この場合Categorizationは:conditionsで絞られて、結局唯一の行だけを:throughしてhas_oneしてるわけだから、表記としては単数系の
has_many :categorizations has_one :main_category, :through => :categorization, :conditions => ...
のほうが自然だね、ということになった。ここは実装にちょっと工夫が要るかな。
これらの詳細は舞波さんからの告知を待て。期待して良いはず。私が書くより舞波さんにAA付きで書いてもらった方がきっと読者は感動できるだろう。
どちらも、結局はレコードのアイデンティティをどう捉えるのが自然か、というところに通じるのね。舞波さんが考えていたacts_as_tabと、後述するABDの読み込みとを通じて、habtmにあってhas_many :throughが失ったものは本質的に何かというのをみんなで検討した結果、その失われたものを取り戻すhabtm 2.0の構想が生まれた、とだけ言っておこう。
acts_as_tabと関連して舞波さんがスマートにタブ表示を実現するCSSを欲しがっていたのだけれど、これはshachiさんが近いうちに実装してまとめてくださるとのこと。
さてさて。このセッションで得た大きな収穫としてABD理解の深化もあった。
Searsar Conference 2006 SpringではぶさんのABDの話を聞いて、それでえらく感動してABDかわいいよABDと言い続けてきたけれど、実はABDについて私は大きな勘違いをしていることが判明。
はぶさんが「アクティビティ系を忘れるな」と指摘なさってるけれど、私はまさにアクティビティ系を見落としてた。で、「アクティビティ系って何だっけ?」と慌てたのであった。
Seasar Conferenceのときのはぶさんの資料を見てほしい。32ページ目の図を見ると、売上の日付は独立した「売上」テーブルになっている。「売上_関連」テーブルとは別物だ。
私の考えていたやり方だと、この2つのテーブルは結合してるのね。売上があったというイベントに対応する「売上」テーブルがJoin Tableとして 「顧客」「商品」「売上明細」を結びつけていて、日付は「売上」の属性になる。これはDHHがPerson - Membership - Memberのモデルで説明してるのと同じところに落ち着いて、だからRailsがActiveResourceで目指そうとしてるのと大体同じだ。
でも、はぶさんの説明は違う。売上があったという事実(Fact)の記録は「売上」表に記録されて、これはForeign Keyを持たない。そして、「顧客」「商品」「売上」「売上明細」を結びつけるActivityの記録が「売上_関連」テーブルだ。はぶさんが言ってるイベント系が「売上」でアクティビティ系が「売上_関連」であるらしい。
そういえばSearsar Conferenceでもそう言っていた気がするし、この資料自体にそう書いてある。この資料は今まで何度も読み返したのだけれど、でも私は何か違った思い込みで資料を読んでいて、目が曇っていた。私の目は節穴か。セッションの中でみんなで徹底してこの資料を読み込んで、ようやく、どうも違うらしいということが分かってきた。
そうすると、はぶさんのやり方はDHHのやり方より更にeXtremeだ。そして、今のところの私の思想は、DHHよりはeXtremeではぶさんよりは保守的だ。私がここしばらくRailsに「アクティビティ系を見落としたABD」を適用してきた限りでは、「結合してしまったイベント系+アクティビティ系」にアクティビティにまつわるロジックを持たせるとコントローラーにMVCのオーケストレイト以外のロジックが混ざり込まなくて割り合い綺麗に実装できるらしい。それで、私にはまだ、どうして日付をアクティビティ系の属性にしてはいけないのか理解できていない。ABDは「スーパーDBエンジニアならざるWeb屋にもできる楽な設計法」であると理解しているけれど、日付を分離して「売上」にするとそうでないときに比べてどのように楽になるのか理解できていない。
ともかく、私が前に書いた記事は非常に不完全。私の理解は今も不完全。ABDの方向性には賛同するけれども、結局は異なる考えを持つかもしれない。でも、今回のセッションでちょっとだけアクティビティ系が何なのかという理解に近づいた。
そして、私がABDの方向性の中に見いだす一番の価値はやっぱりアジャイルであることである。週単位でビジネスの視野自体が変化するような現場での、RDBに可能な限りの柔軟性である。たぶん、これははぶさんとは力点が違う。だから、私が今考えている力点の置きかた、そこから導きだされるABDに似てABDならざるモデリング、多分DHHの考え方にかなり似ているやりかた。そこには別の名前をつけるべきなのかもしれない。たとえそれが、試行錯誤を重ねた末に結局は本物のABDに収束していく可能性が高いとしても。
折角はぶさんが「強大な敵求む」と仰ってるので、敵になる気満々でこのABDもどきのことを「"Agile Building Datamodel"とでも名付けるか」、と言ったら舞波さんに「ややこしいから同じabbrはやめてほしい」と言われた。「べっ、別にActivity Based Datamodelなんか気にしてないんだからね(赤面)」感が漂って良いかと思ったのだが。
それにしても、みんなにガンガン意見を出してもらったおかげで悩んでいたhas_one :throughの実装方針もすんなり決まったし、habtm 2.0なんて素敵なものまで出てきた。「こういうブレーンストーミング系のセッションは効果的」とのshachiさんの評。
Selenium IDE で Selenium on Rails の形式でテストを記録するのは、こちらのフォーマットを追加するとより確実です。
http://wiki.openqa.org/display/SIDE/SeleniumOnRails