[Ruby on Rails 4] Validator クラスを利用した Rails の Custom Validation

Validator クラスを利用して、カスタムバリデーターを適切にモデルから分離したい。

想定する利用方法

モデル内の validates に full_width_length: { maximum: 15 } のような指定をします。

app/models/item.rb
class Item < ActiveRecord::Base
  validates :title, full_width_length: { maximum: 15 }

非アスキー文字を2文字としてカウントして、length をバリデーションします。

Validator クラスの置き場所

いろいろ眺めた感じでは、app/validators を作成し、config/application.rb にパスを加える方法が定石のようです。

ここでは app/models/concerns に設置します。

Custom validator

このあたりを参考にします。

Ruby on Rails 4 アプリケーションプログラミング
山田 祥寛
技術評論社
売り上げランキング: 19,129

「5.5.5 自作検証クラスの定義」

app/models/concerns/full_width_length_validator.rb
class FullWidthLengthValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    count = count_width(value)
    maximum = options[:maximum]
    message = I18n.t('errors.messages.too_long', count: maximum)
    record.errors[attribute] << (options[:message] || message) if count > maximum
  end
  private
  def count_width(str)
    str.length + str.chars.reject(&:ascii_only?).length
  end
end

I18n による訳文の参照と追加

I18n の訳文メッセージに変数を渡す方法が分からず手間取りましたが、前述のコードの通りそのままでした。

以下のように訳文ファイルを用意します。

config/locales/en.yml
en:
  errors:
    messages:
      too_long: " is too long (maximum is %{count} characters)"
config/locales/ja.yml
ja:
  errors:
    messages:
      too_long: " は%{count}文字以内で入力してください"

RSpec

後で書く。

補遺