Rails Tutorialの第14章にある、メッセージ機能を作る件の続きです。
前回までメッセージを作成する機能ができました。
今回は受信したDMが表示される機能画面を作ります。
送信したDMに加え受信したDMを表示
DMの画面に送信したDMだけでなく、受信したDMも表示される機能を追加します。
「14.3ステータスフィード」を読みます。
14.3と同様に、テストから書きます。
1.自分が受信者のDMがchatに含まれていること
2.自分が送信者のDMがchatに含まれていること
3.自分が受信者でないDMは含まれないこと
ここで、3は1の裏返しです。テストとしては両方やる必要があります。
Userモデルにchatメソッドがあるので、テストはUserモデルに書くことになります。
test "chat should have the right dms" do
michael = users(:michael)
archer = users(:archer)
lana = users(:lana)
# 自分が送信者のDMを確認
michael.sent_dms.each do |dm|
assert michael.chat.include?(dm)
end
# 受信者のDMを確認
test_dm1 = michael.sent_dms.create!(content: "send message test1",
receiver_id: archer.id)
assert michael.chat.include?(test_dm1)
assert archer.chat.include?(test_dm1)
assert_not lana.chat.include?(test_dm1)
テストはREDです。chatメソッドを変更します。
def chat
#Dm.where("sender_id = ?", id)
Dm.where("sender_id = :user_id OR receiver_id = :user_id", user_id: id)
end
テストを再度実行します。
ubuntu:~/environment/sample_app (create-dm) $ rails test test/models/user_test.rb
GREENになりました。
すべてのテストを実行
全てのテストを実行してみます。エラーになりました。
FAIL["test_dm_display", DmsTest, 2.135752142000001]
test_dm_display#DmsTest (2.14s)
Expected /Deep\ v\ mustache\ godard\ austin\./ to match "<!DOCTYPE html>\n<html>\n <head>\n <!-- <title> | Ruby on R
...
test/integration/dms_test.rb:19:in `block (2 levels) in <class:DmsTest>'
テストを見直します。sent_dmsをchatで置き換える必要があるところがいくつか見つかりました。
変更前:assert_match @user.sent_dms.count.to_s, response.body
変更後:assert_match @user.chat.count.to_s, response.body
変更前:@user.sent_dms.paginate(page: 1).each do |dm|
変更後:@user.chat.paginate(page: 1).each do |dm|
全てのテストがGREENになりました。
誰からのDMか分からない
受信したDMは、名前にreceiverが表示されている、つまり自分が表示されているので、誰が送信したDMなのかが分からない、と気が付きました。
送信したDMと受信したDMを混ぜて表示すると設計したときには、気が付かなかったです。
対策として、表示する名前は
・送信したDMは、受信者
・受信したDMは、送信者
にするように変更します。
それと同様に受信したDMは、レイアウトを右寄せにします。
TwitterのDM画面を調べて気が付きました。
いくつかソースを眺めて、app/views/dms/_dm.html.erbを変更すればよいと分かりました。
<li id="dm-<%= dm.id %>">
<%= link_to gravatar_for(dm.receiver, size: 50), dm.receiver %>
<span class="user"><%= link_to dm.receiver.name, dm.receiver%></span>
<span class="content"><%= dm.content %></span>
<span class="timestamp">
Sent <%= time_ago_in_words(dm.created_at) %> ago.
</span>
</li>
送信者を表示するテスト
機能を作る前にテストから考えます。
# 受信者の画面では送信者が表示
delete logout_path
log_in_as(@receiver)
get dms_path
assert_select "span.user", @user.name
REDになるはずがGREENです。fixtureを調べます。既にテストデータが入っていたので、別のユーザーにすることにします。
受信者をarcherからmaloryに変えます。
malory:
name: Malory Archer
def setup
@user = users(:michael)
@receiver = users(:malory)
end
テストは想定通りの、REDになりました。
<Michael Example> expected but was
<Malory Archer>..
Expected 0 to be >= 1.
テストができるようになったので、viewを変更していきます。
チュートリアルでuserを確認していたif文を参考にします。
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
を参考にして、変更します。
<% if dm.sender = user %>
<%= link_to gravatar_for(dm.receiver, size: 50), dm.receiver %>
<span class="user"><%= link_to dm.receiver.name, dm.receiver %></span>
<% else %>
<%= link_to gravatar_for(dm.sender, size: 50), dm.sender %>
<span class="user"><%= link_to dm.sender.name, dm.sender %></span>
<% end %>
テストしたところ、エラーになりました。
test_send_dm_with_invalid_receiver#DmsTest (0.84s)
ActionView::Template::Error: ActionView::Template::Error: undefined local variable or method `user' for #<#<Class:0x000055c46520e520>:0x000055c465212e18>
Did you mean? @user
app/views/dms/_dm.html.erb:3:in `_app_views_dms__dm_html_erb__3083936748904538070_47150999444720'
app/views/dms/index.html.erb:7:in `_app_views_dms_index_html_erb___2703126462765815417_47150999303880'
test/integration/dms_test.rb:26:in `block in <class:DmsTest>'
@userを使えばいいのか、tutorialを読み直します。
コントローラーで定義した変数、例えば@userや@chat_itemsは、ビューで使えると理解しています。
ビューの中では、変数名dmをなぜ@なしで使えるのかがあやふやだったので読み直します。
リスト 10.51の説明を参考に考えると、DmクラスとRailsが判断し、_dm.html.erbという名前のパーシャルを探しに行きます。パーシャルの中ではそのクラスの変数名、この場合はクラス名に対応したdmという名前の変数に値がセットされています。
10章のまとめにも
render @usersを実行すると、自動的に_user.html.erbパーシャルを参照し、各ユーザーをコレクションとして表示する
と書いてありました。
リスト 10.57と
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
リスト 13.51:
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
を参考に修正します。
<% if current_user?(dm.sender) %>
<%= link_to gravatar_for(dm.receiver, size: 50), dm.receiver %>
<span class="user"><%= link_to dm.receiver.name, dm.receiver %></span>
<% else %>
<%= link_to gravatar_for(dm.sender, size: 50), dm.sender %>
<span class="user"><%= link_to dm.sender.name, dm.sender %></span>
<% end %>
テストがGreenになりました。
rails serverで表示してみます。受信したDMは送信者が表示されました。
受信と送信でレイアウトを変更
完成した画面を見て気が付いたのですが、それぞれのDMが送信したものか受信したものかの見分けが付きません。
Twitterでは画面のレイアウトを送信と受信で左寄せと右寄せに分けています。
同様に変更します。
リスト 13.26、さらにリスト 5.8: を読みます。
text-align: center;
.user {
margin-top: 5em;
padding-top:0;
}
.user {
margin-top: 5em;
padding-top:0;
text-align: right;
}
ネットで「CSS 右寄せ」で検索してみます。
https://qiita.com/Rock22/items/e4e89f15c29e1415977d
試しに変更して、表示してみます。
.microposts {
list-style: none;
padding: 0;
text-align: right;
.gravatar {
/*float: left; */
float: right;
現在はDMのCSSはmicropostsクラスを使っているので、新たにdmのクラスを2つ作ります。
/* dms sent and received*/
.dms_sent {
list-style: none;
padding: 0;
text-align: right;
.gravatar {
float: right;
}
}
/* dms sent and received*/
.dms_received {
list-style: none;
padding: 0;
.gravatar {
float: left;
}
}
OLタグからはclassの指定を削除します。
<ol>
<%= render @chat_items %>
<%#= render @dms %>
</ol>
受信と送信でクラスを分けます。
<% if current_user?(dm.sender) %>
<li id="dm-<%= dm.id %>" class="dms_sent">
<span class="gravatar"><%= link_to gravatar_for(dm.receiver, size: 50), dm.receiver %></span>
<span class="user"><%= link_to dm.receiver.name, dm.receiver %></span>
<span class="content"><%= dm.content %></span>
<span class="timestamp">
Sent <%= time_ago_in_words(dm.created_at) %> ago.
</span>
</li>
<% else %>
<li id="dm-<%= dm.id %>" class="dms_received">
<%= link_to gravatar_for(dm.sender, size: 50), dm.sender %>
<span class="user"><%= link_to dm.sender.name, dm.sender %></span>
<span class="content"><%= dm.content %></span>
<span class="timestamp">
Sent <%= time_ago_in_words(dm.created_at) %> ago.
</span>
</li>
<% end %>
試しに表示してみます。送信の右寄せ、受信の左寄せができました。
さらに送信と受信が分かるように、文言を加えました。from:とto: ReceivedとSentの対にしました。
<% if current_user?(dm.sender) %>
<span class="user">to:<%= link_to dm.receiver.name, dm.receiver %></span>
Sent <%= time_ago_in_words(dm.created_at) %> ago.
<% else %>
<span class="user">from:<%= link_to dm.sender.name, dm.sender %></span>
Received <%= time_ago_in_words(dm.created_at) %> ago.
<% end %>
所要時間
11/23から11/28までの5.5時間です。