たたかう
データの形式や数値について、制約を設けることをバリデーションという。Railsにはあるていど整ったバリデーションがあり、上手に使うことで簡単にいろいろできる。
以下は私のアプリのReport
モデルの記述だが、このようにcontentの文字数の最大値を設定したり、空白の場合はエラーとなるようにしている。
# 略
validates :content, length: { maximum: 200 }, presence: true
ただ、細かいところを詰めようとすると、どうしても既存の表現ではうまくいかないところが出てくる。そうなったらつくるしかない。
カスタムメソッドの登場だ。
カスタムメソッド
1. 過去の日付はNG
例えば、「目標の締め切りは現在より過去の日付はNG」としたいなら、以下のように入力する。
class Plan < ApplicationRecord
# 略
validates :deadline, presence: true
validate :deadline_cannot_set_in_past
# 略
def deadline_cannot_set_in_past
if status == "進行中" && (deadline.present? && deadline < Date.today)
errors.add(:deadline, ":ステータスが「進行中」の場合、過去の日付は使えません")
end
end
end
validateにメソッド名を書いておき、紐づけるイメージだろうか。わりあいわかりやすい。
Railsガイドにも同様の記述がある。
2. 条件を満たすデータの総数の制約
次に、「進行中の目標の個数は3個まで」を設定した。これはアプリを使っていく上で、振り返りながら目標の状態を変更する流れがある。ただ、同時に進行中にできる目標は制限がないといけないので、制約を設けることにした。
(振り返りの記述の際に、すこし面倒なのだ)
それは以下のようになる。
class Plan < ApplicationRecord
# 略
validate :progress_total_count_cannot_set_exceeded_limit
# 略
PROGRESS_MAX_COUNT = 3
def progress_total_count_cannot_set_exceeded_limit
count_in_progress = user.plans.where(status: "進行中").count
if count_in_progress > PROGRESS_MAX_COUNT
errors.add(:status, ":「進行中」の目標の数は、3個までです")
end
end
end
ログイン中のユーザについて、進行中となっている目標の個数を出してくる。それだけ。オーバーならエラーが出るようにした。
3. 条件を満たすデータの変更禁止
最後に、「完了となった目標はもういじっちゃだめ」という制約をつくった。これはすでに完了した目標は締め切りが過ぎているため、
「名前だけでも変更しよう...」
とすると、
「締め切りが現在より以前ですだめ!」
と怒られるためである。
やっていく。
class Plan < ApplicationRecord
#略
validate :cannot_edit_on_complete, on: :update
#略
def cannot_edit_on_complete
if status == "完了" && (deadline_changed? || name_changed?)
errors.add(:deadline, ":完了状態で締め切りは変更できません")
end
end
end
on: :update
をつけることで、更新の時のみバリデーションが効くようにしている。また、_changed?
をつけることで、属性が変更したかどうかを確認している。
テストもがんばっていくけど。。。
バリデーションをつくったらテストも書かないといけないのだが、テストってぜんぜんわからん。これは来年やっていこう。