2007-05-13(Sun) [長年日記]

_1 [Rails] 飲み会管理システム(1)

中ボスから話もあったので、Ruby on Railsの初アプリとして以下のような感じで飲み会管理システムを作ることにした。

  • 管理者がユーザ登録
  • 管理者が飲み会生成
    • 招待するユーザを選択して、案内をメール
  • ユーザが自分の情報を更新
    • 招待されている飲み会に参加するかどうか
    • アカウント情報更新

_2 [Rails] 飲み会管理システム(2)

飲み会管理システム = MeetingManager = mm を作ってみよう。

% rails mm

作られたconfig/database.ymlのままで問題はないので、データベース作成

% mysql -u root
mysql> create database mm_development;
mysql> create database mm_test;
mysql> create database mm_production;
mysql> quit

モデルを作成

% ruby script/generate model user
% ruby script/generate model event
% ruby script/generate model membership

db/migrateに、テーブルを作ったり消したりするファイルも出来たので、self.upの中を更新。 ユーザに見せる会員番号のためにaccountnumberを入れた。

class CreateUsers < ActiveRecord::Migration
 def self.up
   options = {
     :options => "DEFAULT CHARSET=utf8"
   }
   create_table(:users, options) do |t|
     t.column :nickname, :string
     t.column :accountnumber, :string
     t.column :mailaddress, :string
     t.column :root, :boolean, :default =>  1
   end
 end

宴会の案内はメールしたいので、maibodyでtextにした。

class CreateEvents < ActiveRecord::Migration
 def self.up
   options = {
     :options => "DEFAULT CHARSET=utf8"
   }
   create_table(:events, options) do |t|
     t.column :title, :string
     t.column :mailbody, :text
   end
 end

宴会に招待されるとmembershipが作られ、参加するとjoinedがtrueになる。 user_id, event_idは外部キーにしたいが、よく分からないので、integerにする。

class CreateMemberships < ActiveRecord::Migration
 def self.up
   create_table(:memberships, options) do |t|
     t.column :user_id, :integer
     t.column :event_id, :integer
     t.column :joined, :boolean, :default => 0
 end

実行。

% rake db:migrate

確かめてみる。

% mysql -u root mm_development
 mysql> describe events;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| title    | varchar(255) | YES  |     | NULL    |                |
| mailbody | text         | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+

管理者を一人、一般ユーザを一人足しておこう。

%  ruby script/generate migration AddDefaultUsers

class AddDefaultUsers < ActiveRecord::Migration
 def self.up
   User.create(:nickname => 'root', :accountnumber => '0000', :mailaddress => 'root@example.com', :root => 1)
   User.create(:nickname => 'test', :accountnumber => '9999', :mailaddress => 'test@example.com', :root => 0)
 end

% rake db:migrate

確認してみる。

% mysql -u root mm_development
mysql> select * from users;
+----+----------+---------------+------------------+------+
| id | nickname | accountnumber | mailaddress      | root |
+----+----------+---------------+------------------+------+
|  1 | root     | 0000          | root@example.com |    1 |
|  2 | test     | 9999          | test@example.com |    0 |
+----+----------+---------------+------------------+------+

_3 [Rails] 飲み会管理システム(3)

scaffoldを作る。

% ruby script/generate scaffold User User
% ruby script/generate scaffold Event Event
% ruby script/generate scaffold Membership Membership

サーバを起動して

% ruby script/server

http://0.0.0.0:3000/userにアクセスすると

Listing users
Nickname        Accountnumber   Mailaddress             Root
root            0000            root@example.com        true    Show    Edit    Destroy
test            9999            test@example.com        false   Show    Edit    Destroy

New user

が出た。すげー。

メインの処理用にMMコントローラを作る。

ruby script/generate controller MM login logout edit update

ザルな認証のために、フォームからnickname, accountnumberを入れさせる。

<h1>MM#login</h1>
<p>Find me in app/views/mm/login.rhtml</p>
<%= form_tag %>
ニックネーム
<%= text_field("user", "nickname") %>
会員番号
<%= text_field("user", "accountnumber") %>
<input type="submit" value="ログイン" />
<%= end_form_tag %>

