Rails習作 ログイン一覧作った

ちょっと高機能なチャットCGIになると必ずついている”現在ログインしているユーザ一覧を表示”を作ってみました。


まずはテーブル定義から

create table logon_users (
id                  int             not null    auto_increment,
user_id             int             null,
updated_at          datetime        not null,
constraint          fk_user_id      foreign key(user_id) references users(id),
primary key(id)
);


Rails(RoRと呼んでいましたが、今後はこちらで)のルールに従った構成になっています。ActiveRecordのテーブルリレーション機能を使うため、user_idはnull可にしてます。
テーブルの親子関係を1レコード:1レコードのリレーションに設定した場合、新しい子レコードを生成した際に、古い子レコードとのリレーションを切断する必要があります。この際にRailsは古い子レコードの外部キーカラムをnullに更新しようとします。このため、nullを許容する必要があります。
モデルを生成。

ruby script/generate model LogonUser


リレーションを設定。usersテーブル(親)、logon_usersテーブル(子)。関係は1:1。親テーブルのリレーション設定が”has_one”になっている点に注目。子の方は”belongs_to(所属している)”としか設定しないでよい。子を何人まで保持するかは親側が決めるという思想。上にあるとおり、二人目の子が設定された場合、自動的に一人目との関係は解除される。

class User < ActiveRecord::Base
has_one :logon_user
(略)


class LogonUser < ActiveRecord::Base
belongs_to :user
end


ここまででルール設定終了。あとは、実際にログオン情報を記録するだけ。どのタイミングで更新すればいいのか悩むところだけど

    ログイン時
    ログアウト時
    リスト更新要求時(ハートビート)

の3つに設定。最終発言時刻などを記録するなら、メッセージ送信時などにも必要ですな。
まずはログイン時に実装。ログイン処理はRails本のユーザ管理を参考に作ってます。そのうち、こちらも紹介。

  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
session[:name] = logged_in_user.name
        logon_user = LogonUser.find_by_user_id( logged_in_user.id ) ||
LogonUser.new( :user => logged_in_user  )
logged_in_user.logon_user = logon_user
redirect_to(:controller => "admin", :action => "index" )
else
flash[:notice] = "ユーザ/パスワードの組み合わせが無効です"
end
end
end


実際の処理は強調表示している部分。DBからfind_by_user_idで更新対象レコードを探す。見つからなかったら、newで新規作成。次の行で親子関係を設定。この時点で自動的にUpdateが実行される。
続いてログアウト処理。

    LogonUser.destroy_all( ["user_id = ?", session[:user_id]] )
session[:user_id] = nil
flash[:notice] = "ログアウトしました"
redirect_to(:action => "login")


「ログインしているユーザー一覧を保持するテーブル」なので、ログアウトしたら削除なのだ。destroy_allで条件を指定して実行。
んで、リスト更新要求時。これはログイン中に定期的に実行されるので、ハートビート(心音)確認するため。ブラウザを閉じるなどでログアウト処理を行わなかった場合、ハートビート最終時間を元に古いデータを削除する。

  def checklogonuser
    logon_user = LogonUser.find_by_user_id( session[:user_id] )
logon_user.save
@users = LogonUser.find( :all,
:order => "user_id" )
render(:layout => false)
end


find_by_user_idで対象レコードを特定。んで、いきなりsave。updated_atというRailsが特殊扱いするカラム名を使っているため。名前の通り、更新の際に最終更新日時を設定してくれる。古いデータの削除処理はまだ入れてないです。システムからのチャット発言(○○さんが入室しました)などと一緒にやろうかな、と。
最後の表示。無駄にAJAXを使っております。メイン画面のRHTMLファイルで。

<div id="userlist">
<%= render :partial => 'userlist' %>
</div>
<%= periodically_call_remote(:update => 'userlist',
:url => { :action => :checklogonuser },
:frequency => 30 ) %>


30秒おきに自動更新。これだけでうまいことやってくれるんだから、すごいもんだ。
続いて、checklogonuserのビュー。

<%= render :partial => 'userlist' %>


呼び出しルートが2種類あるため、実処理を別に分けている。2種類とは「初期表示(通常リクエスト)」と「AJAX表示」。
実体はこちら。

参加者:
<% for user in @users %>
<%=h user.user.name %> 
<% end %>


んで、コントローラー。

  def checklogonuser
logon_user = LogonUser.find_by_user_id( session[:user_id] )
logon_user.save
    @users = LogonUser.find( :all,
:order => "user_id" )
render(:layout => false)
end


findでデータを設定して、renderで共通レイアウト出力を無効化。(HTMLに填め込む形で利用するから)
と、こんなところでございます。開始が25時と遅かったんで、システムメッセージまで手が回りませんでした。
んで、こんな駄文、ネットに上げていいんかね?Web2.0的には完成度50%で上げるべきらしいが、完成度1%ぐらいなのですががが。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

コメント

  1. うみぽ より:

    えっと、よくわかりませんでした(・∀・)
    でも、がんがってるのは分かったので、
    てきとにガンガレ(‘A`)b

  2. Aoba より:

    うん、がんばるお
    ありがとう( ゚д゚)

  3. ARNIE より:

    久しぶりにPGらしいBlogで(・∀・)イイ!!
    完成度1%で見せたくなる気持ちは、すげー分かるなーw

  4. Hajime より:

    ちゃんと理解してからまとめて書きたいと思うのだけど、
    理解できるかもわからないし、
    納得できるクオリティを実現できるのは、さらに先になりそう
    つーことで、無知は無知なりにがんがん書いていく方向で