コメント編集のテストと実装を記述する: 小林研 Rails Tips (69)

このページの内容は以下のリポジトリに1日遅れで反映されます(記事執筆前に前日分をコミットしています)。

https://github.com/hkob/hkob_blog

はじめに

Rails Tips の 69 回目です。ガイドにはないのですが、コメント編集のテストと実装を追加します。

Rails をはじめよう - Railsガイド

コメントを編集する

現在、comment を表示している部分テンプレートを以下のように変更します。

  • 編集リンクを追加
  • 選択されたコメントは form を描画
- if comment == @comment
  = render "articles/comments/form"
- else
  - %i[commenter body].each do |attr|
    %p= t_ars Comment, attr
    %p= comment.send attr
  %p= lotfp article_path(@article, comment_id: comment.id), title: "コメントを編集"

articles#show では新規コメントのために、 @comment を設定していました。今回、comment_id が指定されたときには指定されたコメントを取得し、なかった場合のみ新規コメントを作成するようにします。

  def show
    @comments = @article.comments.order_created_at_desc
    @comment = objects_from_params(Comment) || @article.comments.build
  end

このままだと更新用の form と新規用の form の両方が表示されてしまうので、new_record でなかった場合には、Add a comment より下を表示しないようにします。

- if @comment.new_record?
  %h2 Add a comment:
  = render "articles/comments/form"

あとは実際の処理を記述するだけです。まずはテストを記述します。

  describe "PATCH #update" do
    subject { -> { patch article_comment_path(article, object), params: {comment: attrs} } }
    context "正しいパラメータに対して" do
      before { attrs["commenter"] = "ABC" }
      it_behaves_like "レスポンスコード確認", 302
      it_behaves_like "オブジェクト属性が変化した?", Comment, :commenter, "ABC"
      it_behaves_like "リダイレクト確認"
      it_behaves_like "Notice メッセージ確認", "コメントを更新しました。"
    end

    context "不正なパラメータに対して" do
      before { attrs["commenter"] = "" }
      it_behaves_like "レスポンスコード確認", 422
      it_behaves_like "オブジェクト属性が変化しない?", Comment, :commenter
      it_behaves_like "Alert メッセージ確認", "コメントの更新に失敗しました。"
    end
  end

実装は以下のようになりました。昨日の create もそのまま載せておきます。どのコントローラもほぼ同じ記述になりますね。ただし、update に失敗した時、@comments が存在しないと自分自身が表示されなくなります。失敗した場合にはコントローラであらかじめ @comments を取得しておく必要があります。

class Articles::CommentsController < ApplicationController
  before_action :take_base
  before_action :take_one, only: %i[update]

  def create
    @comment = @article.comments.build(comment_params)
    if @comment.save
      redirect_to @article, notice: notice_message(Comment)
    else
      flash.now[:alert] = alert_message Comment
      render "articles/show", status: :unprocessable_entity
    end
  end

  def update
    if @comment.update(comment_params)
      redirect_to @article, notice: notice_message(Comment)
    else
      @comments = @article.comments.order_created_at_desc
      flash.now[:alert] = alert_message Comment
      render "articles/show", status: :unprocessable_entity
    end
  end

  private

  def take_base
    @article = objects_from_params Article
  end

  def take_one
    @comment = object_from_params_id Comment
  end

  def comment_params
    params.require(:comment).permit(:commenter, :body, :article_id)
  end
end

おわりに

コメントの編集まで完了しました。明日はコメントの削除も実施します。