active_storage_validations 0.9.7 → 2.0.2
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 +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
|