概要
業務でActiveModel::Validations
をmix-in
したカスタムモデルでのレコード作成処理を実装しました。
その際にvalid?
, invalid?
メソッドの挙動を履き違えていたことに気がついたため、自戒の念を込めて本記事の執筆に至りました。
該当処理
以下ユーザーの作成処理を担うカスタムモデル(サンプル)です。
class CreateUser
include ActiveModel::Validations
attr_reader :name, :password
def initialize(name, password)
@name = name
@password = password
end
def call
# 任意の処理
# ...
# 問題があった場合にerrorsに追加
errors.add(:base, '~が不正です')
return if invalid?
User.create!(
name:,
password:
)
end
end
当時の認識
当時の私は以下のような流れになると思っていました。
-
valid?
,invalid?
メソッドの実行 -
validation
メソッドが実行 -
errors
オブジェクトに値が格納されているか検証され、その結果が返る
実際の挙動
実際の挙動は以下になりました。
-
valid?
,invalid?
メソッドの実行 -
errors
オブジェクトがクリアされる -
validation
メソッドが実行 -
errors
オブジェクトに値が格納されているか検証され、その結果が返る
そのため、以下のerrors.add
で格納された情報は、後続のinvalid?
の実行によってclear
されます。
結果、無効なデータはないと判断され、ユーザー作成処理が実行されます。
# 問題があった場合にerrorsに追加
errors.add(:base, '~が不正です')
return if invalid?
Source
ActiveModel::Validations
のソースコードを確認しました。
errors.clear
されることがわかりますね.
def valid?(context = nil)
current_context = validation_context
context_for_validation.context = context
errors.clear
run_validations!
ensure
context_for_validation.context = current_context
end
def invalid?(context = nil)
!valid?(context)
end
ref.
まとめ
以上です。
本記事の内容について、認識齟齬などあればコメントいただきたいです🙏