読者です 読者をやめる 読者になる 読者になる

Rails5からActiveModel::Errorsにdetailsがはえてた

※ Rails5でのActiveModelの話

class User < ActiveRecord::Base
  validates :name, presence: true,
                   uniqueness: true,
                   length: { minimum: 2, maximum: 20 }
end

こんな感じのモデルがあったとして

> user = User.new
> user.tap(&:validate).errors.messages
# => {:name=>["can't be blank", "is too short (minimum is 2 characters)"]}

入力に不備があると ActiveModel::Errors インスタンスでエラー内容が参照できる
今回は特定のバリデーション(例えば今回の例だとlengthのminimumにかかった時など)にかかった時の分岐を考える

愚直にかけばこうなる

if user.tap(&:validate).errors[:name].include?('is too short (minimum is 2 characters)')
   # something
end

I18nとかで :en があったり :ja など複数扱う場合だとこれでは :ja のときにかからない
しかしこれはRailsの4系までの話

Rails5のActiveModelからはdetailsというメソッドがはえておりどのバリデーションにかかったのかのtypeを返してくれる
⇓の中盤より少し下にAdd ActiveModel::Errors#detailsとして書いてある

rails/CHANGELOG.md at v5.0.0 · rails/rails · GitHub

このdetailsを使うと下記のようなvalidatorのtypeがかえってくる

> user.tap(&:validate).errors.details
# => {:email=>[{:error=>:blank}], :password=>[{:error=>:blank}], :name=>[{:error=>:blank}, {:error=>:too_short, :count=>2}]}

typeなのでi18nとかに左右されて変わらないので、先程ほどの特定のバリデーションにかかった時の分岐は

if user.tap(&:validate).errors.details[:name].any? { |e| e[:error] == :too_short }
   # something
end

のように書ける

余談

このdetailsをRails4.xとかでも使えるようにするgemは存在するようです

github.com