status 属性を追加する: 小林研 Rails Tips (71)

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

https://github.com/hkob/hkob_blog

はじめに

Rails Tips の 71 回目です。Rails ガイドに戻り、「concern を使う」をやってみます。個人的はあまり concern を使ったことがないので、今回試してみてうまく使えるようであれば自分のプロジェクトでも対応してみたいと思います。今日は status 属性を追加するところまでになる予定です。

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

9.3 concern を使う

concern を使うという内容ですが、最初は concern を使わない実装が行われます。まずは、status 属性を article と comment の両方に追加するための migration ファイルを作成します。ガイドでは CamelCase のもので説明していますが、私は underscore の方が好みなのでそれで行います。

$ bin/rails g migration add_status_to_article status:string
      invoke  active_record
      create    db/migrate/20240208115326_add_status_to_article.rb
$ bin/rails g migration add_status_to_comment status:string
      invoke  active_record
      create    db/migrate/20240208115356_add_status_to_comment.rb

それぞれのファイルは以下のようになりました。null: false を追加しています。

class AddStatusToArticle < ActiveRecord::Migration[7.1]
  def change
    add_column :articles, :status, :string, null: false
  end
end
class AddStatusToComment < ActiveRecord::Migration[7.1]
  def change
    add_column :comments, :status, :string, null: false
  end
end

migration しておきます。

$ bin/rails db:migrate
$ bin/rails db:migrate RAILS_ENV=test

fixtures の articles.yml や comments.yml に status を追加しておきます。

article1:
  title: "This is the first article"
  body: "This is the body of the first article"
  status: "public"

can_delete:
  title: "This is the second article that can be deleted"
  body: "This is the body of the second article"
  status: "private"
comment1:
  commenter: "user 1"
  body: "comment 1 for article 1"
  article: article1
  status: "public"

can_delete:
  commenter: "user 2"
  body: "comment 2 for article can_delete"
  article: can_delete
  status: "archived"

status には public, private, archived しか入れることができないのですが、このテストは明日に回します。今はとりあえず、article_spec.rb や comment_spec.rb に存在制約だけを付けてみます。

    it_behaves_like "存在制約", %i[title body status]
    it_behaves_like "存在制約", %i[commenter body article_id status]

このテストを追加させるためだけに、以下の記述をしてこのテストだけを通過させます。明日これを書き換えます。

validates :status, presence: true

おわりに

ここまで書いて値を限定する shared example (値限定制約)が作りたくなったので、今日はここで切ります。