Skip to content

Instantly share code, notes, and snippets.

@donnfelker
Last active October 28, 2023 02:51
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save donnfelker/9264bbc388f47a1db62f6aad6a9e1391 to your computer and use it in GitHub Desktop.
Save donnfelker/9264bbc388f47a1db62f6aad6a9e1391 to your computer and use it in GitHub Desktop.
Custom Active Storage Validator
class AttachedValidator < ActiveModel::EachValidator
# Active Storage validator to ensure that an attachment is attached.
#
# usage:
# validates :upload, attached: true
#
def validate_each(record, attribute, _value)
return if record.send(attribute).attached?
errors_options = {}
errors_options[:message] = options[:message] if options[:message].present?
record.errors.add(attribute, :blank, **errors_options)
end
end
# Source: https://github.com/igorkasyanchuk/active_storage_validations
# Example usage:
# validates :preview, attached: true, size: { less_than: 100.megabytes , message: 'is not given between size' }
class SizeValidator < ActiveModel::EachValidator # :nodoc:
delegate :number_to_human_size, to: ActiveSupport::NumberHelper
AVAILABLE_CHECKS = %i[less_than less_than_or_equal_to greater_than greater_than_or_equal_to between].freeze
def check_validity!
return true if AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
raise ArgumentError, "You must pass either :less_than, :greater_than, or :between to the validator"
end
def validate_each(record, attribute, _value)
# only attached
return true unless record.send(attribute).attached?
files = Array.wrap(record.send(attribute))
errors_options = {}
errors_options[:message] = options[:message] if options[:message].present?
files.each do |file|
next if content_size_valid?(file.blob.byte_size)
errors_options[:file_size] = number_to_human_size(file.blob.byte_size)
errors_options[:min_size] = number_to_human_size(min_size)
errors_options[:max_size] = number_to_human_size(max_size)
record.errors.add(attribute, :file_size_out_of_range, **errors_options)
break
end
end
def content_size_valid?(file_size)
if options[:between].present?
options[:between].include?(file_size)
elsif options[:less_than].present?
file_size < options[:less_than]
elsif options[:less_than_or_equal_to].present?
file_size <= options[:less_than_or_equal_to]
elsif options[:greater_than].present?
file_size > options[:greater_than]
elsif options[:greater_than_or_equal_to].present?
file_size >= options[:greater_than_or_equal_to]
end
end
def min_size
options[:between]&.min || options[:greater_than] || options[:greater_than_or_equal_to]
end
def max_size
options[:between]&.max || options[:less_than] || options[:less_than_or_equal_to]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment