カスタムバリデーター

[カスタムバリデーター]とは?

モデルに記載するのとは別で、自分でバリデーションメソッドを作成することができる
[app/validators]を作成することで自動で読み込んでくれる

EachValidatorメソッド

[EachValidator]メソッドは、1つの属性に対しての検証を定義できる
[EachValidator]メソッドは、以下の3つを引数として受け取れる
・record: モデルのインスタンス
・attribute: 属性名
value: 属性値
クラス名は、[検証名]Validatorの形式にする必要がある

以下に使用例を記述します

[app/models/site.rb]

class Site < ApplicationRecord
  validates :og_image, attachment: { purge: true, content_type: %r{\Aimage/(png|jpeg)\Z}, maximum: 524_288_000 }  #  [app/validators/attachment_validator.rb]に定義したattachment
  validates :favicon, attachment: { purge: true, content_type: %r{\Aimage/png\Z}, maximum: 524_288_000 }
  validates :main_images, attachment: { purge: true, content_type: %r{\Aimage/(png|jpeg)\Z}, maximum: 524_288_000 }
end
[app/validators/attachment_validator.rb]

class AttachmentValidator < ActiveModel::EachValidator
  include ActiveSupport::NumberHelper

  def validate_each(record, attribute, value)
    return if value.blank? || !value.attached?

    has_error = false

    if options[:maximum]
      if value.is_a?(ActiveStorage::Attached::Many)
        # 画像が複数枚投稿された場合
        value.each do |v|
          unless validate_maximum(record, attribute, v)
            has_error = true
            break
          end
        end
      else
        # 画像が1枚投稿された場合
        has_error = true unless validate_maximum(record, attribute, value)
      end
    end

    if options[:content_type]
      if value.is_a?(ActiveStorage::Attached::Many)
        # 画像が複数枚投稿された場合
        value.each do |v|
          unless validate_content_type(record, attribute, v)
            has_error = true
            break
          end
        end
      else
        # 画像が1枚投稿された場合
        has_error = true unless validate_content_type(record, attribute, value)
      end
    end

    record.send(attribute).purge if options[:purge] && has_error
  end

  private

  def validate_maximum(record, attribute, value)
    if value.byte_size > options[:maximum]
      record.errors[attribute] << (options[:message] || "は#{number_to_human_size(options[:maximum])}以下にしてください")
      false
    else
      true
    end
  end

  def validate_content_type(record, attribute, value)
    if value.content_type.match?(options[:content_type])
      true
    else
      record.errors[attribute] << (options[:message] || 'は対応できないファイル形式です')
      false
    end
  end
end

上記でoptions[:maximum]は、バリデーションに定義したmaximumの値を参照できる(デバッグで確認した以下を参照)

[1] pry(#<AttachmentValidator>)> options[:maximum]
=> 524288000
[2] pry(#<AttachmentValidator>)> options[:content_type]
=> /\Aimage\/(png|jpeg)\Z/
[3] pry(#<AttachmentValidator>)> options[:purge]
=> true

参考記事

Rails カスタムバリデーション(validator, EachValidator) - Qiita