0
0

More than 3 years have passed since last update.

Rails Tutorial 拡張機能のメッセージ機能を作ってみた(その2):表示する画面を作成

Last updated at Posted at 2020-11-15

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を参考にします。

app/views/dms/show.html.erb
<% 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コントローラ」を読みます。

config/routes.rb
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: ユーザー一覧ページへのリンクを更新する 」にリンクを追加しているところがありました。同様に追加します。

views/layouts/_header.html.erb
                <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>

画面を表示してリンクがメニューに追加されたことを確かめます。

dm1.png

ログインしていなかったらRedirectするテストは後で作ることにします。

コントローラーはindexなのに、viewはshowなことに気が付きました。ファイルをリネームします。
show.html.erb -> index.html.erb

app/controllers/dms_controller.rb
class DmsController < ApplicationController
  def index
  end
end

画面を試しに表示

rails serverで画面で表示してみます。
エラーになりました。メッセージは
undefined method `name' for nil:NilClass
で、エラーが起きた場所は
<% provide(:title, @user.name)%>
です。@userがnilなのだと考えます。
@userにどこでログインしたユーザーを設定するのか、userのshow画面を参考に見て同様に変更します。

app/controllers/dms_controller.rb
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で一覧表示しています。参考にして変更します。

app/controllers/dms_controller.rb
  def index
      @user = current_user
      @dms = @user.sent_dms
  end

Micropostではfeedとfeed_itemsをうまく使っている13章を参考にするとよさそうです。読み返すなかで

render @user

が何を意味しているのかがあやふやだったので、さかのぼって読み返します。
コントローラーに

app/controllers/dms_controller.rb
  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>

に何を書けばいいのか考えます。

app/views/dms/index.html.erb
<% 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 %>  
app/views/dms/_dm.html.erb
<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を使ってみます。

db/seeds.rb
# 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

画面を表示してみます。

dm3.png

receiverが出ていないことに気が付きましたので、senderから変更します。

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>

dm4.png

DM表示のテスト作成

DMを表示する画面のテストを作ります。
tutorialの「13.2.3 プロフィール画面のマイクロポストをテストする」を参考にします。

test/fixtures/dms.yml
...
<% 30.times do |n| %>
dm_<%= n %>
  content: <%= Faker::Hipster.sentence %>
  created_at* <%= 42.days.ago %>
  sender: michael
  receiver: archer
<% end %>
test/integration/dms_test.rb
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/controllers/dms_controller_test.rb
  test "should redirect index when not logged in" do
    get dms_path
    assert_redirected_to login_url
  end  

REDです。コントローラーにindexアクションに対するアクセス制限を追加します。

app/controllers/dms_controller.rb
class DmsController < ApplicationController
  before_action :logged_in_user, only: [:index]

テストがGREENになりました。

所要時間

11/7から11/14までの7.0時間です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0