rails save! create! update!のバリデーション例外を捕捉する


Goal

save! / create! / update!メソッドでバリデーションエラー・コールバックエラーを例外ハンドリングできるようにして、なおかつエラー内容を知りたい.
以下のコードはpersonが作られる前に例外発生するため機能しない.

##
## 機能しません!!!
##
def create
  @person = Person.create!({ name: "Isaac Newton", age: 35 })
rescue ActiveRecord::RecordInvalid
  pp @person.errors # personに値が代入される前にcreate!は例外を投げるためpersonは空になってしまう
end

Code

validationエラー時に投げられるActiveRecord::RecordInvalidオブジェクトがバリデーションエラー情報を持っているのでこれを使えばいい.
http://api.rubyonrails.org/classes/ActiveRecord/RecordInvalid.html#method-c-new

def create
  @person = Person.create!({ name: "Isaac Newton", age: 35 })
rescue ActiveRecord::RecordInvalid => e
  pp e.record.errors
end

save! / create! / update!はモデル層のエラーを例外として返し、save / create / updateは返り値falseで返す.
モデル層のエラーとはvalidationとcallback. その他のSQLエラーやDB通信エラーなどインフラ層と考えられるエラーは!ありでもなしでも例外として投げられる.
http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-save-21

save,create,update save!,create!,update!
ActiveRecord::RecordInvalid(validation系) return false 例外
ActiveRecord::RecordNotSaved(callback系) return false 例外
ActiveRecord::RecordNotUnique,ActiveRecord::StatementInvalidなど 例外 例外

Environment

% bundle exec rails -v
Rails 4.1.9