ActiveModel::EachValidator

ActiveModel::EachValidatorを継承したクラスでは、validate_eachというインスタンスメソッドにバリデーションルールを実装します。record、attribute、valueという引数については、後ほど解説いたします。



実装

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]
      has_error = true unless validate_maximum(record, attribute, value)

    end

    if options[:content_type]
      has_error = true unless validate_content_type(record, attribute, value)
    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



record、attribute、valueについて

record:バリデーション対象のオブジェクト

[1] pry(#<AttachmentValidator>)> record
=> #<Site:0x00007fd0418d2160
 id: 1,
 name: "Blog",
 subtitle: "Very awesome!",
 description: "",
 created_at: Sun, 27 Jun 2021 17:42:08 JST +09:00,
 updated_at: Tue, 29 Jun 2021 22:51:10 JST +09:00>


attribute: バリデーション対象の属性名

[2] pry(#<AttachmentValidator>)> attribute
=> :og_image


value:属性名の値

[3] pry(#<AttachmentValidator>)> value
=> #<ActiveStorage::Attached::One:0x00007fd04b6bd6e0
 @dependent=:purge_later,
 @name="og_image",
 @record=
  #<Site:0x00007fd0418d2160
   id: 1,
   name: "Blog",
   subtitle: "Very awesome!",
   description: "",
   created_at: Sun, 27 Jun 2021 17:42:08 JST +09:00,
   updated_at: Tue, 29 Jun 2021 22:51:10 JST +09:00>>



定義したバリデーション ルールの呼び出し

定義したバリデーションルールは、validatesメソッドの呼び出し時にオプションとして指定できるようになります。

class Site < ApplicationRecord
  has_one_attached :og_image
  has_one_attached :favicon
  has_many_attached :main_images

  validates :name, presence: true, length: { maximum: 100 }
  validates :subtitle, length: { maximum: 100 }
  validates :description, length: { maximum: 400 }
  validates :og_image, attachment: { purge: true, content_type: %r{\Aimage/(png|jpeg)\Z}, maximum: 524_288_000 }
  validates :favicon, attachment: { purge: true, content_type: %r{\Aimage/png\Z}, maximum: 524_288_000 }
end