⚠️
Railsでバリデーションエラー時にカスタム例外を出したい!!
弊社の開発チームは現在週一出社なんですが、週に一回しかしっかり外出しないとはいえ、この溶けるような暑さに無事やられています。。
そんなことは置いておいて、今回はタイトル通りRails(ActiveRecord
)でvalidationでコケた際にカスタム例外をraiseしたくなるときはありませんか??
そんなあなたに必見のTipsを紹介します!!
やりたいこと
以下のようなvalidationを組んでいるとしましょう。
class Hoge < ActiveRecord::Base
validates :title, presence: true, uniqueness: { case_sensitive: false }
validate :validate_description
private
def validate_description
errors.add(:description, '何らかのエラー') if description.fuga?
end
end
そしてこのHogeクラスをcreateする際にvalidationごとになんらかの値を返したいcontrollerがいるとします。
自分がいるチームではinsertやupdateをする際は例外を投げる形を採用しているため、rescueをしてエラーの中身を見て条件分岐する必要があります。
controllerで、しかもrescueで条件分岐なんてできるだけしたくないですよね...
class HogeController < ActionController::Base
def fuga
@hoge = Hoge.create!(title: params[:title], description: params[:description])
rescue ActiveRecord::RecordInvalid => e
@error = if e.record.errors.details.key?(:title)
'Title is invalid'
elsif e.record.errors.details.key?(:description)
'Description is invalid'
else
''
end
end
end
解決方法
Railsガイド#厳密なバリデーションにもある通り、strict
を使えばraiseをするクラスを指定できます。
また、Railsガイドにはvalidates
での指定の仕方は記述されていますが、errors.add()
を使ったstrictの使い方は書かれていなかったので少し苦戦しました。
class Hoge < ActiveRecord::Base
+ class TitleInvalidError < StandardError; end
+ class DescriptionInvalidError < StandardError; end
+ validates :title, presence: true, uniqueness: { case_sensitive: false }, strict: TitleInvalidError
- validates :title, presence: true, uniqueness: { case_sensitive: false }
validate :validate_description
private
def validate_description
+ errors.add(:description, strict: DescriptionInvalidError) if description.fuga?
- errors.add(:description, '何らかのエラー') if description.fuga?
end
end
class HogeController < ActionController::Base
def fuga
@hoge = Hoge.create!(title: params[:title], description: params[:description])
+ rescue Hoge::TitleInvalidError
+ @error = 'Title is invalid'
+ rescue Hoge::DescriptionInvalidError
+ @error = 'Description is invalid'
- rescue ActiveRecord::RecordInvalid => e
- @error = if e.record.errors.details.key?(:title)
- 'Title is invalid'
- elsif e.record.errors.details.key?(:description)
- 'Description is invalid'
- else
- ''
- end
end
end
きれいになりましたね!!
暑い夏もstrict
を使って涼しげにrecueしていきましょう!!
Discussion