controllerでは、try_to_loginが通ったら、"edit"に移る。 この辺は、RailsによるアジャイルWebアプリケーション開発のまま。 RailsによるアジャイルWebアプリケーション開発

class MMController < ApplicationController
 def login
   if request.get?
     session[:user_id] = nil
     @user = User.new
   else
     @user = User.new(params[:user])
     logged_in_user = @user.try_to_login
     if logged_in_user
       session[:user_id] = logged_in_user.id
       redirect_to(:action => "edit", :id => logged_in_user.id)
     else
       flash[:notice] = "invalid"
     end
   end
 end

models/user.rbでは、nickname, accountnumberが一致したら、try_to_loginで該当userを返す。

class User < ActiveRecord::Base
 def self.login(nickname, accontnumber)
   find(:first,
        :conditions => ["nickname = ? and accountnumber = ?",
                        nickname, accontnumber])
 end
 def try_to_login
   User.login(self.nickname, self.accountnumber)
 end
end

http://0.0.0.0:3000/mm/login にアクセスしたら

 LoadError in MmController#login
  app/controllers/mm_controller.rb to define MmController

と言われた。

mm_controller.rbでは、MmControllerではなく、MMControllerが定義されている。 2文字のコントローラを作ったのが失敗だったか。MmControllerに修正しよう。

ログインに成功すると

MM#edit
Find me in app/views/mm/edit.rhtml

が出るようになった。

_4 [Rails] 飲み会管理システム(4)

管理者として認証にパスしたら、という条件は後で考えるとして、飲み会管理画面からユーザを誘えるようにしよう。 とりあえず、editのときだけ誘えればいいや。

models/event.rbで、membershipと招待フラグ、参加フラグをhashで返せるようにしておく。

class Event < ActiveRecord::Base
 def memberships
   mhash = Hash::new
   m = Membership.find(:all,
                       :conditions => ["event_id = ?", self.id])
   m.each do |membership|
     mhash[membership.user_id] = membership
   end
   return mhash
 end
 def proposes
   phash = Hash::new
   memberships.each do |id, membership|
     phash[membership.user_id] = true
   end
   return phash
 end
 def joins
   jhash = Hash::new
   memberships.each do |id, membership|
     jhash[membership.user_id] = membership.joined
   end
   return jhash
 end
end

event_contoller.rbで変数に入れて

 def edit
   @event = Event.find(params[:id])
   @users = User.find(:all)
   @memberships = @event.memberships
   @proposes = @proposes.joins
   @joins = @event.joins
 end

event/edit.rhtmlで表示する。 form_tagが、end_form_tagでなくendで閉じられているようだけど、いいのかな。

<h1>Editing event</h1>
<% form_tag :action => 'update', :id => @event do %>
 <%= render :partial => 'form' %>
       <table>
<tr><td>ニックネーム</td><td>招待</td><td>参加</td></tr>
<% @users.each do |user| %>
<tr>
<td><%= user.nickname %></td>
<td><%= check_box("proposed", user.id, {:checked => @proposes[user.id]}, "yes", "no") %></td>
<td><%= check_box("joined", user.id, {:checked => @joins[user.id]}, "yes", "no") %></td>
</tr>
<% end %>
 <%= submit_tag '更新' %>
<% end %>
<%= link_to 'Show', :action => 'show', :id => @event %> |
<%= link_to 'Back', :action => 'list' %>

event_contoller.rbではmembershipを更新。

 def update
   @event = Event.find(params[:id])
   users = User.find(:all)
   users.each do |user|
     membership = Membership.find(:first,
                                  :conditions => ["user_id = ? and event_id = ?",
                                                  user.id, @event.id])
     if ((params[:proposed][user.id.to_s] == "yes") ||
         (params[:joined][user.id.to_s] == "yes"))
       if (! membership)
           membership = Membership.new
           membership.event_id = @event.id
           membership.user_id = user.id
       end
       if (params[:joined][user.id.to_s] == "yes")
         membership.joined = true
       else
         membership.joined = false
       end
       membership.save
     else
       if (membership)
         membership.destroy
       end
     end
   end

