Rails4
Validation
ActiveModel

小文字/大文字や単数/複数形も含めてexclusionチェックするCustomValidator

ユーザー名などのバリデーションに、予約語リストを作って対処していたのですが、ケースセンシティブだったり、単数・複数を個別に書くのが大変だったので、CustomValidatorを作ってみました。

exclusionを使った場合

/app/models/user.rb
validates :title,
           exclusion: { in: YAML.safe_load(open("#{Rails.root}/config/exclusion/username.yml", &:read)) }
/config/exclusion/username.yml
# 予約語リスト
- user
- users
- manager
- managers

この場合、単数形・複数形は、自力で書いてるのでいいのですが、大文字小文字まで自力でやるわけには行きません…。

CustomValidatorを使った場合

カスタムバリデータークラスを作成します。

/app/validators/exclusion_extend_validator.rb
# coding: utf-8
# frozen_string_literal: true
# 通常のexclusionに追加で、小文字単数形にしてチェックをする
class ExclusionExtendValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    return unless value.present?

    list = options[:in]
    # 含まれていたらエラー
    if list.include?(value)
      record.errors.add(attribute, 'is reserved')
    # 小文字、単数形に変換して再度チェック
    elsif list.include?(value.try(:downcase).try(:singularize))
      record.errors.add(attribute, 'is reserved')
    end
  end
end

上記ファイルを読み込むように、下記追加します。

/config/application.rb
config.autoload_paths += Dir["#{config.root}/app/validators"]

exclusion:のときと同じように、使います。

/app/models/user.rb
validates :title,
           exclusion_extend: { in: YAML.safe_load(open("#{Rails.root}/config/exclusion/username.yml", &:read)) }

大文字/小文字や、単数形/複数形も含めてチェックされるようになります。
※ yamlファイルには、小文字・単数形で書くようにします。

/config/exclusion/username.yml
# 予約語リスト(小文字・単数形)
- user
- manager

これでuserusersUserUSERSなども弾かれるようになります。