diff --git a/Gemfile.lock b/Gemfile.lock index df66c8fa..041f3a88 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - active_storage_validations (1.1.0) + active_storage_validations (1.1.1) activejob (>= 5.2.0) activemodel (>= 5.2.0) activestorage (>= 5.2.0) diff --git a/lib/active_storage_validations/matchers/size_validator_matcher.rb b/lib/active_storage_validations/matchers/size_validator_matcher.rb index b234f978..6633a471 100644 --- a/lib/active_storage_validations/matchers/size_validator_matcher.rb +++ b/lib/active_storage_validations/matchers/size_validator_matcher.rb @@ -13,6 +13,7 @@ def initialize(attribute_name) @attribute_name = attribute_name @min = @max = nil @custom_message = nil + @context = nil end def description @@ -49,6 +50,10 @@ def with_message(message) self end + def on(context) + @context = context + end + def matches?(subject) @subject = subject.is_a?(Class) ? subject.new : subject responds_to_methods && not_lower_than_min? && higher_than_min? && lower_than_max? && not_higher_than_max? @@ -86,11 +91,15 @@ def not_higher_than_max? @max.nil? || @max == Float::INFINITY || !passes_validation_with_size(@max + 1) end + def validate + @subject.validate(@context) + end + def passes_validation_with_size(new_size) io = Tempfile.new('Hello world!') Matchers.stub_method(io, :size, new_size) do @subject.public_send(@attribute_name).attach(io: io, filename: 'test.png', content_type: 'image/pg') - @subject.validate + validate exclude_error_message = @custom_message || "file_size_not_" @subject.errors.details[@attribute_name].none? do |error| error[:error].to_s.include?(exclude_error_message) diff --git a/lib/active_storage_validations/size_validator.rb b/lib/active_storage_validations/size_validator.rb index 67b04350..49afa38d 100644 --- a/lib/active_storage_validations/size_validator.rb +++ b/lib/active_storage_validations/size_validator.rb @@ -31,7 +31,8 @@ def validate_each(record, attribute, _value) errors_options[:file_size] = number_to_human_size(file.blob.byte_size) errors_options[:min_size] = number_to_human_size(min_size(flat_options)) errors_options[:max_size] = number_to_human_size(max_size(flat_options)) - error_type = "file_size_not_#{flat_options.keys.first}".to_sym + keys = AVAILABLE_CHECKS & flat_options.keys + error_type = "file_size_not_#{keys.first}".to_sym add_error(record, attribute, error_type, **errors_options) break diff --git a/test/dummy/app/models/size/portfolio.rb b/test/dummy/app/models/size/portfolio.rb index 3f26b8ea..22baf32c 100644 --- a/test/dummy/app/models/size/portfolio.rb +++ b/test/dummy/app/models/size/portfolio.rb @@ -17,6 +17,8 @@ class Size::Portfolio < ApplicationRecord has_one_attached :size_greater_than_or_equal_to has_one_attached :size_between has_one_attached :size_with_message + has_one_attached :size_with_context + has_one_attached :size_with_if has_one_attached :proc_size_less_than has_one_attached :proc_size_less_than_or_equal_to @@ -33,6 +35,8 @@ class Size::Portfolio < ApplicationRecord validates :size_greater_than_or_equal_to, size: { greater_than_or_equal_to: 7.kilobytes } validates :size_between, size: { between: 2.kilobytes..7.kilobytes } validates :size_with_message, size: { between: 2.kilobytes..7.kilobytes, message: 'is not in required file size range' } + validates :size_with_context, size: { less_than: 2.kilobytes }, on: :custom_context + validates :size_with_if, size: { less_than: 2.kilobytes }, if: -> { title == 'very_nice_title' } validates :proc_size_less_than, size: { less_than: -> (record) { 2.kilobytes } } validates :proc_size_less_than_or_equal_to, size: { less_than_or_equal_to: -> (record) { 2.kilobytes } } diff --git a/test/matchers/size_validator_matcher_test.rb b/test/matchers/size_validator_matcher_test.rb index 1a3b6f80..075addaa 100644 --- a/test/matchers/size_validator_matcher_test.rb +++ b/test/matchers/size_validator_matcher_test.rb @@ -153,6 +153,14 @@ class UnknownAttachedAttribute < ActiveStorageValidations::Matchers::SizeValidat end end + class WithContextMatcher < ActiveStorageValidations::Matchers::SizeValidatorMatcher::Test + test 'matches when provided with the validation context' do + matcher = ActiveStorageValidations::Matchers::SizeValidatorMatcher.new(:size_with_context) + matcher.less_than 2.kilobytes + matcher.on(:custom_context) + assert matcher.matches?(Size::Portfolio) + end + end # Other tests test 'matches when provided with an instance' do diff --git a/test/validators/size_validator_test.rb b/test/validators/size_validator_test.rb index 4fd93e0b..9da7e117 100644 --- a/test/validators/size_validator_test.rb +++ b/test/validators/size_validator_test.rb @@ -239,6 +239,41 @@ def error_message end end + class WithContext < ActiveStorageValidations::SizeValidator::Test + # validates :size_with_context, size: { less_than: 2.kilobytes }, on: :custom_context + + test 'generates correct error message' do + pt = Size::Portfolio.new(title: 'Matisse') + pt.size_with_context.attach(file_10ko) + + refute pt.invalid? + refute pt.valid?(:custom_context) + + assert_equal( + ['Size with context file size must be less than 2 KB (current size is 10 KB)'], + pt.errors.full_messages + ) + end + end + + class WithIf < ActiveStorageValidations::SizeValidator::Test + # validates :size_with_if, size: { less_than: 2.kilobytes }, if: -> { title == 'very_nice_title' } + + test 'generates correct error message' do + pt = Size::Portfolio.new(title: 'Matisse') + pt.size_with_if.attach(file_10ko) + + refute pt.invalid? + + pt.title = 'very_nice_title' + refute pt.valid? + + assert_equal( + ['Size with if file size must be less than 2 KB (current size is 10 KB)'], + pt.errors.full_messages + ) + end + end end def file_1ko