file_size_validator.rb 2.37 KB
Newer Older
1 2
# frozen_string_literal: true

gitlabhq's avatar
gitlabhq committed
3
class FileSizeValidator < ActiveModel::EachValidator
4 5
  MESSAGES = { is: :wrong_size, minimum: :size_too_small, maximum: :size_too_big }.freeze
  CHECKS   = { is: :==, minimum: :>=, maximum: :<= }.freeze
gitlabhq's avatar
gitlabhq committed
6

7 8
  DEFAULT_TOKENIZER = -> (value) { value.split(//) }.freeze
  RESERVED_OPTIONS  = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long].freeze
gitlabhq's avatar
gitlabhq committed
9 10 11 12

  def initialize(options)
    if range = (options.delete(:in) || options.delete(:within))
      raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
13

gitlabhq's avatar
gitlabhq committed
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
      options[:minimum], options[:maximum] = range.begin, range.end
      options[:maximum] -= 1 if range.exclude_end?
    end

    super
  end

  def check_validity!
    keys = CHECKS.keys & options.keys

    if keys.empty?
      raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
    end

    keys.each do |key|
      value = options[key]

31 32
      unless (value.is_a?(Integer) && value >= 0) || value.is_a?(Symbol)
        raise ArgumentError, ":#{key} must be a nonnegative Integer or symbol"
gitlabhq's avatar
gitlabhq committed
33 34 35 36
      end
    end
  end

37
  # rubocop: disable CodeReuse/ActiveRecord
gitlabhq's avatar
gitlabhq committed
38
  def validate_each(record, attribute, value)
Douwe Maan's avatar
Douwe Maan committed
39
    raise(ArgumentError, "A CarrierWave::Uploader::Base object was expected") unless value.is_a? CarrierWave::Uploader::Base
Nihad Abbasov's avatar
Nihad Abbasov committed
40

Douwe Maan's avatar
Douwe Maan committed
41
    value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value) if value.is_a?(String)
gitlabhq's avatar
gitlabhq committed
42 43 44 45

    CHECKS.each do |key, validity_check|
      next unless check_value = options[key]

46 47 48 49 50
      check_value =
        case check_value
        when Integer
          check_value
        when Symbol
51
          record.public_send(check_value) # rubocop:disable GitlabSecurity/PublicSend
52 53
        end

gitlabhq's avatar
gitlabhq committed
54 55 56
      value ||= [] if key == :maximum

      value_size = value.size
57
      next if value_size.public_send(validity_check, check_value) # rubocop:disable GitlabSecurity/PublicSend
gitlabhq's avatar
gitlabhq committed
58 59 60 61 62 63 64 65 66 67

      errors_options = options.except(*RESERVED_OPTIONS)
      errors_options[:file_size] = help.number_to_human_size check_value

      default_message = options[MESSAGES[key]]
      errors_options[:message] ||= default_message if default_message

      record.errors.add(attribute, MESSAGES[key], errors_options)
    end
  end
68
  # rubocop: enable CodeReuse/ActiveRecord
Nihad Abbasov's avatar
Nihad Abbasov committed
69

gitlabhq's avatar
gitlabhq committed
70 71 72 73 74 75 76 77 78
  def help
    Helper.instance
  end

  class Helper
    include Singleton
    include ActionView::Helpers::NumberHelper
  end
end