記事を更新する: 小林研 Rails Tips (60)

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

https://github.com/hkob/hkob_blog

はじめに

Rails Tips の 60 回目です。昨日は、create による記事の追加を行いました。次に記事を更新するに書かれている edit と update を実装しましょう。

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

7.4 記事を更新する

こちらも先にテストを記述しましょう。update は create と同様に成功する場合と失敗する場合の二通りのテストが必要です。まだ shared_example に登録されていないものはこちらで紹介してあります。

describe "GET #edit" do
    subject { -> { get edit_article_path(object) } }
    it_behaves_like "レスポンスコード確認", 200
    it_behaves_like "描画結果に文字列が含まれている?", "記事の編集"
  end

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

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

以前、showparams[:id] から @article を取得していました。この処理は今回の edit, update 、そしてこの後に説明する destroy でも共通で実施します。そこで、 before_action で共通処理で記載します。当初、destroy も入れていたのですが、Rails 7.1 からは記述していないメソッドを before_actiononly に入れるとエラーが発生するようになっていました。RSPec だとこのエラーが見えなくてブラウザから確認して原因を理解しました。

before_action :take_one, only: %i[show edit update]

take_one は private メソッドとして実装します。

  private
  (中略)

  def take_one
    @article = object_from_params_id Article
  end

以前の show は中身がなくなります。

  def show
  end

以前の edit も同じです。

  def edit
  end

app/views/articles/edit.html.haml は new と内容が同じです。form は共通ですし、タイトルは翻訳テキストから取得されるためです。そもそもファイルを作ることもなく、シンボリックリンクにしてしまっていもいいかもしれません。

- content_for :title do
  - @page_title = t ".title"

= render "form"

ja/locales/views/ja.yml に以下のテキストを追加します。

    edit:
      title: 記事更新
      link_title: 編集

残りは update の実装です。基本的に create とほとんど変わりません。save と update の違いくらいです。

  def update
    if @article.update(article_params)
      redirect_to @article, notice: notice_message(Article)
    else
      flash.now[:alert] = alert_message Article
      render :edit, status: :unprocessable_entity
    end
  end

これで edit, update のテストおよび実装が完了しました。RSpec の結果は以下のようになっています。無事に全てのテストが通過しています。

  GET #edit
    behaves like レスポンスコード確認
      レスポンスのステータスが 200 であること
    behaves like 描画結果に文字列が含まれている?
      レスポンスの文字列が「記事更新」を含むこと
  PATCH #update
    正しいパラメータに対して
      behaves like レスポンスコード確認
        レスポンスのステータスが 302 であること
      behaves like オブジェクト属性が変化した?
        is expected to change `klass.find(object.id).send(key)` from "This is the second article that can be deleted" to "ABC"
      behaves like リダイレクト確認
        is expected to redirect to "/articles/158250001"
      behaves like Notice メッセージ確認
        notice に「記事を更新しました。」が出力されること
    不正なパラメータに対して
      behaves like レスポンスコード確認
        レスポンスのステータスが 422 であること
      behaves like オブジェクト属性が変化しない?
        Article の title が変化しないこと
      behaves like Alert メッセージ確認
        alert に「記事の更新に失敗しました。」が出力されること

Finished in 0.2431 seconds (files took 1.72 seconds to load)
22 examples, 0 failures

おわりに

edit, update が完了したので、記事が更新できるようになりました。明日は最後の destroy まで実装します。