2008年1月27日日曜日

日程調整アプリ - イベント 積み残し

まだイベント登録部分しか作っていないが、ここだけでもいろんな RoR のエッセンスが詰まっていて、結構勉強になった。ただ、まだやりたいことがいくつかあって実現できていないことがある。
  • イベントのリスト表示を管理者(パスワード認証)のみに有効にする
  • 新規イベントを作成したら、メールで申請者に通知
  • メール中の URL にアクセスすることでイベントを正式登録
まぁ、どれも今はなくてもとりあえずは困らないので、積み残して後の楽しみとして取っておくことにする。

日程調整アプリ - イベント 入力チェック

さて、次に改良するのは、入力項目のチェックだ。昔は RoR などなかったので、今回のようなアプリを作る場合は、たいてい入力項目のチェックを端折るか、簡単なものだけやることになり、十分なチェックはできなかった(していなかった)。(私だけ?)
だが、 RoR はこの辺も簡単に記述できるので早速実践してみた。

まずは空白チェック。
これはかなり楽チンに実現できる。 apps/models/event.rb に
validates_presence_of :name, :desc, :owner_name, :owner_email
と書くだけだ。
これで空白チェックが行われ、空白が発見されると、ページのトップにエラーメッセージが表示されるとともに、該当箇所が赤くなって訂正を促す。これはかなりいい。
エラーメッセージはデフォルトでは英語になっているので、適宜日本語に変更してもいいが、とりあえずはそのままにする。

次はパスワードの設定などでよく使われる、同じ項目を二度入力させてチェックする入力確認だ。
今回は、イベント作成時等に主催者にメールを飛ばすことを考えているので、メアドが正しく入力されているかを確認する。
まず app/models/event.rb に以下の行を追加する
validates_confirmation_of :owner_email
そして、app/view/events/new.html.erb の owner_email の入力フィールドの次に
<%= f.text_field :owner_email_confirmation %>
と、元の名前に _confirmation をつけた入力フィールドを追加する。
これだけで入力の確認が簡単に行える。

そして最後に、uuid の自動入力 の際には先送りした uuid の重複チェックを行いたいと思う。
この uuid の性格は、イベント登録時に自動的に入力され、以後は変更されない、というものだ。
なので、新規作成時に必ず呼ばれる before_create にて、自動入力と重複チェックを行うこととした。
まず、app/view/events/new.html.erb の頭に記載した uuid の自動入力の行を削除する。
そして、app/models/event.rb に以下のように before_create を定義する。
   def before_create
self.uuid = rand(256**16).to_s(16)
while Event.find(:first, :conditions => ["uuid = ?", self.uuid]) != nil
self.uuid = rand(256**16).to_s(16)
end
end
これで間違いではないのだが、同じ記述を二度しているところがどうにもエレガントではない。C の do-while っぽい書き方をすればいいのだが、よく分からなかったのだ。
この辺に Ruby に慣れていないことの弊害が出てしまった。

調べてみたら do-while っぽく書けるらしい、ということで変更
   def before_create
begin
self.uuid = rand(256**16).to_s(16)
end while Event.find(:first, :conditions => ["uuid = ?", self.uuid]) != nil
end

日程調整アプリ - イベント uuid でアクセス

id ではなく uuid でアクセスすべく、とりあえず uuid を自動入力するようにした。今回はこの uuid を使用して実際にアクセスできるように各所を変更してみる。

まずは、コントローラの各アクションでデータベース検索している
Event.find(params[:id])
の部分。通常だと params[:id] は、1 からインクリメンタルにセットされる id がセットされている(両方とも id だが、前者はURLで指定されるもので、後者はテーブルのフィールド)が、ここが uuid に変わるため、uuid が params[:id] になっているデータを検索するように変更する必要がある。そのような場合の検索方法は以下の通り
Event.find(:first, :conditions => ["uuid = ?", params[:id]])
これで uuid をキーに検索できるようになる

次に、各テンプレートの最下行等に表示される、"Edit" 等のリンク先の変更を行う
まず、"Show" へのリンクだが、通常は @event となっている link_to の第二引数を
{:id => @event.uuid, :action => "show"}
に変更する。"Destroy" も同じだ。
次に、"Edit" へのリンクだが、通常は edit_event_path(@event) となっている部分を
edit_event_path(@event.uuid)
に変更する。
なお、app/view/events/index.html.erb においては、for 文で回しているので、@event の部分は event になる。

新規作成や修正のアクション(create/update)は、実行後に redirect_to で表示(show)のアクションに飛ぶが、これも変更が必要である。これも、先ほどの "Show" へのリンクの変更と一緒で
:id => @event.uuid, :action => "show"
に変更する。

あと一つ変更すべき場所がある。それは、修正時のページ中の form 文だ。
form の action にも id が渡されるので、これを uuid に変更する必要がある。
対応する箇所は app/view/events/edit.html.erb の form_for の部分だ。通常は @event になっている箇所を以下のように変更する。
@event, :url => {:action => :update, :id => @event.uuid}

これで、一通りイベントに対するアクションを id から uuid をキーに行えるようになった

2008年1月26日土曜日

日程調整アプリ - トップページの切り替え

RoR は、始めてみて、テストアプリを作ってみて、動かして、ブラウザから見て「あ、動いた」となるまでが非常に早い。そしてその時誰もが見るページが "Welcome aboard" などと書かれたページだ。そして、このあとどうしたらいいか、があっさりと書かれている。

そして、徐々にアプリを拡張していくが、ここから先で作る部分は基本的に controller/action/id の形式の URL でアクセスするように作成される。

… となると、アプリが出来上がってもトップページは "Welcome aboard" のままなのか???

