Rails Tutorialの第14章にある、メッセージ機能を作る件の続きです。
前回まででモデルができました。表示する画面を作ります。
###DMを表示するViewの仕様を設計
DMを表示する方法を作ります。
tutorialの13.2 「マイクロポストを表示する」を読みます。
MicropostのようにUserの画面に合わせて表示するのではなく、独立したページで表示することにします。Twitterと同様です。
モックアップを作ります。送信者が複数いるので、送信者が表示されているモックアップとして、図 14.5を参考にします。
DM(3)
画像1 Thomas Hobbes Lorem ipsum
sent 1 day ago.
画像2 Sasha Smith Also poor,nasty,
sent 2 days ago.
画像3 John Calvin Excepteur sint
sent 3 days ago.
Previous 1 2 3 next
図 DMページのモックアップ
###DMを表示するViewを作成
コントローラとビューを作成するために、コントローラを生成します。
ubuntu:~/environment/sample_app (create-dm) $ rails generate controller Dms
ビューを作ります。リスト13.22と13.24を参考にします。
<% provide(:title, @user.name)%>
<div class="row">
<div class="col-md-8">
<% if @user.send_dms.any? %>
<h3>DMs (<%= @user.sent_dms.count %>)</h3>
<ol class="dms">
<li id="dm-<%= dm.id %>">
<%= 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>
</ol>
<% end %>
</div>
</div>
###DMを表示するコントローラーを作成
新しいDMのページを表示するためのコントローラーを作ります。
tutorialの「12.1.1 PasswordResetsコントローラ」を読みます。
Rails.application.routes.draw do
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users do
member do
get :following, :followers
end
end
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :microposts, only: [:create, :destroy]
resources :relationships, only: [:create, :destroy]
resources :dms, only: [:new, :create, :index, :destroy]
end
HTTPリクエスト | URL | Action | 名前付きルート |
---|---|---|---|
GET | /dms/new | new | new_dm_path |
POST | /dms | create | dms_path |
GET | /dms | index | dms_path |
DELETE | dms/ | destroy | dm_path |
RESTfulルーティング
tutorialの「10.3.1 ユーザーの一覧ページ」を読みます。
リスト「 10.40: ユーザー一覧ページへのリンクを更新する 」にリンクを追加しているところがありました。同様に追加します。
<ul class="dropdown-menu">
<li><%= link_to "Profile", current_user %></li>
<li><%= link_to "Settings", edit_user_path(current_user) %></li>
<li><%= link_to "DM", dms_path %></li>
画面を表示してリンクがメニューに追加されたことを確かめます。
ログインしていなかったらRedirectするテストは後で作ることにします。
コントローラーはindexなのに、viewはshowなことに気が付きました。ファイルをリネームします。
show.html.erb -> index.html.erb
class DmsController < ApplicationController
def index
end
end
###画面を試しに表示
rails serverで画面で表示してみます。
エラーになりました。メッセージは
undefined method `name' for nil:NilClass
で、エラーが起きた場所は
<% provide(:title, @user.name)%>
です。@userがnilなのだと考えます。
@userにどこでログインしたユーザーを設定するのか、userのshow画面を参考に見て同様に変更します。
class DmsController < ApplicationController
def index
@user = current_user
end
end
画面で表示してみます。またエラーになりました。
undefined local variable or method `dm' for #<#<Class:0x00005575f57bf2a8>:0x00005575f57deb58>
エラーが起きた場所は
<li id="dm-<%= dm.id %>">
です。
コントローラーで@dmsにデータを入れる必要があると考えます。
micropostをhome画面に表示するところを参考に見てみます。
コントローラーで
@micropost = current_user.microposts.build
@feed_items = current_user.feed.paginate(page: params[:page])
と@feed_itemsにデータを入れています。
ビューでは
<ol class="microposts">
<%= render @feed_items%>
</ol>
と@feed_itemsをrenderで一覧表示しています。参考にして変更します。
def index
@user = current_user
@dms = @user.sent_dms
end
Micropostではfeedとfeed_itemsをうまく使っている13章を参考にするとよさそうです。読み返すなかで
render @user
が何を意味しているのかがあやふやだったので、さかのぼって読み返します。
コントローラーに
def index
@user = current_user
@dms = @user.sent_dms.paginate(page: params[:page])
end
とすればよいと分かり、その場合viewの
<span class="user"><%= link_to dm.sender.name, dm.sender%></span>
に何を書けばいいのか考えます。
<% provide(:title, @user.name) %>
<h1>DM</h1>
<% if @user.sent_dms.any? %>
<h3>DMs (<%= @user.sent_dms.count %>)</h3>
<ol class= "microposts">
<%= render @dms %>
</ol>
<%= will_paginate @dms %>
<% end %>
<li id="dm-<%= dm.id %>">
<%= 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>
試しにrails serverで画面を表示してみます。データが少ないため1ページしかありません。
pagnateがされているか確認するために、データを増やします。
contentのテストデータ生成に、Faker::Hipster.sentenceを使ってみます。
# DM
users = User.order(:created_at).take(6)
receiver = users.second
50.times do
content = Faker::Hipster.sentence
users.each {|user| user.sent_dms.create!(content: content,
receiver_id: receiver.id) }
end
画面を表示してみます。
receiverが出ていないことに気が付きましたので、senderから変更します。
<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>
###DM表示のテスト作成
DMを表示する画面のテストを作ります。
tutorialの「13.2.3 プロフィール画面のマイクロポストをテストする」を参考にします。
...
<% 30.times do |n| %>
dm_<%= n %>:
content: <%= Faker::Hipster.sentence %>
created_at* <%= 42.days.ago %>
sender: michael
receiver: archer
<% end %>
class DmsTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
test "dm display" do
log_in_as(@user)
get dms_path
assert_template 'dms/index'
assert_select 'title', full_title(@user.name)
assert_match @user.sent_dms.count.to_s, response.body
assert_select 'div.pagination'
@user.sent_dms.paginate(page: 1).each do |dm|
assert_match CGI.escapeHTML(dm.content), response.body
end
end
リスト 13.28を参考に、「'」などの記号が特殊文字で出力されていたので、エスケープする方法をネットで調べて修正しました。
https://rakuda3desu.net/rakudas-rails-tutorial14-3/
###コントローラーのアクセス制御のテスト作成
controllerのテストを作ります。
tutorialの「13.3.1 マイクロポストのアクセス制御」を読みます。
test "should redirect index when not logged in" do
get dms_path
assert_redirected_to login_url
end
REDです。コントローラーにindexアクションに対するアクセス制限を追加します。
class DmsController < ApplicationController
before_action :logged_in_user, only: [:index]
テストがGREENになりました。
##所要時間
11/7から11/14までの7.0時間です。