Rails 6 – add validation for files and images

web-development

Validation for active storage isn’t yet integrated in the official release of Rails 6 https://github.com/rails/rails/pull/35390 , in the meantime we can add a minimal solution that will work to validate the size and the type of a file.

First we create a custom validation function that will be called from the model, for example if we have a model file like this

app/models/card.rb

			class card < ApplicationRecord
  has_many_attached :images
  validates :title, presence: true
end
		

we can add validation for the images

			class card < ApplicationRecord
  has_many_attached :images
  validates :images, presence: true, blob: { content_type: ['image/jpg', 'image/jpeg', 'image/png'], size_range: 1..3.megabytes }
  validates :title, presence: true
end
		

When the model will be validated, the function blob will be called with the parameters between the { }

First we have to create the blob function that will be called. To do so create the file app/models/concerns/blob_validator.rb

app/models/concerns/blob_validator.rb

			class BlobValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, values)
    return unless values.attached?

    Array(values).each do |value|
      if options[:size_range].present?
        if options[:size_range].min > value.blob.byte_size
          record.errors.add(attribute, :min_size_error, min_size: ActiveSupport::NumberHelper.number_to_human_size(options[:size_range].min))
        elsif options[:size_range].max < value.blob.byte_size
          record.errors.add(attribute, :max_size_error, max_size: ActiveSupport::NumberHelper.number_to_human_size(options[:size_range].max))
        end
      end

      unless valid_content_type?(value.blob)
        record.errors.add(attribute, :content_type)
      end
    end
  end

  private

  def valid_content_type?(blob)
    return true if options[:content_type].nil?

    case options[:content_type]
    when Regexp
      options[:content_type].match?(blob.content_type)
    when Array
      options[:content_type].include?(blob.content_type)
    when Symbol
      blob.public_send("#{options[:content_type]}?")
    else
      options[:content_type] == blob.content_type
    end
  end
end
		

This is only a simple solution that works, and is a way to play around and learn more about custom validation in Rails. To add a more complete solution (and actually is also what will be integrated in the next Rail 6 release), you can install the active_storage_validation gem by @igorkasyanchuk