active_storage_validations 0.9.7 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +737 -229
- data/config/locales/da.yml +53 -0
- data/config/locales/de.yml +50 -19
- data/config/locales/en.yml +50 -19
- data/config/locales/es.yml +50 -19
- data/config/locales/fr.yml +50 -19
- data/config/locales/it.yml +50 -19
- data/config/locales/ja.yml +50 -19
- data/config/locales/nl.yml +50 -19
- data/config/locales/pl.yml +50 -19
- data/config/locales/pt-BR.yml +50 -19
- data/config/locales/ru.yml +50 -19
- data/config/locales/sv.yml +53 -0
- data/config/locales/tr.yml +50 -19
- data/config/locales/uk.yml +50 -19
- data/config/locales/vi.yml +50 -19
- data/config/locales/zh-CN.yml +53 -0
- data/lib/active_storage_validations/analyzer/audio_analyzer.rb +58 -0
- data/lib/active_storage_validations/analyzer/content_type_analyzer.rb +60 -0
- data/lib/active_storage_validations/analyzer/image_analyzer/image_magick.rb +47 -0
- data/lib/active_storage_validations/analyzer/image_analyzer/vips.rb +57 -0
- data/lib/active_storage_validations/analyzer/image_analyzer.rb +49 -0
- data/lib/active_storage_validations/analyzer/null_analyzer.rb +18 -0
- data/lib/active_storage_validations/analyzer/shared/asv_ff_probable.rb +61 -0
- data/lib/active_storage_validations/analyzer/video_analyzer.rb +130 -0
- data/lib/active_storage_validations/analyzer.rb +87 -0
- data/lib/active_storage_validations/aspect_ratio_validator.rb +154 -99
- data/lib/active_storage_validations/attached_validator.rb +22 -5
- data/lib/active_storage_validations/base_comparison_validator.rb +71 -0
- data/lib/active_storage_validations/content_type_validator.rb +206 -25
- data/lib/active_storage_validations/dimension_validator.rb +105 -82
- data/lib/active_storage_validations/duration_validator.rb +55 -0
- data/lib/active_storage_validations/extensors/asv_blob_metadatable.rb +49 -0
- data/lib/active_storage_validations/extensors/asv_marcelable.rb +12 -0
- data/lib/active_storage_validations/limit_validator.rb +75 -16
- data/lib/active_storage_validations/matchers/aspect_ratio_validator_matcher.rb +119 -0
- data/lib/active_storage_validations/matchers/attached_validator_matcher.rb +48 -25
- data/lib/active_storage_validations/matchers/base_comparison_validator_matcher.rb +140 -0
- data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +94 -59
- data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +97 -55
- data/lib/active_storage_validations/matchers/duration_validator_matcher.rb +39 -0
- data/lib/active_storage_validations/matchers/limit_validator_matcher.rb +127 -0
- data/lib/active_storage_validations/matchers/processable_file_validator_matcher.rb +78 -0
- data/lib/active_storage_validations/matchers/shared/asv_active_storageable.rb +19 -0
- data/lib/active_storage_validations/matchers/shared/asv_allow_blankable.rb +28 -0
- data/lib/active_storage_validations/matchers/shared/asv_attachable.rb +72 -0
- data/lib/active_storage_validations/matchers/shared/asv_contextable.rb +49 -0
- data/lib/active_storage_validations/matchers/shared/asv_messageable.rb +28 -0
- data/lib/active_storage_validations/matchers/shared/asv_rspecable.rb +27 -0
- data/lib/active_storage_validations/matchers/shared/asv_validatable.rb +56 -0
- data/lib/active_storage_validations/matchers/size_validator_matcher.rb +17 -71
- data/lib/active_storage_validations/matchers/total_size_validator_matcher.rb +47 -0
- data/lib/active_storage_validations/matchers.rb +11 -16
- data/lib/active_storage_validations/processable_file_validator.rb +37 -0
- data/lib/active_storage_validations/railtie.rb +11 -0
- data/lib/active_storage_validations/shared/asv_active_storageable.rb +30 -0
- data/lib/active_storage_validations/shared/asv_analyzable.rb +80 -0
- data/lib/active_storage_validations/shared/asv_attachable.rb +204 -0
- data/lib/active_storage_validations/shared/asv_errorable.rb +40 -0
- data/lib/active_storage_validations/shared/asv_loggable.rb +11 -0
- data/lib/active_storage_validations/shared/asv_optionable.rb +29 -0
- data/lib/active_storage_validations/shared/asv_symbolizable.rb +14 -0
- data/lib/active_storage_validations/size_validator.rb +24 -40
- data/lib/active_storage_validations/total_size_validator.rb +51 -0
- data/lib/active_storage_validations/version.rb +1 -1
- data/lib/active_storage_validations.rb +20 -6
- metadata +127 -21
- data/lib/active_storage_validations/metadata.rb +0 -123
@@ -1,20 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'shared/asv_active_storageable'
|
4
|
+
require_relative 'shared/asv_allow_blankable'
|
5
|
+
require_relative 'shared/asv_attachable'
|
6
|
+
require_relative 'shared/asv_contextable'
|
7
|
+
require_relative 'shared/asv_messageable'
|
8
|
+
require_relative 'shared/asv_rspecable'
|
9
|
+
require_relative 'shared/asv_validatable'
|
10
|
+
|
3
11
|
module ActiveStorageValidations
|
4
12
|
module Matchers
|
5
|
-
def validate_dimensions_of(
|
6
|
-
DimensionValidatorMatcher.new(
|
13
|
+
def validate_dimensions_of(attribute_name)
|
14
|
+
DimensionValidatorMatcher.new(attribute_name)
|
7
15
|
end
|
8
16
|
|
9
17
|
class DimensionValidatorMatcher
|
18
|
+
include ASVActiveStorageable
|
19
|
+
include ASVAllowBlankable
|
20
|
+
include ASVAttachable
|
21
|
+
include ASVContextable
|
22
|
+
include ASVMessageable
|
23
|
+
include ASVRspecable
|
24
|
+
include ASVValidatable
|
25
|
+
|
10
26
|
def initialize(attribute_name)
|
27
|
+
initialize_allow_blankable
|
28
|
+
initialize_contextable
|
29
|
+
initialize_messageable
|
30
|
+
initialize_rspecable
|
11
31
|
@attribute_name = attribute_name
|
12
32
|
@width_min = @width_max = @height_min = @height_max = nil
|
13
|
-
@custom_message = nil
|
14
33
|
end
|
15
34
|
|
16
35
|
def description
|
17
|
-
"validate image dimensions of
|
36
|
+
"validate the image dimensions of :#{@attribute_name}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def failure_message
|
40
|
+
message = ["is expected to validate dimensions of :#{@attribute_name}"]
|
41
|
+
build_failure_message(message)
|
42
|
+
message.join("\n")
|
43
|
+
end
|
44
|
+
|
45
|
+
def width(width)
|
46
|
+
@width_min = @width_max = width
|
47
|
+
self
|
18
48
|
end
|
19
49
|
|
20
50
|
def width_min(width)
|
@@ -27,13 +57,13 @@ module ActiveStorageValidations
|
|
27
57
|
self
|
28
58
|
end
|
29
59
|
|
30
|
-
def
|
31
|
-
@
|
60
|
+
def width_between(range)
|
61
|
+
@width_min, @width_max = range.first, range.last
|
32
62
|
self
|
33
63
|
end
|
34
64
|
|
35
|
-
def
|
36
|
-
@
|
65
|
+
def height(height)
|
66
|
+
@height_min = @height_max = height
|
37
67
|
self
|
38
68
|
end
|
39
69
|
|
@@ -47,42 +77,40 @@ module ActiveStorageValidations
|
|
47
77
|
self
|
48
78
|
end
|
49
79
|
|
50
|
-
def width_between(range)
|
51
|
-
@width_min, @width_max = range.first, range.last
|
52
|
-
self
|
53
|
-
end
|
54
|
-
|
55
80
|
def height_between(range)
|
56
81
|
@height_min, @height_max = range.first, range.last
|
57
82
|
self
|
58
83
|
end
|
59
84
|
|
60
|
-
def height(height)
|
61
|
-
@height_min = @height_max = height
|
62
|
-
self
|
63
|
-
end
|
64
|
-
|
65
85
|
def matches?(subject)
|
66
86
|
@subject = subject.is_a?(Class) ? subject.new : subject
|
67
|
-
responds_to_methods &&
|
68
|
-
width_smaller_than_min? && width_larger_than_min? && width_smaller_than_max? && width_larger_than_max? && width_equals? &&
|
69
|
-
height_smaller_than_min? && height_larger_than_min? && height_smaller_than_max? && height_larger_than_max? && height_equals?
|
70
|
-
end
|
71
87
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
88
|
+
is_a_valid_active_storage_attribute? &&
|
89
|
+
is_context_valid? &&
|
90
|
+
is_allowing_blank? &&
|
91
|
+
is_custom_message_valid? &&
|
92
|
+
width_not_smaller_than_min? &&
|
93
|
+
width_larger_than_min? &&
|
94
|
+
width_smaller_than_max? &&
|
95
|
+
width_not_larger_than_max? &&
|
96
|
+
width_equals? &&
|
97
|
+
height_not_smaller_than_min? &&
|
98
|
+
height_larger_than_min? &&
|
99
|
+
height_smaller_than_max? &&
|
100
|
+
height_not_larger_than_max? &&
|
101
|
+
height_equals?
|
78
102
|
end
|
79
103
|
|
80
104
|
protected
|
81
105
|
|
82
|
-
def
|
83
|
-
@
|
84
|
-
|
85
|
-
|
106
|
+
def build_failure_message(message)
|
107
|
+
return unless @failure_message_artefacts.present?
|
108
|
+
|
109
|
+
message << " but there seem to have issues with the matcher methods you used, since:"
|
110
|
+
@failure_message_artefacts.each do |error_case|
|
111
|
+
message << " validation failed when provided with a #{error_case[:width]}x#{error_case[:height]}px test image"
|
112
|
+
end
|
113
|
+
message << " whereas it should have passed"
|
86
114
|
end
|
87
115
|
|
88
116
|
def valid_width
|
@@ -93,59 +121,73 @@ module ActiveStorageValidations
|
|
93
121
|
((@height_min || 0) + (@height_max || 2000)) / 2
|
94
122
|
end
|
95
123
|
|
96
|
-
def
|
97
|
-
@width_min.nil? || !passes_validation_with_dimensions(@width_min - 1, valid_height
|
124
|
+
def width_not_smaller_than_min?
|
125
|
+
@width_min.nil? || !passes_validation_with_dimensions(@width_min - 1, valid_height)
|
98
126
|
end
|
99
127
|
|
100
128
|
def width_larger_than_min?
|
101
|
-
@width_min.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_min + 1, valid_height
|
129
|
+
@width_min.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_min + 1, valid_height)
|
102
130
|
end
|
103
131
|
|
104
132
|
def width_smaller_than_max?
|
105
|
-
@width_max.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_max - 1, valid_height
|
133
|
+
@width_max.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_max - 1, valid_height)
|
106
134
|
end
|
107
135
|
|
108
|
-
def
|
109
|
-
@width_max.nil? || !passes_validation_with_dimensions(@width_max + 1, valid_height
|
136
|
+
def width_not_larger_than_max?
|
137
|
+
@width_max.nil? || !passes_validation_with_dimensions(@width_max + 1, valid_height)
|
110
138
|
end
|
111
139
|
|
112
140
|
def width_equals?
|
113
|
-
@width_min.nil? || @width_min != @width_max || passes_validation_with_dimensions(@width_min, valid_height
|
141
|
+
@width_min.nil? || @width_min != @width_max || passes_validation_with_dimensions(@width_min, valid_height)
|
114
142
|
end
|
115
143
|
|
116
|
-
def
|
117
|
-
@height_min.nil? || !passes_validation_with_dimensions(valid_width, @height_min - 1
|
144
|
+
def height_not_smaller_than_min?
|
145
|
+
@height_min.nil? || !passes_validation_with_dimensions(valid_width, @height_min - 1)
|
118
146
|
end
|
119
147
|
|
120
148
|
def height_larger_than_min?
|
121
|
-
@height_min.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_min + 1
|
149
|
+
@height_min.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_min + 1)
|
122
150
|
end
|
123
151
|
|
124
152
|
def height_smaller_than_max?
|
125
|
-
@height_max.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_max - 1
|
153
|
+
@height_max.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_max - 1)
|
126
154
|
end
|
127
155
|
|
128
|
-
def
|
129
|
-
@height_max.nil? || !passes_validation_with_dimensions(valid_width, @height_max + 1
|
156
|
+
def height_not_larger_than_max?
|
157
|
+
@height_max.nil? || !passes_validation_with_dimensions(valid_width, @height_max + 1)
|
130
158
|
end
|
131
159
|
|
132
160
|
def height_equals?
|
133
|
-
@height_min.nil? || @height_min != @height_max || passes_validation_with_dimensions(valid_width, @height_min
|
161
|
+
@height_min.nil? || @height_min != @height_max || passes_validation_with_dimensions(valid_width, @height_min)
|
134
162
|
end
|
135
163
|
|
136
|
-
def passes_validation_with_dimensions(width, height
|
137
|
-
|
164
|
+
def passes_validation_with_dimensions(width, height)
|
165
|
+
mock_dimensions_for(attach_file, width, height) do
|
166
|
+
validate
|
167
|
+
detach_file
|
168
|
+
is_valid? || add_failure_message_artefact(width, height)
|
169
|
+
end
|
170
|
+
end
|
138
171
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
172
|
+
def add_failure_message_artefact(width, height)
|
173
|
+
@failure_message_artefacts << { width: width, height: height }
|
174
|
+
false
|
175
|
+
end
|
176
|
+
|
177
|
+
def is_custom_message_valid?
|
178
|
+
return true unless @custom_message
|
179
|
+
|
180
|
+
mock_dimensions_for(attach_file, -1, -1) do
|
181
|
+
validate
|
182
|
+
detach_file
|
183
|
+
has_an_error_message_which_is_custom_message?
|
144
184
|
end
|
145
185
|
end
|
146
186
|
|
147
|
-
def
|
148
|
-
|
187
|
+
def mock_dimensions_for(attachment, width, height)
|
188
|
+
Matchers.mock_metadata(attachment, { width: width, height: height }) do
|
189
|
+
yield
|
190
|
+
end
|
149
191
|
end
|
150
192
|
end
|
151
193
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_comparison_validator_matcher'
|
4
|
+
|
5
|
+
module ActiveStorageValidations
|
6
|
+
module Matchers
|
7
|
+
def validate_duration_of(attribute_name)
|
8
|
+
DurationValidatorMatcher.new(attribute_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
class DurationValidatorMatcher < BaseComparisonValidatorMatcher
|
12
|
+
def description
|
13
|
+
"validate file duration of :#{@attribute_name}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def failure_message
|
17
|
+
message = ["is expected to validate file duration of :#{@attribute_name}"]
|
18
|
+
build_failure_message(message)
|
19
|
+
message.join("\n")
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def failure_message_unit
|
25
|
+
"seconds"
|
26
|
+
end
|
27
|
+
|
28
|
+
def smallest_measurement
|
29
|
+
1.second
|
30
|
+
end
|
31
|
+
|
32
|
+
def mock_value_for(io, duration)
|
33
|
+
Matchers.mock_metadata(io, { duration: duration }) do
|
34
|
+
yield
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'shared/asv_active_storageable'
|
4
|
+
require_relative 'shared/asv_allow_blankable'
|
5
|
+
require_relative 'shared/asv_attachable'
|
6
|
+
require_relative 'shared/asv_contextable'
|
7
|
+
require_relative 'shared/asv_messageable'
|
8
|
+
require_relative 'shared/asv_rspecable'
|
9
|
+
require_relative 'shared/asv_validatable'
|
10
|
+
|
11
|
+
module ActiveStorageValidations
|
12
|
+
module Matchers
|
13
|
+
def validate_limits_of(name)
|
14
|
+
LimitValidatorMatcher.new(name)
|
15
|
+
end
|
16
|
+
|
17
|
+
class LimitValidatorMatcher
|
18
|
+
include ASVActiveStorageable
|
19
|
+
include ASVAllowBlankable
|
20
|
+
include ASVAttachable
|
21
|
+
include ASVContextable
|
22
|
+
include ASVMessageable
|
23
|
+
include ASVRspecable
|
24
|
+
include ASVValidatable
|
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
|
+
@min = @max = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def description
|
36
|
+
"validate the limit files of :#{@attribute_name}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def failure_message
|
40
|
+
message = ["is expected to validate limit file of :#{@attribute_name}"]
|
41
|
+
build_failure_message(message)
|
42
|
+
message.join("\n")
|
43
|
+
end
|
44
|
+
|
45
|
+
def min(count)
|
46
|
+
@min = count
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def max(count)
|
51
|
+
@max = count
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def matches?(subject)
|
56
|
+
@subject = subject.is_a?(Class) ? subject.new : subject
|
57
|
+
|
58
|
+
is_a_valid_active_storage_attribute? &&
|
59
|
+
is_context_valid? &&
|
60
|
+
is_custom_message_valid? &&
|
61
|
+
file_count_not_smaller_than_min? &&
|
62
|
+
file_count_equal_min? &&
|
63
|
+
file_count_larger_than_min? &&
|
64
|
+
file_count_smaller_than_max? &&
|
65
|
+
file_count_equal_max? &&
|
66
|
+
file_count_not_larger_than_max?
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def build_failure_message(message)
|
72
|
+
return unless @failure_message_artefacts.present?
|
73
|
+
|
74
|
+
message << " but there seem to have issues with the matcher methods you used, since:"
|
75
|
+
@failure_message_artefacts.each do |error_case|
|
76
|
+
message << " validation failed when provided with #{error_case[:count]} file(s)"
|
77
|
+
end
|
78
|
+
message << " whereas it should have passed"
|
79
|
+
end
|
80
|
+
|
81
|
+
def file_count_not_smaller_than_min?
|
82
|
+
@min.nil? || @min.zero? || !passes_validation_with_limits(@min - 1)
|
83
|
+
end
|
84
|
+
|
85
|
+
def file_count_equal_min?
|
86
|
+
@min.nil? || @min.zero? || passes_validation_with_limits(@min)
|
87
|
+
end
|
88
|
+
|
89
|
+
def file_count_larger_than_min?
|
90
|
+
@min.nil? || @min.zero? || @min == @max || passes_validation_with_limits(@min + 1)
|
91
|
+
end
|
92
|
+
|
93
|
+
def file_count_smaller_than_max?
|
94
|
+
@max.nil? || @min == @max || passes_validation_with_limits(@max - 1)
|
95
|
+
end
|
96
|
+
|
97
|
+
def file_count_equal_max?
|
98
|
+
@max.nil? || passes_validation_with_limits(@max)
|
99
|
+
end
|
100
|
+
|
101
|
+
def file_count_not_larger_than_max?
|
102
|
+
@max.nil? || !passes_validation_with_limits(@max + 1)
|
103
|
+
end
|
104
|
+
|
105
|
+
def passes_validation_with_limits(count)
|
106
|
+
attach_files(count)
|
107
|
+
validate
|
108
|
+
detach_files
|
109
|
+
is_valid? || add_failure_message_artefact(count)
|
110
|
+
end
|
111
|
+
|
112
|
+
def is_custom_message_valid?
|
113
|
+
return true if !@custom_message || (@min&.zero? && @max.nil?)
|
114
|
+
|
115
|
+
@min.nil? ? attach_files(@max + 1) : attach_files(@min - 1)
|
116
|
+
validate
|
117
|
+
detach_files
|
118
|
+
has_an_error_message_which_is_custom_message?
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_failure_message_artefact(count)
|
122
|
+
@failure_message_artefacts << { count: count }
|
123
|
+
false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'shared/asv_active_storageable'
|
4
|
+
require_relative 'shared/asv_allow_blankable'
|
5
|
+
require_relative 'shared/asv_attachable'
|
6
|
+
require_relative 'shared/asv_contextable'
|
7
|
+
require_relative 'shared/asv_messageable'
|
8
|
+
require_relative 'shared/asv_rspecable'
|
9
|
+
require_relative 'shared/asv_validatable'
|
10
|
+
|
11
|
+
module ActiveStorageValidations
|
12
|
+
module Matchers
|
13
|
+
def validate_processable_file_of(name)
|
14
|
+
ProcessableFileValidatorMatcher.new(name)
|
15
|
+
end
|
16
|
+
|
17
|
+
class ProcessableFileValidatorMatcher
|
18
|
+
include ASVActiveStorageable
|
19
|
+
include ASVAllowBlankable
|
20
|
+
include ASVAttachable
|
21
|
+
include ASVContextable
|
22
|
+
include ASVMessageable
|
23
|
+
include ASVRspecable
|
24
|
+
include ASVValidatable
|
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_file_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_file_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
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
module ActiveStorageValidations
|
6
|
+
module Matchers
|
7
|
+
module ASVActiveStorageable
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def is_a_valid_active_storage_attribute?
|
13
|
+
@subject.respond_to?(@attribute_name) &&
|
14
|
+
@subject.public_send(@attribute_name).respond_to?(:attach) &&
|
15
|
+
@subject.public_send(@attribute_name).respond_to?(:detach)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
module ActiveStorageValidations
|
6
|
+
module Matchers
|
7
|
+
module ASVAllowBlankable
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
def initialize_allow_blankable
|
11
|
+
@allow_blank = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def allow_blank
|
15
|
+
@allow_blank = true
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def is_allowing_blank?
|
22
|
+
return true unless @allow_blank
|
23
|
+
|
24
|
+
validate
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
module ActiveStorageValidations
|
6
|
+
module Matchers
|
7
|
+
module ASVAttachable
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def attach_file(file = dummy_file)
|
13
|
+
@subject.public_send(@attribute_name).attach(file)
|
14
|
+
@subject.public_send(@attribute_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def attach_files(count)
|
18
|
+
return unless count.positive?
|
19
|
+
|
20
|
+
file_array = Array.new(count, dummy_file)
|
21
|
+
|
22
|
+
@subject.public_send(@attribute_name).attach(file_array)
|
23
|
+
end
|
24
|
+
|
25
|
+
def detach_file
|
26
|
+
@subject.attachment_changes.delete(@attribute_name.to_s)
|
27
|
+
end
|
28
|
+
alias :detach_files :detach_file
|
29
|
+
|
30
|
+
def file_attached?
|
31
|
+
@subject.public_send(@attribute_name).attached?
|
32
|
+
end
|
33
|
+
|
34
|
+
def dummy_blob
|
35
|
+
ActiveStorage::Blob.create_and_upload!(**dummy_file)
|
36
|
+
end
|
37
|
+
|
38
|
+
def dummy_file
|
39
|
+
{
|
40
|
+
io: io,
|
41
|
+
filename: 'test.png',
|
42
|
+
content_type: 'image/png'
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def processable_image
|
47
|
+
{
|
48
|
+
io: StringIO.new(image_data),
|
49
|
+
filename: 'processable_image.png',
|
50
|
+
content_type: 'image/png'
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def not_processable_image
|
55
|
+
{
|
56
|
+
io: Tempfile.new('.'),
|
57
|
+
filename: 'not_processable_image.txt',
|
58
|
+
content_type: 'text/plain'
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def io
|
63
|
+
@io ||= Tempfile.new('Hello world!')
|
64
|
+
end
|
65
|
+
|
66
|
+
def image_data
|
67
|
+
# Binary data for a 1x1 transparent PNG image
|
68
|
+
"\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1F\x15\xC4\x89\x00\x00\x00\nIDATx\x9Cc\x00\x01\x00\x00\x05\x00\x01\r\n\x2D\xB4\x00\x00\x00\x00IEND\xAE\x42\x60\x82"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
module ActiveStorageValidations
|
6
|
+
module Matchers
|
7
|
+
module ASVContextable
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
def initialize_contextable
|
11
|
+
@context = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def on(context)
|
15
|
+
@context = context
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def is_context_valid?
|
22
|
+
return true if !@context && attribute_validators.none? { |validator| validator.options[:on] }
|
23
|
+
|
24
|
+
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] }
|
25
|
+
raise ArgumentError, "This validator matcher option only allows a symbol or an array" if !(@context.is_a?(Symbol) || @context.is_a?(Array))
|
26
|
+
|
27
|
+
if @context.is_a?(Array)
|
28
|
+
(validator_contexts & @context.map(&:to_s)) == validator_contexts || raise_context_not_listed_error
|
29
|
+
elsif @context.is_a?(Symbol)
|
30
|
+
validator_contexts.include?(@context.to_s) || raise_context_not_listed_error
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def validator_contexts
|
35
|
+
attribute_validators.map do |validator|
|
36
|
+
case validator.options[:on]
|
37
|
+
when Array then validator.options[:on].map { |context| context.to_s }
|
38
|
+
when NilClass then nil
|
39
|
+
else validator.options[:on].to_s
|
40
|
+
end
|
41
|
+
end.flatten.compact
|
42
|
+
end
|
43
|
+
|
44
|
+
def raise_context_not_listed_error
|
45
|
+
raise ArgumentError, "One of the provided contexts to the #on method is not found in any of the listed contexts for this attribute"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
module ActiveStorageValidations
|
6
|
+
module Matchers
|
7
|
+
module ASVMessageable
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
def initialize_messageable
|
11
|
+
@custom_message = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def with_message(custom_message)
|
15
|
+
@custom_message = custom_message
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def has_an_error_message_which_is_custom_message?
|
22
|
+
validator_errors_for_attribute.one? do |error|
|
23
|
+
error[:custom_message] == @custom_message
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|