active_storage_validations 1.1.3 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +81 -55
- data/lib/active_storage_validations/aspect_ratio_validator.rb +47 -22
- data/lib/active_storage_validations/attached_validator.rb +12 -3
- data/lib/active_storage_validations/concerns/errorable.rb +38 -0
- data/lib/active_storage_validations/concerns/symbolizable.rb +8 -6
- data/lib/active_storage_validations/content_type_validator.rb +41 -6
- data/lib/active_storage_validations/dimension_validator.rb +15 -15
- data/lib/active_storage_validations/limit_validator.rb +44 -7
- data/lib/active_storage_validations/matchers/aspect_ratio_validator_matcher.rb +128 -0
- data/lib/active_storage_validations/matchers/attached_validator_matcher.rb +20 -23
- data/lib/active_storage_validations/matchers/concerns/active_storageable.rb +17 -0
- data/lib/active_storage_validations/matchers/concerns/allow_blankable.rb +26 -0
- data/lib/active_storage_validations/matchers/concerns/contextable.rb +35 -0
- data/lib/active_storage_validations/matchers/concerns/messageable.rb +26 -0
- data/lib/active_storage_validations/matchers/concerns/rspecable.rb +25 -0
- data/lib/active_storage_validations/matchers/concerns/validatable.rb +5 -10
- data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +39 -25
- data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +61 -44
- data/lib/active_storage_validations/matchers/size_validator_matcher.rb +41 -24
- data/lib/active_storage_validations/matchers.rb +1 -0
- data/lib/active_storage_validations/metadata.rb +42 -28
- data/lib/active_storage_validations/processable_image_validator.rb +14 -5
- data/lib/active_storage_validations/size_validator.rb +7 -6
- data/lib/active_storage_validations/version.rb +1 -1
- data/lib/active_storage_validations.rb +1 -1
- metadata +9 -3
- data/lib/active_storage_validations/error_handler.rb +0 -21
@@ -1,32 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'concerns/errorable.rb'
|
3
4
|
require_relative 'concerns/symbolizable.rb'
|
4
5
|
|
5
6
|
module ActiveStorageValidations
|
6
7
|
class LimitValidator < ActiveModel::EachValidator # :nodoc:
|
7
8
|
include OptionProcUnfolding
|
8
|
-
include
|
9
|
+
include Errorable
|
9
10
|
include Symbolizable
|
10
11
|
|
11
12
|
AVAILABLE_CHECKS = %i[max min].freeze
|
13
|
+
ERROR_TYPES = %i[
|
14
|
+
limit_out_of_range
|
15
|
+
].freeze
|
12
16
|
|
13
17
|
def check_validity!
|
14
|
-
|
15
|
-
|
16
|
-
end
|
18
|
+
ensure_at_least_one_validator_option
|
19
|
+
ensure_arguments_validity
|
17
20
|
end
|
18
21
|
|
19
22
|
def validate_each(record, attribute, _)
|
20
23
|
files = Array.wrap(record.send(attribute)).reject { |file| file.blank? }.compact.uniq
|
21
24
|
flat_options = unfold_procs(record, self.options, AVAILABLE_CHECKS)
|
25
|
+
|
26
|
+
return true if files_count_valid?(files.count, flat_options)
|
27
|
+
|
22
28
|
errors_options = initialize_error_options(options)
|
23
29
|
errors_options[:min] = flat_options[:min]
|
24
30
|
errors_options[:max] = flat_options[:max]
|
25
|
-
|
26
|
-
return true if files_count_valid?(files.count, flat_options)
|
27
|
-
add_error(record, attribute, :limit_out_of_range, **errors_options)
|
31
|
+
add_error(record, attribute, ERROR_TYPES.first, **errors_options)
|
28
32
|
end
|
29
33
|
|
34
|
+
private
|
35
|
+
|
30
36
|
def files_count_valid?(count, flat_options)
|
31
37
|
if flat_options[:max].present? && flat_options[:min].present?
|
32
38
|
count >= flat_options[:min] && count <= flat_options[:max]
|
@@ -36,5 +42,36 @@ module ActiveStorageValidations
|
|
36
42
|
count >= flat_options[:min]
|
37
43
|
end
|
38
44
|
end
|
45
|
+
|
46
|
+
def ensure_at_least_one_validator_option
|
47
|
+
unless AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
|
48
|
+
raise ArgumentError, 'You must pass either :max or :min to the validator'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def ensure_arguments_validity
|
53
|
+
return true if min_max_are_proc? || min_or_max_is_proc_and_other_not_present?
|
54
|
+
|
55
|
+
raise ArgumentError, 'You must pass integers to :min and :max' if min_or_max_defined_and_not_integer?
|
56
|
+
raise ArgumentError, 'You must pass a higher value to :max than to :min' if min_higher_than_max?
|
57
|
+
end
|
58
|
+
|
59
|
+
def min_max_are_proc?
|
60
|
+
options[:min]&.is_a?(Proc) && options[:max]&.is_a?(Proc)
|
61
|
+
end
|
62
|
+
|
63
|
+
def min_or_max_is_proc_and_other_not_present?
|
64
|
+
(options[:min]&.is_a?(Proc) && options[:max].nil?) ||
|
65
|
+
(options[:min].nil? && options[:max]&.is_a?(Proc))
|
66
|
+
end
|
67
|
+
|
68
|
+
def min_or_max_defined_and_not_integer?
|
69
|
+
(options.key?(:min) && !options[:min].is_a?(Integer)) ||
|
70
|
+
(options.key?(:max) && !options[:max].is_a?(Integer))
|
71
|
+
end
|
72
|
+
|
73
|
+
def min_higher_than_max?
|
74
|
+
options[:min] > options[:max] if options[:min].is_a?(Integer) && options[:max].is_a?(Integer)
|
75
|
+
end
|
39
76
|
end
|
40
77
|
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'concerns/active_storageable.rb'
|
4
|
+
require_relative 'concerns/allow_blankable.rb'
|
5
|
+
require_relative 'concerns/contextable.rb'
|
6
|
+
require_relative 'concerns/messageable.rb'
|
7
|
+
require_relative 'concerns/rspecable.rb'
|
8
|
+
require_relative 'concerns/validatable.rb'
|
9
|
+
|
10
|
+
module ActiveStorageValidations
|
11
|
+
module Matchers
|
12
|
+
def validate_aspect_ratio_of(name, expected_aspect_ratio)
|
13
|
+
AspectRatioValidatorMatcher.new(name, expected_aspect_ratio)
|
14
|
+
end
|
15
|
+
|
16
|
+
class AspectRatioValidatorMatcher
|
17
|
+
include ActiveStorageable
|
18
|
+
include AllowBlankable
|
19
|
+
include Contextable
|
20
|
+
include Messageable
|
21
|
+
include Rspecable
|
22
|
+
include Validatable
|
23
|
+
|
24
|
+
def initialize(attribute_name)
|
25
|
+
initialize_allow_blankable
|
26
|
+
initialize_contextable
|
27
|
+
initialize_messageable
|
28
|
+
initialize_rspecable
|
29
|
+
@attribute_name = attribute_name
|
30
|
+
@allowed_aspect_ratios = @rejected_aspect_ratios = []
|
31
|
+
end
|
32
|
+
|
33
|
+
def description
|
34
|
+
"validate the aspect ratios allowed on :#{@attribute_name}."
|
35
|
+
end
|
36
|
+
|
37
|
+
def failure_message
|
38
|
+
"is expected to validate aspect ratio of :#{@attribute_name}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def allowing(*aspect_ratios)
|
42
|
+
@allowed_aspect_ratios = aspect_ratios.flatten
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def rejecting(*aspect_ratios)
|
47
|
+
@rejected_aspect_ratios = aspect_ratios.flatten
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def matches?(subject)
|
52
|
+
@subject = subject.is_a?(Class) ? subject.new : subject
|
53
|
+
|
54
|
+
is_a_valid_active_storage_attribute? &&
|
55
|
+
is_context_valid? &&
|
56
|
+
is_allowing_blank? &&
|
57
|
+
is_custom_message_valid? &&
|
58
|
+
all_allowed_aspect_ratios_allowed? &&
|
59
|
+
all_rejected_aspect_ratios_rejected?
|
60
|
+
end
|
61
|
+
|
62
|
+
protected
|
63
|
+
|
64
|
+
def all_allowed_aspect_ratios_allowed?
|
65
|
+
@allowed_aspect_ratios_not_allowed ||= @allowed_aspect_ratios.reject { |aspect_ratio| aspect_ratio_allowed?(aspect_ratio) }
|
66
|
+
@allowed_aspect_ratios_not_allowed.empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
def all_rejected_aspect_ratios_rejected?
|
70
|
+
@rejected_aspect_ratios_not_rejected ||= @rejected_aspect_ratios.select { |aspect_ratio| aspect_ratio_allowed?(aspect_ratio) }
|
71
|
+
@rejected_aspect_ratios_not_rejected.empty?
|
72
|
+
end
|
73
|
+
|
74
|
+
def aspect_ratio_allowed?(aspect_ratio)
|
75
|
+
width, height = valid_width_and_height_for(aspect_ratio)
|
76
|
+
|
77
|
+
mock_dimensions_for(attach_file, width, height) do
|
78
|
+
validate
|
79
|
+
is_valid?
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def is_custom_message_valid?
|
84
|
+
return true unless @custom_message
|
85
|
+
|
86
|
+
mock_dimensions_for(attach_file, -1, -1) do
|
87
|
+
validate
|
88
|
+
has_an_error_message_which_is_custom_message?
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def attach_file
|
93
|
+
@subject.public_send(@attribute_name).attach(dummy_file)
|
94
|
+
@subject.public_send(@attribute_name)
|
95
|
+
end
|
96
|
+
|
97
|
+
def dummy_file
|
98
|
+
{
|
99
|
+
io: Tempfile.new('Hello world!'),
|
100
|
+
filename: 'test.png',
|
101
|
+
content_type: 'image/png'
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
def mock_dimensions_for(attachment, width, height)
|
106
|
+
Matchers.mock_metadata(attachment, width, height) do
|
107
|
+
yield
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def valid_width_and_height_for(aspect_ratio)
|
112
|
+
case aspect_ratio
|
113
|
+
when :square then [100, 100]
|
114
|
+
when :portrait then [100, 200]
|
115
|
+
when :landscape then [200, 100]
|
116
|
+
when validator_class::ASPECT_RATIO_REGEX
|
117
|
+
aspect_ratio =~ validator_class::ASPECT_RATIO_REGEX
|
118
|
+
x = Regexp.last_match(1).to_i
|
119
|
+
y = Regexp.last_match(2).to_i
|
120
|
+
|
121
|
+
[100 * x, 100 * y]
|
122
|
+
else
|
123
|
+
[-1, -1]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'concerns/active_storageable.rb'
|
4
|
+
require_relative 'concerns/contextable.rb'
|
5
|
+
require_relative 'concerns/messageable.rb'
|
6
|
+
require_relative 'concerns/rspecable.rb'
|
3
7
|
require_relative 'concerns/validatable.rb'
|
4
8
|
|
5
9
|
module ActiveStorageValidations
|
@@ -9,46 +13,39 @@ module ActiveStorageValidations
|
|
9
13
|
end
|
10
14
|
|
11
15
|
class AttachedValidatorMatcher
|
16
|
+
include ActiveStorageable
|
17
|
+
include Contextable
|
18
|
+
include Messageable
|
19
|
+
include Rspecable
|
12
20
|
include Validatable
|
13
21
|
|
14
22
|
def initialize(attribute_name)
|
23
|
+
initialize_contextable
|
24
|
+
initialize_messageable
|
25
|
+
initialize_rspecable
|
15
26
|
@attribute_name = attribute_name
|
16
|
-
@custom_message = nil
|
17
27
|
end
|
18
28
|
|
19
29
|
def description
|
20
|
-
"validate
|
30
|
+
"validate that :#{@attribute_name} must be attached"
|
21
31
|
end
|
22
32
|
|
23
|
-
def
|
24
|
-
|
25
|
-
self
|
33
|
+
def failure_message
|
34
|
+
"is expected to validate attachment of :#{@attribute_name}"
|
26
35
|
end
|
27
36
|
|
28
37
|
def matches?(subject)
|
29
38
|
@subject = subject.is_a?(Class) ? subject.new : subject
|
30
|
-
responds_to_methods &&
|
31
|
-
is_valid_when_file_attached? &&
|
32
|
-
is_invalid_when_file_not_attached? &&
|
33
|
-
validate_custom_message?
|
34
|
-
end
|
35
39
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
"is expected to not validate attached of #{@attribute_name}"
|
40
|
+
is_a_valid_active_storage_attribute? &&
|
41
|
+
is_context_valid? &&
|
42
|
+
is_custom_message_valid? &&
|
43
|
+
is_valid_when_file_attached? &&
|
44
|
+
is_invalid_when_file_not_attached?
|
42
45
|
end
|
43
46
|
|
44
47
|
private
|
45
48
|
|
46
|
-
def responds_to_methods
|
47
|
-
@subject.respond_to?(@attribute_name) &&
|
48
|
-
@subject.public_send(@attribute_name).respond_to?(:attach) &&
|
49
|
-
@subject.public_send(@attribute_name).respond_to?(:detach)
|
50
|
-
end
|
51
|
-
|
52
49
|
def is_valid_when_file_attached?
|
53
50
|
attach_dummy_file unless file_attached?
|
54
51
|
validate
|
@@ -61,7 +58,7 @@ module ActiveStorageValidations
|
|
61
58
|
!is_valid?
|
62
59
|
end
|
63
60
|
|
64
|
-
def
|
61
|
+
def is_custom_message_valid?
|
65
62
|
return true unless @custom_message
|
66
63
|
|
67
64
|
detach_file if file_attached?
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
|
3
|
+
module ActiveStorageValidations
|
4
|
+
module Matchers
|
5
|
+
module ActiveStorageable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def is_a_valid_active_storage_attribute?
|
11
|
+
@subject.respond_to?(@attribute_name) &&
|
12
|
+
@subject.public_send(@attribute_name).respond_to?(:attach) &&
|
13
|
+
@subject.public_send(@attribute_name).respond_to?(:detach)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
|
3
|
+
module ActiveStorageValidations
|
4
|
+
module Matchers
|
5
|
+
module AllowBlankable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def initialize_allow_blankable
|
9
|
+
@allow_blank = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def allow_blank
|
13
|
+
@allow_blank = true
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def is_allowing_blank?
|
20
|
+
return true unless @allow_blank
|
21
|
+
|
22
|
+
validate
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
|
3
|
+
module ActiveStorageValidations
|
4
|
+
module Matchers
|
5
|
+
module Contextable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def initialize_contextable
|
9
|
+
@context = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def on(context)
|
13
|
+
@context = context
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def is_context_valid?
|
20
|
+
return true if !@context && !(attribute_validator && attribute_validator.options[:on])
|
21
|
+
|
22
|
+
raise ArgumentError, "This validator matcher needs the #on option to work since its validator has one" if !@context
|
23
|
+
raise ArgumentError, "This validator matcher option only allows a symbol or an array" if !(@context.is_a?(Symbol) || @context.is_a?(Array))
|
24
|
+
|
25
|
+
if @context.is_a?(Array) && attribute_validator.options[:on].is_a?(Array)
|
26
|
+
@context.to_set == attribute_validator.options[:on].to_set
|
27
|
+
elsif @context.is_a?(Symbol) && attribute_validator.options[:on].is_a?(Symbol)
|
28
|
+
@context == attribute_validator.options[:on]
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
|
3
|
+
module ActiveStorageValidations
|
4
|
+
module Matchers
|
5
|
+
module Messageable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def initialize_messageable
|
9
|
+
@custom_message = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def with_message(custom_message)
|
13
|
+
@custom_message = custom_message
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def has_an_error_message_which_is_custom_message?
|
20
|
+
validator_errors_for_attribute.one? do |error|
|
21
|
+
error[:error] == @custom_message
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
|
3
|
+
module ActiveStorageValidations
|
4
|
+
module Matchers
|
5
|
+
module Rspecable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def initialize_rspecable
|
9
|
+
@failure_message_artefacts = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def description
|
13
|
+
raise NotImplementedError, "#{self.class} did not define #{__method__}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def failure_message
|
17
|
+
raise NotImplementedError, "#{self.class} did not define #{__method__}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def failure_message_when_negated
|
21
|
+
failure_message.sub(/is expected to validate/, 'is expected not to validate')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
require "active_support/concern"
|
3
2
|
|
4
3
|
module ActiveStorageValidations
|
@@ -9,7 +8,7 @@ module ActiveStorageValidations
|
|
9
8
|
private
|
10
9
|
|
11
10
|
def validate
|
12
|
-
@subject.validate
|
11
|
+
@subject.validate(@context)
|
13
12
|
end
|
14
13
|
|
15
14
|
def validator_errors_for_attribute
|
@@ -35,18 +34,14 @@ module ActiveStorageValidations
|
|
35
34
|
self.class.name.gsub(/::Matchers|Matcher/, '').constantize
|
36
35
|
end
|
37
36
|
|
38
|
-
def
|
39
|
-
|
37
|
+
def attribute_validator
|
38
|
+
@subject.class.validators_on(@attribute_name).find do |validator|
|
40
39
|
validator.class == validator_class
|
41
40
|
end
|
42
|
-
|
43
|
-
associated_validation.options[:message]
|
44
41
|
end
|
45
42
|
|
46
|
-
def
|
47
|
-
|
48
|
-
error[:error] == @custom_message
|
49
|
-
end
|
43
|
+
def error_from_custom_message
|
44
|
+
attribute_validator.options[:message]
|
50
45
|
end
|
51
46
|
end
|
52
47
|
end
|
@@ -3,6 +3,11 @@
|
|
3
3
|
# Big thank you to the paperclip validation matchers:
|
4
4
|
# https://github.com/thoughtbot/paperclip/blob/v6.1.0/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb
|
5
5
|
|
6
|
+
require_relative 'concerns/active_storageable.rb'
|
7
|
+
require_relative 'concerns/allow_blankable.rb'
|
8
|
+
require_relative 'concerns/contextable.rb'
|
9
|
+
require_relative 'concerns/messageable.rb'
|
10
|
+
require_relative 'concerns/rspecable.rb'
|
6
11
|
require_relative 'concerns/validatable.rb'
|
7
12
|
|
8
13
|
module ActiveStorageValidations
|
@@ -12,16 +17,30 @@ module ActiveStorageValidations
|
|
12
17
|
end
|
13
18
|
|
14
19
|
class ContentTypeValidatorMatcher
|
20
|
+
include ActiveStorageable
|
21
|
+
include AllowBlankable
|
22
|
+
include Contextable
|
23
|
+
include Messageable
|
24
|
+
include Rspecable
|
15
25
|
include Validatable
|
16
26
|
|
17
27
|
def initialize(attribute_name)
|
28
|
+
initialize_allow_blankable
|
29
|
+
initialize_contextable
|
30
|
+
initialize_messageable
|
31
|
+
initialize_rspecable
|
18
32
|
@attribute_name = attribute_name
|
19
33
|
@allowed_types = @rejected_types = []
|
20
|
-
@custom_message = nil
|
21
34
|
end
|
22
35
|
|
23
36
|
def description
|
24
|
-
"validate the content types allowed on
|
37
|
+
"validate the content types allowed on :#{@attribute_name}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def failure_message
|
41
|
+
message = ["is expected to validate the content types of :#{@attribute_name}"]
|
42
|
+
build_failure_message(message)
|
43
|
+
message.join("\n")
|
25
44
|
end
|
26
45
|
|
27
46
|
def allowing(*types)
|
@@ -34,42 +53,37 @@ module ActiveStorageValidations
|
|
34
53
|
self
|
35
54
|
end
|
36
55
|
|
37
|
-
def with_message(message)
|
38
|
-
@custom_message = message
|
39
|
-
self
|
40
|
-
end
|
41
|
-
|
42
56
|
def matches?(subject)
|
43
57
|
@subject = subject.is_a?(Class) ? subject.new : subject
|
44
58
|
|
45
|
-
|
59
|
+
is_a_valid_active_storage_attribute? &&
|
60
|
+
is_context_valid? &&
|
61
|
+
is_allowing_blank? &&
|
62
|
+
is_custom_message_valid? &&
|
46
63
|
all_allowed_types_allowed? &&
|
47
|
-
all_rejected_types_rejected?
|
48
|
-
validate_custom_message?
|
64
|
+
all_rejected_types_rejected?
|
49
65
|
end
|
50
66
|
|
51
|
-
|
52
|
-
message = ["Expected #{@attribute_name}"]
|
67
|
+
protected
|
53
68
|
|
69
|
+
def build_failure_message(message)
|
54
70
|
if @allowed_types_not_allowed.present?
|
55
|
-
message << "
|
56
|
-
message << "#{@allowed_types_not_allowed
|
71
|
+
message << " the following content type#{'s' if @allowed_types.count > 1} should be allowed: :#{@allowed_types.join(", :")}"
|
72
|
+
message << " but #{pluralize(@allowed_types_not_allowed)} rejected"
|
57
73
|
end
|
58
74
|
|
59
75
|
if @rejected_types_not_rejected.present?
|
60
|
-
message << "
|
61
|
-
message << "#{@rejected_types_not_rejected
|
76
|
+
message << " the following content type#{'s' if @rejected_types.count > 1} should be rejected: :#{@rejected_types.join(", :")}"
|
77
|
+
message << " but #{pluralize(@rejected_types_not_rejected)} accepted"
|
62
78
|
end
|
63
|
-
|
64
|
-
message.join("\n")
|
65
79
|
end
|
66
80
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
81
|
+
def pluralize(types)
|
82
|
+
if types.count == 1
|
83
|
+
":#{types[0]} was"
|
84
|
+
else
|
85
|
+
":#{types.join(", :")} were"
|
86
|
+
end
|
73
87
|
end
|
74
88
|
|
75
89
|
def all_allowed_types_allowed?
|
@@ -92,7 +106,7 @@ module ActiveStorageValidations
|
|
92
106
|
@subject.public_send(@attribute_name).attach(attachment_for(type))
|
93
107
|
end
|
94
108
|
|
95
|
-
def
|
109
|
+
def is_custom_message_valid?
|
96
110
|
return true unless @custom_message
|
97
111
|
|
98
112
|
attach_invalid_content_type_file
|