概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Encapsulate each validation error as an Error object | Saeloun Blog
- 原文公開日: 2020/06/17
- 著者: Alkesh Ghorpade
- サイト: Saeloun
Rails: 個別のバリデーションエラーをErrorオブジェクトにカプセル化する(翻訳)
モデルのsave、create、updateアクションが失敗した場合のRailsのerrorsの表現方法が#32313で変更されました。
変更前
たとえばUserというモデルがあり、そこにfirst_name、last_name、contact_number、emailなどのカラムがあり、どれも必須だとしましょう。Userオブジェクトを作成するときにfirst_nameやcontact_numberをstringとして渡さないと、#errors関数が以下のようなエラーを出力します。
class User < ApplicationRecord
validates :contact_number,
presence: true,
numericality: true,
length: { :minimum => 10, :maximum => 15 }
end
user = User.create(email: "sam@example.com", last_name: "Example", contact_number: "abcdefghijk")
user.errors
=> #<ActiveModel::Errors:0x00007fe42c1650b8
@base=
#<User:0x00007fe42c1676d8
....
....
@details={:first_name=>[{:error=>:blank}], :contact_number=>[{:error=>:not_a_number}]}
@messages=
{:first_name=>["First Name is required."],
:contact_number=>["Contact number is not a number."]
}>
特定のフィールドに関するエラーメッセージは以下のように[]メソッドでアクセスできます。
user.errors[:first_name]
=> ["First Name is required."]
#messagesや#full_messagesですべてのエラーメッセージのリストを表示することもできます。
user.errors.messages
{
:first_name=>["First Name is required."],
:contact_number=>["Contact number is not a number."]
}
user.errors.full_messages
[
"First name is required.",
"Contact number is not a number."
]
エラーへのアクセスを上のように行うのはオブジェクト指向的ではありません。この方法では、特定のフィールドに関連付けられているエラーが複数ある場合に配列インデックスでアクセスしなければならなくなります。
user = User.create(email: "sam@example.com", last_name: "Example")
user.errors[:contact_number]
#=> ["Contact Name is required.", "Contact Name is not a number."]
user.errors[:contact_number][0]
#=> "Contact Name is required."
user.errors[:contact_number][1]
#=> "Contact Name is not a number."
変更後
最近のActiveModel#errorsの変更によって、上のエラーはハッシュではなくErrorクラスのオブジェクトとして出力されます。
user = User.create(email: "sam@example.com", last_name: "Example", contact_number: "abcdefghijk")
user.errors
#=> #<ActiveModel::Errors:0x00007ff5ba2be5a0 @base=#<User id: nil, first_name: nil, last_name: "Example", email: "sam@example.com", contact_number: "abcdefghijk", created_at: nil, updated_at: nil>,
@errors=[<#ActiveModel::Error attribute=first_name, type=blank, options={}>, <#ActiveModel::Error attribute=contact_number, type=not_a_number, options={:value=>"abcdefghijk"}>]>
where句を用いると、特定の属性に関連するエラーをフェッチできます。
user.errors.where(:contact_number)
#=> [<#ActiveModel::Error attribute=contact_number, type=not_a_number, options={:value=>"abcdefghijk}>]
where => (attribute, type, options)とシグネチャが似ているadd、added?、delete、match?といったメソッドも利用できます。
user.errors.add(:contact_number, :too_short, count: 10)
#=> <#ActiveModel::Error attribute=contact_number, type=too_short, options={:count=>10}>
user.errors.where(:contact_number)
#=> [<#ActiveModel::Error attribute=contact_number, type=not_a_number, options={:value=>nil}>, <#ActiveModel::Error
attribute=contact_number, type=too_short, options={:count=>10}>]
user.errors.added?(:contact_number, :too_short, count: 10)
#=> true
user.errors.added?(:contact_number, :too_short)
#=> false
user.errors.delete(:contact_number, :too_short, count: 10)
user.errors.where(:contact_number)
#=> [<#ActiveModel::Error attribute=contact_number, type=not_a_number, options={:value=>nil}>]
user.errors.match?(:contact_number, :not_a_number)
#=> true
user.errors.match?(:contact_number, :too_long)
#=> false
このように、added?メソッドで指定の属性に特定のエラーが発生しているかをチェックできます。
この変更によって、messageメソッドやfull_messageメソッドでwhere句を用いてアクセスできるようになりました。
user.errors.where(:first_name, :blank).last.message
#=> "can't be blank"
user.errors.where(:contact_number, :not_a_number).last.full_message
#=> "Contact number is not a number"
メモ
以下のメソッドは非推奨化されました。利用するとdeprecation warningが表示されます。
[]each{|attr, msgs|}generate_messagehas_key?key?keysvalues
user.errors.keys
DEPRECATION WARNING: ActiveModel::Errors#keys is deprecated and will be removed in Rails 6.2.
To achieve the same use:
errors.attribute_names
以下のメソッドは変更されていません。
as_json、blank?、clear、count、empty?addadded?full_messagesfull_messages_forinclude?、size、to_hash、to_xml、to_amessagesdetails