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

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

[markdown]
## 想定する利用方法

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

“`ruby:app/models/item.rb
class Item < ActiveRecord::Base validates :title, full_width_length: { maximum: 15 } ``` 非アスキー文字を2文字としてカウントして、length をバリデーションします。 > * [全角文字を2文字として文字数をカウントする | deadwood](https://www.d-wood.com/blog/2017/05/11_8932.html)

## Validator クラスの置き場所

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

> * [Rails 4 でバリデータを自作する(custom validator) – Qiita](http://qiita.com/n-oshiro/items/4a3188be66dd0e18bae5)

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

> * [Performing custom validations in Rails — an example](https://hackernoon.com/performing-custom-validations-in-rails-an-example-9a373e807144)

## Custom validator

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

> * [Active Record バリデーション | Rails ガイド](https://railsguides.jp/active_record_validations.html#%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B)
> * [Rails 4でモデルのバリデーションまとめ – Rails Webook](http://ruby-rails.hatenadiary.com/entry/20140724/1406145303#model-validation-by-myself)

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

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

“`ruby: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 の[訳文メッセージに変数を渡す方法](https://railsguides.jp/i18n.html#%E8%A8%B3%E6%96%87%E3%81%AB%E5%A4%89%E6%95%B0%E3%82%92%E6%B8%A1%E3%81%99)が分からず手間取りましたが、前述のコードの通りそのままでした。

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

“`yaml:config/locales/en.yml
en:
errors:
messages:
too_long: ” is too long (maximum is %{count} characters)”
“`

“`yaml:config/locales/ja.yml
ja:
errors:
messages:
too_long: ” は%{count}文字以内で入力してください”
“`

## RSpec

後で書く。

## 補遺

> * [Railsでのカスタムバリデーション実装からテストまで – 東京伊勢海老通信](https://altarf.net/computer/rails/3085)
> * [全角1文字を半角2文字としてカウントするvalidator – nkmrshnの日記](http://d.hatena.ne.jp/nkmrshn/20110704/1309775411)
[/markdown]