XP祭りの話ね。
角谷さんとの合意なしに、咳からの視点で書いてます。
前日。
実際に30分でチャットが作れるのか、私には自信がなかったのでKoyaとDivを使って前日に予習しました。
koyaのsample/quitがそれ。基本的な機能までなら30分で作れることを確認。(divipを読みながらAjaxを使った着信通知を書いたらDivのバグを見つけてしまい時間超過。)
KoyaのAPIを角谷さんに説明しながら作っても60分あれば間に合いそうな気がする。
- Koyaの説明 (3m)
- 具の実装 (10m)
- DivとTofuの説明 (7m)
- UIの実装 ph1, ph2, ph3 (10m, 10m, 10m)
くらいな感じで行こう、と、勝手に考える。角谷さんとは相談してない方が面白い気がするので作戦は伝えない。
で、本番の前半。
段取りの打ち合わせががぎりぎりになり、LANの設定に手間取り、モニタ切り替え器だと思ったのがCPU切り替え器だったり、いやなムードで始まる。
事前にkoya, div, qdbmを角谷さんのMacBookにインストールしてもらってたので、角谷さんのマシンで開始。
「具から作るか、UIから作るか」について角谷さんと「具から」と決める。
「テストどうする?」で直ちにrspecでshouldな雰囲気でタイプするんだけど緊張気味で最初のテストの(作成ではなく)実行に手間取ってしまう。
Arrayで具を実装できた段階で思ったよりも時間が経っていたのでちょっと焦る。
irbを使って私のiBookと角谷さんのMacBookから具に発言を追加したり、一覧を眺めたりして動作を確認して最初の30分が終了。
角谷さんはノってくると加速する。
後半の30分。
Koyaを使った具に切り替えてみようかな、と思ったらqdbmの拡張ライブラリがrequireできない。原因は追究しないまま私のiBookで作業することにする。
前半で時間を使ってしまったし、角谷さんのマシンで具サーバが動いてるし、急遽Koyaじゃなくて、いつものdRuby作戦にする。
[UI] ---- [具] iBook MacBook
koya/sample/quitのUIを改造してさっき書いた具にあわせていく。QuitDBの実装を変更するか、UIを直接変更して新しい具に合わせるか10秒くらい議論して、UIを直接変更する作戦に。
Koya版と違ってauthorのオブジェクトがなかったり、いろいろ面倒かな、と思ったけど5mかからずUIが動き出す。
WebUIの変更は見れば動作の確認ができるのでRSpecなしですすめました。
UIはDivを使ってセッションや、UIの状態が住んでいるデーモンと、3行のCGIで構成されるので、最終的にはこんなプロセス構成。
[CGI] | [UI daemon] ---- [具] iBook MacBook
既存のWeb UIを改造するのはずるいかな、と一瞬考えたけど、RailsチームはScaffold使うだろうから気にしないことにする。
当初の目的(?)であるKoyaのお披露目はかなわなかったけど、角谷さんと楽しい時間を過ごせました。
アジャイル王子には引いたけどさ。
# quit_div.rb改造版 require 'div' module QuitDiv class QuitSession < Div::TofuSession DB = DRbObject.new_with_uri('druby://192.168.0.17:12345') def initialize(bartender, hint=nil) super(bartender, hint) @db = DB @base = BaseDiv.new(self) @author = nil end attr_reader :db attr_accessor :author def do_GET(context) update_div(context) return if do_inner_html(context) context.res_header('content-type', 'text/html; charset=ShiftJIS') context.res_body(@base.to_div(context)) end end class BaseDiv < Div::Div def initialize(session) super(session) @db = session.db @page = 0 @size = 5 end attr_reader :arrive def do_write(context, params) text ,= params['msg'] return if text.to_s.empty? @db.add(author, text) end def do_author(context, params) text ,= params['author'] return if text.to_s.empty? @session.author = text end def do_head(context, params) @page = 0 end def do_older(context, params) @page += 1 end def author @session.author end def bgcolor(author=nil) author = @session.author unless author return '#FFFFFF' unless author k ,= Digest::MD5.digest(author).unpack('L') r = 255 - (k & 0b11111) g = 255 - ((k >> 5) & 0b11111) b = 255 - ((k >> 10) & 0b11111) sprintf("#%02x%02x%02x", r, g, b) end def color 'black' end EDIT_FORM_RHTML = <<-EOD <%= form('write', context) %> <input type='text' name='msg' /><br /><input type="submit" value="Write" /> </form> EOD ERB.new(EDIT_FORM_RHTML).def_method(self, 'edit_form(context)') AUTHOR_FORM_RHTML = <<-EOD <%= form('author', context) %> login: <input type='text' name='author' /><input type="submit" value="yes" /> </form> EOD ERB.new(AUTHOR_FORM_RHTML).def_method(self, 'author_form(context)') READ_MESSAGE_RHTML = <<-EOD <table width=90%> <% @db.head().each do |leaf| %> <tr bgcolor='<%=bgcolor(leaf[0])%>'><td> <p><%=h leaf[1] %><p> <small><p style='color: gray'><%=h leaf[0] %>/<%= leaf[2].strftime("%H:%M:%S") %></p></small> </td></tr> <% end %> </table> EOD ERB.new(READ_MESSAGE_RHTML).def_method(self, 'read_message(context)') BASE_RHTML = <<-EOD <html lang="ja"> <head> <meta http-equiv="content-type" content="text/html; charset=Shift_JIS"> <meta http-equiv="content-style-type" content="text/css"> <style type="text/css"><!-- body{font-family:Verdana,sans-serif;margin:2% 20% 10% 20%;color:<%= color%>;background-color:<%= bgcolor%>;} input{font-family:Verdana,sans-serif;} #navi{text-align:right;} p{line-height:150%;} a{color:gray;background-color:<%= bgcolor %>;text-decoration:none;} a:hover{text-decoration:underline;color:white;background-color:gray;} --></style> <script language="javascript"> </script> <title>Quitter</title> </head> <body> <h1>quit</h1> <% if author %> <%= edit_form(context) %> <% else %> <%= author_form(context) %> <% end %> <%= read_message(context) %> <p id="navi"> <%= a('head', {}, context) %>Newest</a> <%= a('older', {}, context) %>Older</a> </p> </div></body></html> EOD ERB.new(BASE_RHTML).def_method(self, 'to_html(context)') ON_ERROR_RHTML = <<-'EOD' Content-Type: text/html; <html><head><title>Error</title></head><body><h1>Error</h1><pre> <%=h "#{$!} (#{$!.class})" %> <%=h $@.join("?n") %> </pre> EOD ERB.new(ON_ERROR_RHTML).def_method(self, 'on_error()') end end