eventが削除されたら関連するmembershipも削除する。

 def destroy
   @event = Event.find(params[:id])
   memberships = Membership.find(:all,
                                :conditions => ["event_id = ?",
                                                @event.id])
   memberships.each do |membership|
     membership.destroy
   end
   @event.destroy
   redirect_to :action => 'list'
 end

同様に、userが削除されたら関連するmembershipも削除するよう、 user_controller.rbも修正しておこう。

 def destroy
   @user = User.find(params[:id])
   memberships = Membership.find(:all,
                                :conditions => ["user_id = ?",
                                                @user.id])
   memberships.each do |membership|
     membership.destroy
   end
   @user.destroy
   redirect_to :action => 'list'
 end

_5 [Rails] 飲み会管理システム(5)

mm/edit.rhtmlに戻ろう。 models/user.rbで、membershipと参加フラグを返せるようにしておく。

class User < ActiveRecord::Base
 def memberships
   mhash = Hash::new
   m = Membership.find(:all,
                       :conditions => ["user_id = ?", self.id])
   m.each do |membership|
     mhash[membership.event_id] = membership
   end
   return mhash
 end
 def joins
   jhash = Hash::new
   memberships.each do |id, membership|
     jhash[membership.event_id] = membership.joined
   end
   return jhash
 end

mm/edit.rhtmlで、ログインしているのが管理者ならば管理者専用メニューを表示する。

<% if (@user.root) %>
<h2>管理者メニュー</h2>
<%= link_to 'ユーザ管理', :controller => 'user' %>
<%= link_to 'イベント管理', :controller => 'event' %>
<% end %>

一般ユーザも、ニックネーム、メールアドレス、参加フラグは更新出来るように、 text_field, check_boxで表示する。

<% form_tag :action => 'update', :id => @user do %>
<h2>アカウント情報</h2>
<table>
<tr><td>会員番号</td><td><%= @user.accountnumber %></td></tr>
<tr><td>ニックネーム</td><td><%= text_field("user", "nickname") %></td></tr>
<tr><td>メールアドレス</td><td><%= text_field("user", "mailaddress") %></td></tr>
</table>

<h2>参加状況</h2>
<table>
<tr><td>タイトル</td><td>参加</td></tr>
<% @memberships.each do |id, membership| %>
<tr>
	<td><%= Event.find(membership.event_id).title %></td>
	<td><%= check_box("join", membership.event_id, {:checked => @joins[membership.event_id]}, "yes", "no") %></td>
</tr>
<% end %>
</table>
<%= submit_tag '更新' %>
<% end %>
<%= link_to 'ログアウト', :action => 'login' %>

最後はend_form_tagだとなぜかエラーするので、endにする。 落ちつきが悪いのでログアウトへのリンクを付ける。

mm_controller.rbで更新を反映させる。もうちょっと簡潔に書く方法がありそうな感じだけど。

 def update
   @user = User.find(params[:id])
   @user.nickname = params[:user][:nickname]
   @user.mailaddress = params[:user][:mailaddress]
   @memberships = @user.memberships
   @memberships.each do |id, membership|
     if (params[:join][id.to_s] == "yes")
       membership.joined = true
     else
       membership.joined = false
     end
     membership.save
   end
   @user.save
   redirect_to(:action => "edit", :id => params[:id])
 end

_6 [Rails] 飲み会管理システム(6)

user/editでユーザ情報を表示すると、現在の状態に関らず常にrootでTrueが選択されている。 user/_form.rhtmlで

<p><label for="user_root">Root</label><br/>
<select id="user_root" name="user[root]"><option value="false">False</option><option value="true" selected>True</option></select></p>

となっているからのようだ。

 user_controller.rbで、現在の状態に従って、片方にselectedを代入する。
 def edit
   @user = User.find(params[:id])
   if (@user.root)
     @true_select = 'selected'
     @false_select = ''
   else
     @true_select = ''
     @false_select = 'selected'
   end
 end

変数に従って適当な方がselectedになるようにuser/_form.rhtmlを変更。

<option value="false" <%= @false_select %>>False</option><option value="true" <%= @true_select %>>True</option>

両方ともnilで呼ばれることもありそうだが、気にしない。

[]

トップ «前の日記(2007-05-12(Sat)) 最新 次の日記(2007-05-18(Fri))»