そんなわけないので、どこかで設定が変えられるはずなのだが、実はこの時点では参考書が届いてなかったので、本屋で立ち読みして、
map.connect '', :controller => 'events', :action=> 'new'
とどこかに書けばいいらしいことが分かった。(今回は、イベントの新規作成ページをトップページにする予定)

ただ、問題は「どこかに」の部分を忘れてしまったのだった。仕方ないので、RadRails の左の小窓をつらつら見ながら、app/controller/application.rb がそれっぽい(頭に "helper :all" などと書いてあるし)ので、書いてみたが効果なし。

結局本屋で翌日また立ち読みして、config/routes.rb であることが分かったが、それでも効果がない。
結局本屋で翌日またまた立ち読みして、public/index.html を消去することが分かった。^_^;

これでトップページがちゃんと切り替わったので特に問題はないのだが、実はトップページの切り替えに関しては専用の書き方が用意されていた。
map.root :controller => 'events', :action=> 'new'

日程調整アプリ - イベント uuid 自動入力

通常 RoR では、テーブルには自動的に id フィールドが追加され、この id でアクセスするようになっている。まぁ、これはこれで便利でいいのだが、今回作成しようと思っている日程調整アプリでは、
1. 誰でも新たな日程調整が始められる
2. 他人の日程調整は簡単に覗けないようにする
という仕様にしているので、このままだとちょっと都合が悪い。

イベントごとにパスワードを設けるという方法もあるが、何でもかんでもパスワード入力を求めるのは個人的に好きではないので、イベントにUUIDを割り当て、それでアクセスする。こうすることで、1,2 の仕様を満たすこととした。(UUIDに関してはここを参照のこと)

そんな理由もあって、イベントテーブルには uuid というフィールドを設けてあったわけだが、これはユーザに入力させるものではないので、自動で入力させる必要がある。それをどうやって実現するかだが、とりあえずは、新規作成ページ(app/view/events/new.html.erb)の頭のほうに
<% @event.uuid = rand(256**16).to_s(16) %>
と書いて自動的に uuid に値を埋めるようにした。
UUID の生成方法はちゃんとした方法もあるだろうし、 plugin もあるようだが、面倒なので上のようにした。

また、同じく new.html.erb で uuid の部分を以下のように hidden フィールドにした。
<%= f.hidden_field :uuid %>
もちろん、そのすぐ上のテキスト表示はコメントアウトなりして消しておく。

ただ一点気になるのはUUIDの重複である。UUIDは一応、「重複や偶然の一致」を想定しなくていいとはなっているが、チェックできるならチェックしておきたところである。が、とりあえず今のところは先送りで… (validate とかでやるのかな?)

日程調整アプリ - イベント 作成

ざっくり仕様から、まずはイベントを登録できるようにする。

特に見た目を気にするアプリではないので、CRUD 操作で事足りる部分に関しては、可能な限り scaffold をベースに作ることにする。その点で、このイベント部分は適している。というわけで、早速 scaffold でイベント登録の部分を作る

RadRails では、下の小窓に Rails 特有の機能を簡単に実現するヘルパー(?)が用意されているが、その中の "Generator" で scaffold も作成できる。
左側のリストから scaffold を選択するが、最初に開いたときはリストが表示されない場合があるので、その際は右上の Referesh ボタンを押すと出るようだ。
右側のフィールドには引数を書く。 RoR 2.0 からこの書式が変わっているので注意が必要だ(先の記事参照)。 Event name:sring desc:text uuid:string owner_name:string owner_email:string

2008年1月17日木曜日

日程調整アプリ - ざっくり仕様

というわけで、手探りで日程調整アプリの作成にとりかかる

まずはざっくりと外部仕様を決める
  1. 誰でも新たな日程調整が始められる
  2. 他人の日程調整は簡単に覗けないようにする
  3. パスワード認証はないほうがいい
  4. 性善説に則る(他人の入力は変更できる)か?
  5. 性悪説に則る(自分の入力のみ変更できる)か?
  6. 日程の候補は、日付のみと、日付+時間と、それ以外(自由)
  7. 日付は、カレンダーから選択したい
  8. 参加者の重み付けができる(偉い人の日程を優先とか)といい
  9. 参加者は追加できる
  10. 入力候補は◎○△×から選ぶ
  11. 未入力者に催促できる
  12. 集計結果は一覧できる(主催者用はランキングも表示できたらいい)
  13. 最終結果をアナウンスできる
操作フローはこんな感じで
  1. 主催者はイベントの概要を入力する
  2. アプリは主催者にメールで主催者用URLを通知する
  3. 主催者は参加者のメアドを登録する
  4. 主催者は日程の候補を入力する
  5. アプリは参加者にメールで参加者用URLを通知する
  6. 参加者は出欠を入力する
  7. 主催者は集計結果を見る
  8. 主催者は未入力者に催促メールを送る
  9. 主催者は最終結果をメールで送る
といった感じだろうか

これらの仕様から、まずデータベースを考えてみると、以下の4つのテーブルが必要そうだ
  • イベント(event)
    • イベント名(name)
    • イベント詳細(desc)
    • 固有ID(event_uuid)
    • 主催者名(owner_name)
    • 主催者メアド(owner_email)
  • 日程候補(option)
    • イベントID(event_id)
    • 日程内容(desc)
  • 参加者(member)
    • 参加者名(name)
    • 参加者メアド(email)
    • イベントID(event_id)
  • 入力結果(attendance)
    • 参加者ID(member_id)
    • 日程候補ID(option_id)
    • 出欠(attend)
間違ってても後で簡単に変更できるようなので、とりあえずこんな感じにしておく

次回はイベント入力画面を作成してみたい