今回は、これまで作ってきたフォロー/リムーブの仕組みなどを実際に組み込んで、WebアプリケーションとしてTwを完成させます。
発言一覧画面の改良
今まで「/words」で呼び出す発言一覧画面では全ユーザの全発言を表示していましたが、ここもTwitter風にするために自分の発言とフォローしている人の発言だけを表示するようにします。以下のようにtemplates/word/list.mabを修正しましょう。
自分とフォローしているユーザの発言のみを表示
@me = Tw :: Models :: User [: name => request . env [ 'REMOTE_USER' ]]
@users = Tw :: Models :: User . all
layout : default , : title => 'Tw' do
table . tw do
tr do
td (: colspan => 2 ) do
form : action => '/words' , : method => 'post' do
textarea '' , : name => 'word.text' , : cols => 80 , : class => 'words' ; br
input : type =>: hidden , : name => 'word.user_id' , : value => @me . id
input : type =>: submit , : value => 'Update'
end
end
end
tr do
td do
table . contents do
@words . each do | word |
is_me = ( word . user . name == @me . name )
unless @me . following_users . map {| u | u . name }. include ?( word . user . name ) || is_me then
next
end
view : word , : summary , : word => word , : is_me => is_me
end
end
end
td do
table . friends do
tr do
th (: colspan => 2 ){ "users" }
end
@users . each do | user |
next if user . name == @me . name
view : word , : show_user , : user => user , : me => @me
end
end
end
end
end
end
自分を判別するために1行目で「@me = Tw::Models::User[:name=>request.env['REMOTE_USER']]」としています。BASIC認証がかかっているので、REMOTE_USER名を拾えば、ログイン名が分かります。全ユーザは「@users = Tw::Models::User.all」で取得します。「 @words.each do |word|」以下のブロックが発言の処理部分です。個々の発言のユーザ名と自分の名前を比較して、その発言が自分のものかどうかチェックします。
その発言は自分のものかどうか?
is_me = ( word . user . name == @me . name )
自分のものなら変数is_meが真になります。
その発言は自分のものもしくはフォローしている人のものか?
unless @me . following_users . map {| u | u . name }. include ?( word . user . name ) || is_me
フォローしているユーザの一覧は「@me.following_users」で分かるので、発言の名前と比較します。これで「自分の発言とフォローしている人の発言だけ表示する」ことができます。表示のビュー呼び出しにはis_me変数も一緒に渡しましょう。
発言を表示するビューの呼び出し
view : word , : summary , : word => word , : is_me => is_me
発言表示のビューを改良する
templates/word/summary.mabを以下のようにします。ここで行う主なことは、自分の発言とそれ以外では異なるcssのクラスを指定することです。
発言表示用のビュー
tr (: class => @is_me ? 'mywords' : 'words' ) do
td . name (: style => 'width:10%;' ) do
strong do
@word . user . name
end
end
td . word do
span . word { @word . text }
span . date { @word . created_on . strftime ( "%Y-%m-%d %H:%M" ) if @word . created_on }
end
end
呼ばれたビューの中のインスタンス変数は、呼び出す側で指定したものであることに注意してください。ここでは「view :word, :summary, :word=>word, :is_me=>is_me」の後半の「:word=>word, :is_me=>is_me」がそれにあたります。呼び出す側の変数wordとis_meが、呼ばれる側では@word、@is_meと参照できるようになります。
フォローUIを作る
次に任意のユーザをフォローしたり、フォローからはずしたりするUIを作ります。
前述の発言一覧画面に、ユーザのフォロー/リムーブ(フォローをやめる)ができるUIを追加します。
ビュー
templates/word/list.mabのリストの後半に以下のような部分があります。
これがフォロー用のUIです。
フォロー用のUIの呼び出し
table . friends do
tr do
th (: colspan => 2 ){ "users" }
end
@users . each do | user |
next if user . name == @me . name
view : word , : show_user , : user => user , : me => @me
end
end
実際のビューはtemplates/word/show_user.mabが呼ばれています。ファイルを作成して、以下のようにします。
フォロー用のUI
tr do
td @user . name
td {
if @me . following_users [: name => @user . name ] then
a 'remove' , : href => "/user/#{@user.name}/remove"
else
a 'follow' , : href => "/user/#{@user.name}/follow"
end
}
end
ここでは全ユーザをリストアップして、すでにフォロー済みのユーザには"remove"、まだフォローしていないユーザには"follow"のアクションへのリンクを生成しています。ここのアクション呼び出しのURLはWaves風ではなく、Rails風になっていますね。というわけで、このURLをWavesが解釈できるようにする必要があります。
mapping.rbの修正
デフォルトで使えるREST型のルールを使ってもいいのですが、ここでは自前のルールを定義してみます。追加するアクションはfollowとremoveの2つで、ちょっとRails風に「/words/(ユーザ名)/follow」「 /words/(ユーザ名)/remove」と呼べるようにしてみます。
まずconfigrations/mapping.rbを修正して、上記のURLを受け取るようにします。
Rails風のURLを受け付けるようにしたmapping.rb
module Tw
module Configurations
module Mapping
extend Waves :: Mapping
path % r {^ /user/ (\ w +)/( follow | remove )/? $ }, : method =>: get do | name , action |
use (: user )
controller { self . send ( action , name )}
redirect ( '/words' )
end
path % r {^ /words/ ? $ }, : method =>: post do
use (: word )
controller { create }
redirect ( '/words' )
end
include Waves :: Mapping :: PrettyUrls :: RestRules
include Waves :: Mapping :: PrettyUrls :: GetRules
end
end
end
followとremoveの場合処理はほとんど同じなので、1つのpathのブロックに処理をまとめました。、まずトリガーとなるURLパターンを定義します。正規表現でURLに対してマッチングを行うことができるのですが、このとき後方参照用の部分が、そのままブロックの引数に渡されます。例えば「%r{^/user/(\w+)/follow/?$}」と指定すると、/user/(任意の文字列)/follow というパターンにマッチして、その文字列がブロックの引数 name で参照できます。ブロック内では「use(:user)」で、Userモデルを指定します。「 controller{self.send(action,name)}」で、Userコントローラのアクション(followかremove)がnameをパラメータとして起動します。最後に一覧表示画面である「/words」にリダイレクトしています。
アクションの追加
最後に実際のアクションを書きます。
controller/user.rbを以下のようにします。Wavesによって自動生成したものに手を入れたものです。
ユーザのフォロー・リムーブのアクション
module Tw
module Controllers
class User
include Waves :: Controllers :: Mixin
def attributes ; params [ model_name . singular . intern ]; end
def all ; model . all ; end
def find ( name ); model [ : name => name ] or not_found ; end
def create ; model . create ( attributes ); end
def update ( name )
instance = find ( name )
instance . set ( attributes )
instance . save_changes
end
def follow ( name )
@me = Tw :: Models :: User [: name => request . env [ 'REMOTE_USER' ]]
user = find ( name )
@me . add_following_user ( user ) unless @me . following_users [: name => user . name ]
end
def remove ( name )
@me = Tw :: Models :: User [: name => request . env [ 'REMOTE_USER' ]]
user = find ( name )
@me . remove_following_user ( user ) if @me . following_users [: name => user . name ]
end
end
end
end
フォローに追加するメソッドはadd_following_user、リムーブするアクションはremove_following_userです。重複して追加や、存在しないユーザを削除しないように、直前にチェックをしています。これはモデル側でチェックすることも可能です。
cssの追加
第4回 で追加したbase.cssに適当な記述をすれば、見栄えもよくできるでしょう。自分の発言と他人の発言は異なるcssクラスなので色分けも可能です。簡単にcssを設定したサンプルのスクリーンショットは、以下のようになります。
cssを設定した画面
まとめと次回の予定
Twitter風簡単アプリTWはこれでほぼ完成です。次回はWavesの今後のロードマップなどを解説したいと思います。