Ruby
Rails
api

ActiveModel::Errorsのエラーメッセージをエラータイプにしたい

More than 3 years have passed since last update.

APIとか作ってると、パラメータエラーのときのレスポンスに困る。
エラーメッセージやら多言語対応などあんまり考えたくない。

そこでバリデーションはActiveModelValidatorにぜんぶお任せしたいし、サーバ側に極力View要素のロジックを持たせたくないので、エラーメッセージに関してもクライアント側で制御して欲しい。

各Validatorにはエラーメッセージが用意されているor再定義できるが、デフォルトのエラーメッセージを使ってクライアント側で制御するには扱いにくく、すべてを再定義するのもめんどうなのでエラータイプを利用したい。

何のことかいまいち伝えにくいので具体的に。

Userモデルにnameのバリデーションを定義して説明してみる。

user.rb
class User < ActiveRecord::Base
  validates :name,
    presence: true,
    uniqueness: { :case_sensitive => false },
    length: { in: 4..30 },
    format: { with: /\A[a-zA-Z][\w]{3,29}\z/ }
end

エラーメッセージは、以下のようなやつ。

  • can't be blank
  • has already been taken
  • is too short
  • is invalid

エラータイプは、以下のようなやつ。

  • blank
  • taken
  • too_short
  • invalid

デフォルト状態のエラーメッセージを出力してみる。

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

user.errorsからtypeを引き出すうまい方法がわからなかったので(※情報求む!)、ActiveModel::Errorsを読んで、generate_messageメソッドに細工する。

config/initializers/active_model/errors.rbを追加。

config/initializers/active_model/errors.rb
module ActiveModel
  class Errors
    # Change error message to error type
    def generate_message(attribute, type = :invalid, options = {})
      type
    end
  end
end

エラータイプを返すようになる

> user = User.new
> user.validate
> user.errors.messages
=> {:name=>[:blank, :too_short, :invalid]}

これでクライアント側は各キーに対応するメッセージを出しやすくなる。

それにしてもこれでは対応が無骨にすぎる。
validatesのオプションであるmessageを定義したりできないし、まぁタイプを返したいんだから使わないんだけれども。。
user.errors.typesとかuser.errors.types_with_messageなどで取り出せるようにしたいところ。