active_storage_validations 1.1.4 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +56 -18
- data/config/locales/da.yml +33 -0
- data/config/locales/de.yml +5 -0
- data/config/locales/en.yml +5 -0
- data/config/locales/es.yml +5 -0
- data/config/locales/fr.yml +5 -0
- data/config/locales/it.yml +5 -0
- data/config/locales/ja.yml +5 -0
- data/config/locales/nl.yml +5 -0
- data/config/locales/pl.yml +5 -0
- data/config/locales/pt-BR.yml +5 -0
- data/config/locales/ru.yml +5 -0
- data/config/locales/sv.yml +10 -1
- data/config/locales/tr.yml +5 -0
- data/config/locales/uk.yml +5 -0
- data/config/locales/vi.yml +5 -0
- data/config/locales/zh-CN.yml +5 -0
- data/lib/active_storage_validations/attached_validator.rb +3 -3
- data/lib/active_storage_validations/base_size_validator.rb +66 -0
- data/lib/active_storage_validations/concerns/errorable.rb +2 -2
- data/lib/active_storage_validations/matchers/aspect_ratio_validator_matcher.rb +6 -15
- data/lib/active_storage_validations/matchers/attached_validator_matcher.rb +5 -13
- data/lib/active_storage_validations/matchers/base_size_validator_matcher.rb +134 -0
- data/lib/active_storage_validations/matchers/concerns/attachable.rb +48 -0
- data/lib/active_storage_validations/matchers/concerns/contextable.rb +20 -8
- data/lib/active_storage_validations/matchers/concerns/validatable.rb +9 -3
- data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +5 -2
- data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +6 -15
- data/lib/active_storage_validations/matchers/processable_image_validator_matcher.rb +78 -0
- data/lib/active_storage_validations/matchers/size_validator_matcher.rb +4 -139
- data/lib/active_storage_validations/matchers/total_size_validator_matcher.rb +40 -0
- data/lib/active_storage_validations/matchers.rb +2 -0
- data/lib/active_storage_validations/metadata.rb +20 -2
- data/lib/active_storage_validations/size_validator.rb +4 -49
- data/lib/active_storage_validations/total_size_validator.rb +49 -0
- data/lib/active_storage_validations/version.rb +1 -1
- data/lib/active_storage_validations.rb +2 -1
- metadata +31 -38
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'concerns/active_storageable.rb'
|
4
|
+
require_relative 'concerns/attachable.rb'
|
4
5
|
require_relative 'concerns/contextable.rb'
|
5
6
|
require_relative 'concerns/messageable.rb'
|
6
7
|
require_relative 'concerns/rspecable.rb'
|
@@ -8,12 +9,13 @@ require_relative 'concerns/validatable.rb'
|
|
8
9
|
|
9
10
|
module ActiveStorageValidations
|
10
11
|
module Matchers
|
11
|
-
def validate_attached_of(
|
12
|
-
AttachedValidatorMatcher.new(
|
12
|
+
def validate_attached_of(attribute_name)
|
13
|
+
AttachedValidatorMatcher.new(attribute_name)
|
13
14
|
end
|
14
15
|
|
15
16
|
class AttachedValidatorMatcher
|
16
17
|
include ActiveStorageable
|
18
|
+
include Attachable
|
17
19
|
include Contextable
|
18
20
|
include Messageable
|
19
21
|
include Rspecable
|
@@ -47,7 +49,7 @@ module ActiveStorageValidations
|
|
47
49
|
private
|
48
50
|
|
49
51
|
def is_valid_when_file_attached?
|
50
|
-
|
52
|
+
attach_file unless file_attached?
|
51
53
|
validate
|
52
54
|
is_valid?
|
53
55
|
end
|
@@ -66,16 +68,6 @@ module ActiveStorageValidations
|
|
66
68
|
has_an_error_message_which_is_custom_message?
|
67
69
|
end
|
68
70
|
|
69
|
-
def attach_dummy_file
|
70
|
-
dummy_file = {
|
71
|
-
io: Tempfile.new('.'),
|
72
|
-
filename: 'dummy.txt',
|
73
|
-
content_type: 'text/plain'
|
74
|
-
}
|
75
|
-
|
76
|
-
@subject.public_send(@attribute_name).attach(dummy_file)
|
77
|
-
end
|
78
|
-
|
79
71
|
def file_attached?
|
80
72
|
@subject.public_send(@attribute_name).attached?
|
81
73
|
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Big thank you to the paperclip validation matchers:
|
4
|
+
# https://github.com/thoughtbot/paperclip/blob/v6.1.0/lib/paperclip/matchers/validate_attachment_size_matcher.rb
|
5
|
+
|
6
|
+
require_relative 'concerns/active_storageable.rb'
|
7
|
+
require_relative 'concerns/allow_blankable.rb'
|
8
|
+
require_relative 'concerns/attachable.rb'
|
9
|
+
require_relative 'concerns/contextable.rb'
|
10
|
+
require_relative 'concerns/messageable.rb'
|
11
|
+
require_relative 'concerns/rspecable.rb'
|
12
|
+
require_relative 'concerns/validatable.rb'
|
13
|
+
|
14
|
+
module ActiveStorageValidations
|
15
|
+
module Matchers
|
16
|
+
class BaseSizeValidatorMatcher
|
17
|
+
# BaseSizeValidatorMatcher is an abstract class and shouldn't be instantiated directly.
|
18
|
+
|
19
|
+
include ActiveStorageable
|
20
|
+
include AllowBlankable
|
21
|
+
include Attachable
|
22
|
+
include Contextable
|
23
|
+
include Messageable
|
24
|
+
include Rspecable
|
25
|
+
include Validatable
|
26
|
+
|
27
|
+
def initialize(attribute_name)
|
28
|
+
initialize_allow_blankable
|
29
|
+
initialize_contextable
|
30
|
+
initialize_messageable
|
31
|
+
initialize_rspecable
|
32
|
+
@attribute_name = attribute_name
|
33
|
+
@min = @max = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def less_than(size)
|
37
|
+
@max = size - 1.byte
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def less_than_or_equal_to(size)
|
42
|
+
@max = size
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def greater_than(size)
|
47
|
+
@min = size + 1.byte
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def greater_than_or_equal_to(size)
|
52
|
+
@min = size
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def between(range)
|
57
|
+
@min, @max = range.first, range.last
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def matches?(subject)
|
62
|
+
@subject = subject.is_a?(Class) ? subject.new : subject
|
63
|
+
|
64
|
+
is_a_valid_active_storage_attribute? &&
|
65
|
+
is_context_valid? &&
|
66
|
+
is_allowing_blank? &&
|
67
|
+
is_custom_message_valid? &&
|
68
|
+
not_lower_than_min? &&
|
69
|
+
higher_than_min? &&
|
70
|
+
lower_than_max? &&
|
71
|
+
not_higher_than_max?
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
def build_failure_message(message)
|
77
|
+
return unless @failure_message_artefacts.present?
|
78
|
+
|
79
|
+
message << " but there seem to have issues with the matcher methods you used, since:"
|
80
|
+
@failure_message_artefacts.each do |error_case|
|
81
|
+
message << " validation failed when provided with a #{error_case[:size]} bytes test file"
|
82
|
+
end
|
83
|
+
message << " whereas it should have passed"
|
84
|
+
end
|
85
|
+
|
86
|
+
def not_lower_than_min?
|
87
|
+
@min.nil? || !passes_validation_with_size(@min - 1)
|
88
|
+
end
|
89
|
+
|
90
|
+
def higher_than_min?
|
91
|
+
@min.nil? || passes_validation_with_size(@min + 1)
|
92
|
+
end
|
93
|
+
|
94
|
+
def lower_than_max?
|
95
|
+
@max.nil? || @max == Float::INFINITY || passes_validation_with_size(@max - 1)
|
96
|
+
end
|
97
|
+
|
98
|
+
def not_higher_than_max?
|
99
|
+
@max.nil? || @max == Float::INFINITY || !passes_validation_with_size(@max + 1)
|
100
|
+
end
|
101
|
+
|
102
|
+
def passes_validation_with_size(size)
|
103
|
+
mock_size_for(io, size) do
|
104
|
+
attach_file
|
105
|
+
validate
|
106
|
+
detach_file
|
107
|
+
is_valid? || add_failure_message_artefact(size)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def add_failure_message_artefact(size)
|
112
|
+
@failure_message_artefacts << { size: size }
|
113
|
+
false
|
114
|
+
end
|
115
|
+
|
116
|
+
def is_custom_message_valid?
|
117
|
+
return true unless @custom_message
|
118
|
+
|
119
|
+
mock_size_for(io, -1.kilobytes) do
|
120
|
+
attach_file
|
121
|
+
validate
|
122
|
+
detach_file
|
123
|
+
has_an_error_message_which_is_custom_message?
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def mock_size_for(io, size)
|
128
|
+
Matchers.stub_method(io, :size, size) do
|
129
|
+
yield
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module ActiveStorageValidations
|
2
|
+
module Matchers
|
3
|
+
module Attachable
|
4
|
+
private
|
5
|
+
|
6
|
+
def attach_file(file = dummy_file)
|
7
|
+
@subject.public_send(@attribute_name).attach(file)
|
8
|
+
@subject.public_send(@attribute_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def dummy_file
|
12
|
+
{
|
13
|
+
io: io,
|
14
|
+
filename: 'test.png',
|
15
|
+
content_type: 'image/png'
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def processable_image
|
20
|
+
{
|
21
|
+
io: File.open(Rails.root.join('public', 'image_1920x1080.png')),
|
22
|
+
filename: 'image_1920x1080_file.png',
|
23
|
+
content_type: 'image/png'
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def not_processable_image
|
28
|
+
{
|
29
|
+
io: Tempfile.new('.'),
|
30
|
+
filename: 'processable.txt',
|
31
|
+
content_type: 'text/plain'
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def io
|
36
|
+
@io ||= Tempfile.new('Hello world!')
|
37
|
+
end
|
38
|
+
|
39
|
+
def detach_file
|
40
|
+
@subject.attachment_changes.delete(@attribute_name.to_s)
|
41
|
+
end
|
42
|
+
|
43
|
+
def file_attached?
|
44
|
+
@subject.public_send(@attribute_name).attached?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -17,19 +17,31 @@ module ActiveStorageValidations
|
|
17
17
|
private
|
18
18
|
|
19
19
|
def is_context_valid?
|
20
|
-
return true if !@context &&
|
20
|
+
return true if !@context && attribute_validators.none? { |validator| validator.options[:on] }
|
21
21
|
|
22
|
-
raise ArgumentError, "This validator matcher needs the #on option to work since its validator has one" if !@context
|
22
|
+
raise ArgumentError, "This validator matcher needs the #on option to work since its validator has one" if !@context && attribute_validators.all? { |validator| validator.options[:on] }
|
23
23
|
raise ArgumentError, "This validator matcher option only allows a symbol or an array" if !(@context.is_a?(Symbol) || @context.is_a?(Array))
|
24
24
|
|
25
|
-
if @context.is_a?(Array)
|
26
|
-
@context.
|
27
|
-
elsif @context.is_a?(Symbol)
|
28
|
-
@context
|
29
|
-
else
|
30
|
-
false
|
25
|
+
if @context.is_a?(Array)
|
26
|
+
(validator_contexts & @context.map(&:to_s)) == validator_contexts || raise_context_not_listed_error
|
27
|
+
elsif @context.is_a?(Symbol)
|
28
|
+
validator_contexts.include?(@context.to_s) || raise_context_not_listed_error
|
31
29
|
end
|
32
30
|
end
|
31
|
+
|
32
|
+
def validator_contexts
|
33
|
+
attribute_validators.map do |validator|
|
34
|
+
case validator.options[:on]
|
35
|
+
when Array then validator.options[:on].map { |context| context.to_s }
|
36
|
+
when NilClass then nil
|
37
|
+
else validator.options[:on].to_s
|
38
|
+
end
|
39
|
+
end.flatten.compact
|
40
|
+
end
|
41
|
+
|
42
|
+
def raise_context_not_listed_error
|
43
|
+
raise ArgumentError, "One of the provided contexts to the #on method is not found in any of the listed contexts for this attribute"
|
44
|
+
end
|
33
45
|
end
|
34
46
|
end
|
35
47
|
end
|
@@ -26,7 +26,7 @@ module ActiveStorageValidations
|
|
26
26
|
def available_errors
|
27
27
|
[
|
28
28
|
*validator_class::ERROR_TYPES,
|
29
|
-
*
|
29
|
+
*errors_from_custom_messages
|
30
30
|
].compact
|
31
31
|
end
|
32
32
|
|
@@ -40,8 +40,14 @@ module ActiveStorageValidations
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
def
|
44
|
-
|
43
|
+
def attribute_validators
|
44
|
+
@subject.class.validators_on(@attribute_name).select do |validator|
|
45
|
+
validator.class == validator_class
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def errors_from_custom_messages
|
50
|
+
attribute_validators.map { |validator| validator.options[:message] }
|
45
51
|
end
|
46
52
|
end
|
47
53
|
end
|
@@ -5,6 +5,7 @@
|
|
5
5
|
|
6
6
|
require_relative 'concerns/active_storageable.rb'
|
7
7
|
require_relative 'concerns/allow_blankable.rb'
|
8
|
+
require_relative 'concerns/attachable.rb'
|
8
9
|
require_relative 'concerns/contextable.rb'
|
9
10
|
require_relative 'concerns/messageable.rb'
|
10
11
|
require_relative 'concerns/rspecable.rb'
|
@@ -12,13 +13,14 @@ require_relative 'concerns/validatable.rb'
|
|
12
13
|
|
13
14
|
module ActiveStorageValidations
|
14
15
|
module Matchers
|
15
|
-
def validate_content_type_of(
|
16
|
-
ContentTypeValidatorMatcher.new(
|
16
|
+
def validate_content_type_of(attribute_name)
|
17
|
+
ContentTypeValidatorMatcher.new(attribute_name)
|
17
18
|
end
|
18
19
|
|
19
20
|
class ContentTypeValidatorMatcher
|
20
21
|
include ActiveStorageable
|
21
22
|
include AllowBlankable
|
23
|
+
include Attachable
|
22
24
|
include Contextable
|
23
25
|
include Messageable
|
24
26
|
include Rspecable
|
@@ -99,6 +101,7 @@ module ActiveStorageValidations
|
|
99
101
|
def type_allowed?(type)
|
100
102
|
attach_file_of_type(type)
|
101
103
|
validate
|
104
|
+
detach_file
|
102
105
|
is_valid?
|
103
106
|
end
|
104
107
|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative 'concerns/active_storageable.rb'
|
4
4
|
require_relative 'concerns/allow_blankable.rb'
|
5
|
+
require_relative 'concerns/attachable'
|
5
6
|
require_relative 'concerns/contextable.rb'
|
6
7
|
require_relative 'concerns/messageable.rb'
|
7
8
|
require_relative 'concerns/rspecable.rb'
|
@@ -9,13 +10,14 @@ require_relative 'concerns/validatable.rb'
|
|
9
10
|
|
10
11
|
module ActiveStorageValidations
|
11
12
|
module Matchers
|
12
|
-
def validate_dimensions_of(
|
13
|
-
DimensionValidatorMatcher.new(
|
13
|
+
def validate_dimensions_of(attribute_name)
|
14
|
+
DimensionValidatorMatcher.new(attribute_name)
|
14
15
|
end
|
15
16
|
|
16
17
|
class DimensionValidatorMatcher
|
17
18
|
include ActiveStorageable
|
18
19
|
include AllowBlankable
|
20
|
+
include Attachable
|
19
21
|
include Contextable
|
20
22
|
include Messageable
|
21
23
|
include Rspecable
|
@@ -162,6 +164,7 @@ module ActiveStorageValidations
|
|
162
164
|
def passes_validation_with_dimensions(width, height)
|
163
165
|
mock_dimensions_for(attach_file, width, height) do
|
164
166
|
validate
|
167
|
+
detach_file
|
165
168
|
is_valid? || add_failure_message_artefact(width, height)
|
166
169
|
end
|
167
170
|
end
|
@@ -176,6 +179,7 @@ module ActiveStorageValidations
|
|
176
179
|
|
177
180
|
mock_dimensions_for(attach_file, -1, -1) do
|
178
181
|
validate
|
182
|
+
detach_file
|
179
183
|
has_an_error_message_which_is_custom_message?
|
180
184
|
end
|
181
185
|
end
|
@@ -185,19 +189,6 @@ module ActiveStorageValidations
|
|
185
189
|
yield
|
186
190
|
end
|
187
191
|
end
|
188
|
-
|
189
|
-
def attach_file
|
190
|
-
@subject.public_send(@attribute_name).attach(dummy_file)
|
191
|
-
@subject.public_send(@attribute_name)
|
192
|
-
end
|
193
|
-
|
194
|
-
def dummy_file
|
195
|
-
{
|
196
|
-
io: Tempfile.new('Hello world!'),
|
197
|
-
filename: 'test.png',
|
198
|
-
content_type: 'image/png'
|
199
|
-
}
|
200
|
-
end
|
201
192
|
end
|
202
193
|
end
|
203
194
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'concerns/active_storageable.rb'
|
4
|
+
require_relative 'concerns/allow_blankable.rb'
|
5
|
+
require_relative 'concerns/attachable.rb'
|
6
|
+
require_relative 'concerns/contextable.rb'
|
7
|
+
require_relative 'concerns/messageable.rb'
|
8
|
+
require_relative 'concerns/rspecable.rb'
|
9
|
+
require_relative 'concerns/validatable.rb'
|
10
|
+
|
11
|
+
module ActiveStorageValidations
|
12
|
+
module Matchers
|
13
|
+
def validate_processable_image_of(name)
|
14
|
+
ProcessableImageValidatorMatcher.new(name)
|
15
|
+
end
|
16
|
+
|
17
|
+
class ProcessableImageValidatorMatcher
|
18
|
+
include ActiveStorageable
|
19
|
+
include AllowBlankable
|
20
|
+
include Attachable
|
21
|
+
include Contextable
|
22
|
+
include Messageable
|
23
|
+
include Rspecable
|
24
|
+
include Validatable
|
25
|
+
|
26
|
+
def initialize(attribute_name)
|
27
|
+
initialize_allow_blankable
|
28
|
+
initialize_contextable
|
29
|
+
initialize_messageable
|
30
|
+
initialize_rspecable
|
31
|
+
@attribute_name = attribute_name
|
32
|
+
end
|
33
|
+
|
34
|
+
def description
|
35
|
+
"validate that :#{@attribute_name} is a processable image"
|
36
|
+
end
|
37
|
+
|
38
|
+
def failure_message
|
39
|
+
"is expected to validate the processable image of :#{@attribute_name}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def matches?(subject)
|
43
|
+
@subject = subject.is_a?(Class) ? subject.new : subject
|
44
|
+
|
45
|
+
is_a_valid_active_storage_attribute? &&
|
46
|
+
is_context_valid? &&
|
47
|
+
is_custom_message_valid? &&
|
48
|
+
is_valid_when_image_processable? &&
|
49
|
+
is_invalid_when_image_not_processable?
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def is_valid_when_image_processable?
|
55
|
+
attach_file(processable_image)
|
56
|
+
validate
|
57
|
+
detach_file
|
58
|
+
is_valid?
|
59
|
+
end
|
60
|
+
|
61
|
+
def is_invalid_when_image_not_processable?
|
62
|
+
attach_file(not_processable_image)
|
63
|
+
validate
|
64
|
+
detach_file
|
65
|
+
!is_valid?
|
66
|
+
end
|
67
|
+
|
68
|
+
def is_custom_message_valid?
|
69
|
+
return true unless @custom_message
|
70
|
+
|
71
|
+
attach_file(not_processable_image)
|
72
|
+
validate
|
73
|
+
detach_file
|
74
|
+
has_an_error_message_which_is_custom_message?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -1,38 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
# https://github.com/thoughtbot/paperclip/blob/v6.1.0/lib/paperclip/matchers/validate_attachment_size_matcher.rb
|
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'
|
11
|
-
require_relative 'concerns/validatable.rb'
|
3
|
+
require_relative 'base_size_validator_matcher'
|
12
4
|
|
13
5
|
module ActiveStorageValidations
|
14
6
|
module Matchers
|
15
|
-
def validate_size_of(
|
16
|
-
SizeValidatorMatcher.new(
|
7
|
+
def validate_size_of(attribute_name)
|
8
|
+
SizeValidatorMatcher.new(attribute_name)
|
17
9
|
end
|
18
10
|
|
19
|
-
class SizeValidatorMatcher
|
20
|
-
include ActiveStorageable
|
21
|
-
include AllowBlankable
|
22
|
-
include Contextable
|
23
|
-
include Messageable
|
24
|
-
include Rspecable
|
25
|
-
include Validatable
|
26
|
-
|
27
|
-
def initialize(attribute_name)
|
28
|
-
initialize_allow_blankable
|
29
|
-
initialize_contextable
|
30
|
-
initialize_messageable
|
31
|
-
initialize_rspecable
|
32
|
-
@attribute_name = attribute_name
|
33
|
-
@min = @max = nil
|
34
|
-
end
|
35
|
-
|
11
|
+
class SizeValidatorMatcher < BaseSizeValidatorMatcher
|
36
12
|
def description
|
37
13
|
"validate file size of :#{@attribute_name}"
|
38
14
|
end
|
@@ -42,117 +18,6 @@ module ActiveStorageValidations
|
|
42
18
|
build_failure_message(message)
|
43
19
|
message.join("\n")
|
44
20
|
end
|
45
|
-
|
46
|
-
def less_than(size)
|
47
|
-
@max = size - 1.byte
|
48
|
-
self
|
49
|
-
end
|
50
|
-
|
51
|
-
def less_than_or_equal_to(size)
|
52
|
-
@max = size
|
53
|
-
self
|
54
|
-
end
|
55
|
-
|
56
|
-
def greater_than(size)
|
57
|
-
@min = size + 1.byte
|
58
|
-
self
|
59
|
-
end
|
60
|
-
|
61
|
-
def greater_than_or_equal_to(size)
|
62
|
-
@min = size
|
63
|
-
self
|
64
|
-
end
|
65
|
-
|
66
|
-
def between(range)
|
67
|
-
@min, @max = range.first, range.last
|
68
|
-
self
|
69
|
-
end
|
70
|
-
|
71
|
-
def matches?(subject)
|
72
|
-
@subject = subject.is_a?(Class) ? subject.new : subject
|
73
|
-
|
74
|
-
is_a_valid_active_storage_attribute? &&
|
75
|
-
is_context_valid? &&
|
76
|
-
is_allowing_blank? &&
|
77
|
-
is_custom_message_valid? &&
|
78
|
-
not_lower_than_min? &&
|
79
|
-
higher_than_min? &&
|
80
|
-
lower_than_max? &&
|
81
|
-
not_higher_than_max?
|
82
|
-
end
|
83
|
-
|
84
|
-
protected
|
85
|
-
|
86
|
-
def build_failure_message(message)
|
87
|
-
return unless @failure_message_artefacts.present?
|
88
|
-
|
89
|
-
message << " but there seem to have issues with the matcher methods you used, since:"
|
90
|
-
@failure_message_artefacts.each do |error_case|
|
91
|
-
message << " validation failed when provided with a #{error_case[:size]} bytes test file"
|
92
|
-
end
|
93
|
-
message << " whereas it should have passed"
|
94
|
-
end
|
95
|
-
|
96
|
-
def not_lower_than_min?
|
97
|
-
@min.nil? || !passes_validation_with_size(@min - 1)
|
98
|
-
end
|
99
|
-
|
100
|
-
def higher_than_min?
|
101
|
-
@min.nil? || passes_validation_with_size(@min + 1)
|
102
|
-
end
|
103
|
-
|
104
|
-
def lower_than_max?
|
105
|
-
@max.nil? || @max == Float::INFINITY || passes_validation_with_size(@max - 1)
|
106
|
-
end
|
107
|
-
|
108
|
-
def not_higher_than_max?
|
109
|
-
@max.nil? || @max == Float::INFINITY || !passes_validation_with_size(@max + 1)
|
110
|
-
end
|
111
|
-
|
112
|
-
def passes_validation_with_size(size)
|
113
|
-
mock_size_for(io, size) do
|
114
|
-
attach_file
|
115
|
-
validate
|
116
|
-
is_valid? || add_failure_message_artefact(size)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def add_failure_message_artefact(size)
|
121
|
-
@failure_message_artefacts << { size: size }
|
122
|
-
false
|
123
|
-
end
|
124
|
-
|
125
|
-
def is_custom_message_valid?
|
126
|
-
return true unless @custom_message
|
127
|
-
|
128
|
-
mock_size_for(io, -1.kilobytes) do
|
129
|
-
attach_file
|
130
|
-
validate
|
131
|
-
has_an_error_message_which_is_custom_message?
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def mock_size_for(io, size)
|
136
|
-
Matchers.stub_method(io, :size, size) do
|
137
|
-
yield
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def attach_file
|
142
|
-
@subject.public_send(@attribute_name).attach(dummy_file)
|
143
|
-
end
|
144
|
-
|
145
|
-
def dummy_file
|
146
|
-
{
|
147
|
-
io: io,
|
148
|
-
filename: 'test.png',
|
149
|
-
content_type: 'image/png'
|
150
|
-
}
|
151
|
-
end
|
152
|
-
|
153
|
-
def io
|
154
|
-
@io ||= Tempfile.new('Hello world!')
|
155
|
-
end
|
156
21
|
end
|
157
22
|
end
|
158
23
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_size_validator_matcher'
|
4
|
+
|
5
|
+
module ActiveStorageValidations
|
6
|
+
module Matchers
|
7
|
+
def validate_total_size_of(attribute_name)
|
8
|
+
TotalSizeValidatorMatcher.new(attribute_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
class TotalSizeValidatorMatcher < BaseSizeValidatorMatcher
|
12
|
+
def description
|
13
|
+
"validate total file size of :#{@attribute_name}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def failure_message
|
17
|
+
message = ["is expected to validate total file size of :#{@attribute_name}"]
|
18
|
+
build_failure_message(message)
|
19
|
+
message.join("\n")
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def attach_file
|
25
|
+
# We attach blobs instead of io for has_many_attached relation
|
26
|
+
@subject.public_send(@attribute_name).attach([dummy_blob])
|
27
|
+
@subject.public_send(@attribute_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def dummy_blob
|
31
|
+
ActiveStorage::Blob.create_and_upload!(
|
32
|
+
io: io,
|
33
|
+
filename: 'test.png',
|
34
|
+
content_type: 'image/png',
|
35
|
+
service_name: 'test'
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -2,9 +2,11 @@
|
|
2
2
|
|
3
3
|
require 'active_storage_validations/matchers/aspect_ratio_validator_matcher'
|
4
4
|
require 'active_storage_validations/matchers/attached_validator_matcher'
|
5
|
+
require 'active_storage_validations/matchers/processable_image_validator_matcher'
|
5
6
|
require 'active_storage_validations/matchers/content_type_validator_matcher'
|
6
7
|
require 'active_storage_validations/matchers/dimension_validator_matcher'
|
7
8
|
require 'active_storage_validations/matchers/size_validator_matcher'
|
9
|
+
require 'active_storage_validations/matchers/total_size_validator_matcher'
|
8
10
|
|
9
11
|
module ActiveStorageValidations
|
10
12
|
module Matchers
|