@m_seki の

I like ruby tooから引っ越し

壇上でなにをやっていたのか、咳サイドからのレポート。

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