Railsに組み込まれているバリデータだけでは足りなくなると、独自のバリデーションルールを書かなければならなくなります。これをやりだすとモデルがたちまち肥大化してしまうので、これを避けるにはバリデーションロジックを別クラスに切り出すのが一番です。これによってコードがすっきりと分離され、再利用しやすくなります。
class Site < ApplicationRecord
has_one_attached :og_image
has_one_attached :favicon
has_many_attached :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 }
validates :images, attachment: { purge: true, content_type: %r{\\Aimage/(png|jpeg)\\Z}, maximum: 524_288_000 }
end
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])}以下にしてください")
#<=number_to_human_sizeはinqlude activesupport::numberhelperによって使えるメソッド
#<=何Mbというようなファイルサイズの単位の表示に使える
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
Railsガイドより
個別の属性を検証するためのカスタムバリデータを追加するには、ActiveModel::EachValidator
を使用するのが最も簡単で便利です。この場合、このカスタムバリデータクラスはvalidate_each
メソッドを実装する必要があります。このメソッドは、
そのインスタンスに対応するレコード、(record)※これで言えばsiteのレコード
バリデーションを行う属性、(attribute)※imagesや、favicon
渡されたインスタンスの属性の値(value)※渡されたパラメータの中身の3つの引数を取ります。