active_storage_validations 2.0.1 → 2.0.3
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 +11 -60
- data/config/locales/da.yml +1 -0
- data/config/locales/de.yml +1 -0
- data/config/locales/en-GB.yml +54 -0
- data/config/locales/en.yml +1 -0
- data/config/locales/es.yml +1 -0
- data/config/locales/fr.yml +1 -0
- data/config/locales/it.yml +2 -1
- data/config/locales/ja.yml +1 -0
- data/config/locales/nl.yml +1 -0
- data/config/locales/pl.yml +1 -0
- data/config/locales/pt-BR.yml +1 -0
- data/config/locales/ru.yml +1 -0
- data/config/locales/sv.yml +1 -0
- data/config/locales/tr.yml +1 -0
- data/config/locales/uk.yml +1 -0
- data/config/locales/vi.yml +1 -0
- data/config/locales/zh-CN.yml +1 -0
- data/lib/active_storage_validations/analyzer/audio_analyzer.rb +2 -2
- data/lib/active_storage_validations/analyzer/content_type_analyzer.rb +5 -5
- data/lib/active_storage_validations/analyzer/image_analyzer/image_magick.rb +0 -1
- data/lib/active_storage_validations/analyzer/image_analyzer/vips.rb +0 -1
- data/lib/active_storage_validations/analyzer/video_analyzer.rb +4 -4
- data/lib/active_storage_validations/analyzer.rb +24 -23
- data/lib/active_storage_validations/aspect_ratio_validator.rb +19 -16
- data/lib/active_storage_validations/attached_validator.rb +3 -3
- data/lib/active_storage_validations/base_comparison_validator.rb +6 -6
- data/lib/active_storage_validations/content_type_validator.rb +39 -24
- data/lib/active_storage_validations/dimension_validator.rb +158 -82
- data/lib/active_storage_validations/duration_validator.rb +28 -14
- data/lib/active_storage_validations/extensors/asv_blob_metadatable.rb +15 -8
- data/lib/active_storage_validations/extensors/asv_marcelable.rb +1 -1
- data/lib/active_storage_validations/limit_validator.rb +27 -19
- data/lib/active_storage_validations/matchers/aspect_ratio_validator_matcher.rb +12 -12
- data/lib/active_storage_validations/matchers/attached_validator_matcher.rb +6 -6
- data/lib/active_storage_validations/matchers/base_comparison_validator_matcher.rb +7 -7
- data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +15 -13
- data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +8 -8
- data/lib/active_storage_validations/matchers/duration_validator_matcher.rb +2 -2
- data/lib/active_storage_validations/matchers/limit_validator_matcher.rb +8 -8
- data/lib/active_storage_validations/matchers/processable_file_validator_matcher.rb +7 -7
- data/lib/active_storage_validations/matchers/shared/asv_active_storageable.rb +1 -1
- data/lib/active_storage_validations/matchers/shared/asv_allow_blankable.rb +1 -1
- data/lib/active_storage_validations/matchers/shared/asv_attachable.rb +9 -9
- data/lib/active_storage_validations/matchers/shared/asv_contextable.rb +11 -3
- data/lib/active_storage_validations/matchers/shared/asv_messageable.rb +1 -1
- data/lib/active_storage_validations/matchers/shared/asv_rspecable.rb +2 -2
- data/lib/active_storage_validations/matchers/shared/asv_validatable.rb +2 -2
- data/lib/active_storage_validations/matchers/size_validator_matcher.rb +2 -2
- data/lib/active_storage_validations/matchers/total_size_validator_matcher.rb +3 -3
- data/lib/active_storage_validations/matchers.rb +10 -10
- data/lib/active_storage_validations/processable_file_validator.rb +37 -37
- data/lib/active_storage_validations/railtie.rb +7 -1
- data/lib/active_storage_validations/shared/asv_attachable.rb +60 -28
- data/lib/active_storage_validations/shared/asv_errorable.rb +1 -1
- data/lib/active_storage_validations/shared/asv_symbolizable.rb +1 -1
- data/lib/active_storage_validations/size_validator.rb +1 -1
- data/lib/active_storage_validations/total_size_validator.rb +2 -2
- data/lib/active_storage_validations/version.rb +1 -1
- data/lib/active_storage_validations.rb +22 -25
- metadata +3 -2
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
9
|
-
require_relative
|
3
|
+
require_relative "shared/asv_active_storageable"
|
4
|
+
require_relative "shared/asv_analyzable"
|
5
|
+
require_relative "shared/asv_attachable"
|
6
|
+
require_relative "shared/asv_errorable"
|
7
|
+
require_relative "shared/asv_optionable"
|
8
|
+
require_relative "shared/asv_symbolizable"
|
9
|
+
require_relative "analyzer/content_type_analyzer"
|
10
10
|
|
11
11
|
module ActiveStorageValidations
|
12
12
|
class ContentTypeValidator < ActiveModel::EachValidator # :nodoc:
|
@@ -96,9 +96,7 @@ module ActiveStorageValidations
|
|
96
96
|
|
97
97
|
return true if attachable_content_type_is_authorized
|
98
98
|
|
99
|
-
|
100
|
-
add_error(record, attribute, ERROR_TYPES.first, **errors_options)
|
101
|
-
false
|
99
|
+
add_content_type_invalid_error(record, attribute, attachable)
|
102
100
|
end
|
103
101
|
|
104
102
|
def marcel_attachable_content_type(attachable)
|
@@ -108,14 +106,15 @@ module ActiveStorageValidations
|
|
108
106
|
def not_spoofing_content_type?(record, attribute, attachable, blob)
|
109
107
|
return true unless enable_spoofing_protection?
|
110
108
|
|
111
|
-
@detected_content_type =
|
109
|
+
@detected_content_type = begin
|
110
|
+
metadata_for(blob, attachable, METADATA_KEYS)&.fetch(:content_type, nil)
|
111
|
+
rescue ActiveStorage::FileNotFoundError
|
112
|
+
add_attachment_missing_error(record, attribute, attachable)
|
113
|
+
return false
|
114
|
+
end
|
112
115
|
|
113
116
|
if attachable_content_type_vs_detected_content_type_mismatch?
|
114
|
-
|
115
|
-
errors_options[:detected_content_type] = @detected_content_type
|
116
|
-
errors_options[:detected_human_content_type] = content_type_to_human_format(@detected_content_type)
|
117
|
-
add_error(record, attribute, ERROR_TYPES.second, **errors_options)
|
118
|
-
false
|
117
|
+
add_content_type_spoofed_error(record, attribute, attachable, @detected_content_type)
|
119
118
|
else
|
120
119
|
true
|
121
120
|
end
|
@@ -142,13 +141,27 @@ module ActiveStorageValidations
|
|
142
141
|
end
|
143
142
|
|
144
143
|
def enlarged_content_type(content_type)
|
145
|
-
[content_type, *parent_content_types(content_type)].compact.uniq
|
144
|
+
[ content_type, *parent_content_types(content_type) ].compact.uniq
|
146
145
|
end
|
147
146
|
|
148
147
|
def parent_content_types(content_type)
|
149
148
|
Marcel::TYPE_PARENTS[content_type] || []
|
150
149
|
end
|
151
150
|
|
151
|
+
def add_content_type_invalid_error(record, attribute, attachable)
|
152
|
+
errors_options = initialize_and_populate_error_options(options, attachable)
|
153
|
+
add_error(record, attribute, ERROR_TYPES.first, **errors_options)
|
154
|
+
false
|
155
|
+
end
|
156
|
+
|
157
|
+
def add_content_type_spoofed_error(record, attribute, attachable, detected_content_type)
|
158
|
+
errors_options = initialize_and_populate_error_options(options, attachable)
|
159
|
+
errors_options[:detected_content_type] = @detected_content_type
|
160
|
+
errors_options[:detected_human_content_type] = content_type_to_human_format(@detected_content_type)
|
161
|
+
add_error(record, attribute, ERROR_TYPES.second, **errors_options)
|
162
|
+
false
|
163
|
+
end
|
164
|
+
|
152
165
|
def initialize_and_populate_error_options(options, attachable)
|
153
166
|
errors_options = initialize_error_options(options, attachable)
|
154
167
|
errors_options[:content_type] = @attachable_content_type
|
@@ -170,12 +183,12 @@ module ActiveStorageValidations
|
|
170
183
|
end
|
171
184
|
.flatten
|
172
185
|
.compact
|
173
|
-
.join(
|
186
|
+
.join(", ")
|
174
187
|
end
|
175
188
|
|
176
189
|
def ensure_exactly_one_validator_option
|
177
190
|
unless AVAILABLE_CHECKS.one? { |argument| options.key?(argument) }
|
178
|
-
raise ArgumentError,
|
191
|
+
raise ArgumentError, "You must pass either :with or :in to the validator"
|
179
192
|
end
|
180
193
|
end
|
181
194
|
|
@@ -211,20 +224,22 @@ module ActiveStorageValidations
|
|
211
224
|
end
|
212
225
|
|
213
226
|
def invalid_content_type?(content_type)
|
214
|
-
if content_type ==
|
227
|
+
if content_type == "image/jpg"
|
215
228
|
raise ArgumentError, "'image/jpg' is not a valid content type, you should use 'image/jpeg' instead"
|
216
229
|
end
|
217
230
|
|
218
|
-
all_available_marcel_content_types.
|
231
|
+
all_available_marcel_content_types.exclude?(content_type.to_s)
|
219
232
|
end
|
220
233
|
|
221
234
|
def all_available_marcel_content_types
|
222
|
-
@all_available_marcel_content_types ||= Marcel::
|
223
|
-
|
235
|
+
@all_available_marcel_content_types ||= Marcel::TYPE_EXTS
|
236
|
+
.keys
|
237
|
+
.push(*Marcel::MAGIC.map(&:first))
|
238
|
+
.tap(&:uniq!)
|
224
239
|
end
|
225
240
|
|
226
241
|
def invalid_extension?(content_type)
|
227
|
-
Marcel::MimeType.for(extension: content_type.to_s) ==
|
242
|
+
Marcel::MimeType.for(extension: content_type.to_s) == "application/octet-stream"
|
228
243
|
end
|
229
244
|
end
|
230
245
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
3
|
+
require_relative "shared/asv_active_storageable"
|
4
|
+
require_relative "shared/asv_analyzable"
|
5
|
+
require_relative "shared/asv_attachable"
|
6
|
+
require_relative "shared/asv_errorable"
|
7
|
+
require_relative "shared/asv_optionable"
|
8
|
+
require_relative "shared/asv_symbolizable"
|
9
9
|
|
10
10
|
module ActiveStorageValidations
|
11
11
|
class DimensionValidator < ActiveModel::EachValidator # :nodoc
|
@@ -33,9 +33,9 @@ module ActiveStorageValidations
|
|
33
33
|
METADATA_KEYS = %i[width height].freeze
|
34
34
|
|
35
35
|
def check_validity!
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
ensure_at_least_one_validator_option
|
37
|
+
ensure_dimension_in_option_validity
|
38
|
+
ensure_min_max_option_validity
|
39
39
|
end
|
40
40
|
|
41
41
|
def validate_each(record, attribute, _value)
|
@@ -46,101 +46,177 @@ module ActiveStorageValidations
|
|
46
46
|
|
47
47
|
private
|
48
48
|
|
49
|
+
def ensure_at_least_one_validator_option
|
50
|
+
unless AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
|
51
|
+
raise ArgumentError, "You must pass either :width, :height, :min or :max to the validator"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def ensure_dimension_in_option_validity
|
56
|
+
%i[width height].each do |dimension|
|
57
|
+
if options[dimension]&.is_a?(Hash) && options[dimension][:in].present?
|
58
|
+
raise ArgumentError, "{ #{dimension}: { in: value } } value must be a Range (min..max)" if !options[dimension][:in].is_a?(Range) && !options[dimension][:in].is_a?(Proc)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def ensure_min_max_option_validity
|
64
|
+
%i[min max].each do |bound|
|
65
|
+
if options[bound].present?
|
66
|
+
raise ArgumentError, "{ #{bound}: value } value must be a Range (#{bound}_width..#{bound}_height)" if !options[bound]&.is_a?(Range) && !options[bound]&.is_a?(Proc)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
49
71
|
def is_valid?(record, attribute, file, metadata)
|
50
72
|
flat_options = process_options(record)
|
51
73
|
errors_options = initialize_error_options(options, file)
|
52
74
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
75
|
+
return add_media_metadata_missing_error(record, attribute, file, errors_options) unless valid_metadata?(metadata)
|
76
|
+
|
77
|
+
if min_max_validation?(flat_options)
|
78
|
+
validate_min_max(record, attribute, metadata, flat_options, errors_options)
|
79
|
+
else
|
80
|
+
validate_width_height(record, attribute, metadata, flat_options, errors_options)
|
57
81
|
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def valid_metadata?(metadata)
|
85
|
+
metadata[:width].to_i > 0 && metadata[:height].to_i > 0
|
86
|
+
end
|
58
87
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
88
|
+
def min_max_validation?(flat_options)
|
89
|
+
flat_options[:min] || flat_options[:max]
|
90
|
+
end
|
91
|
+
|
92
|
+
def validate_min_max(record, attribute, metadata, flat_options, errors_options)
|
93
|
+
return false unless validate_min(record, attribute, metadata, flat_options, errors_options)
|
94
|
+
return false unless validate_max(record, attribute, metadata, flat_options, errors_options)
|
95
|
+
|
96
|
+
true
|
97
|
+
end
|
67
98
|
|
68
|
-
|
69
|
-
|
99
|
+
def validate_width_height(record, attribute, metadata, flat_options, errors_options)
|
100
|
+
%i[width height].each do |dimension|
|
101
|
+
next unless flat_options[dimension]
|
102
|
+
|
103
|
+
if flat_options[dimension].is_a?(Hash)
|
104
|
+
validate_range(record, attribute, dimension, metadata, flat_options, errors_options)
|
105
|
+
else
|
106
|
+
validate_exact(record, attribute, dimension, metadata, flat_options, errors_options)
|
70
107
|
end
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# rubocop:disable Metrics/BlockLength
|
112
|
+
%i[min max].each do |bound|
|
113
|
+
define_method("validate_#{bound}") do |record, attribute, metadata, flat_options, errors_options|
|
114
|
+
if send(:"invalid_#{bound}?", flat_options, metadata)
|
115
|
+
send(:"add_#{bound}_error", record, attribute, flat_options, errors_options)
|
116
|
+
false
|
117
|
+
else
|
118
|
+
true
|
80
119
|
end
|
120
|
+
end
|
81
121
|
|
82
|
-
#
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
if flat_options[length].is_a?(Hash)
|
89
|
-
if flat_options[length][:in] && (metadata[length] < flat_options[length][:min] || metadata[length] > flat_options[length][:max])
|
90
|
-
error_type = :"dimension_#{length}_not_included_in"
|
91
|
-
errors_options[:min] = flat_options[length][:min]
|
92
|
-
errors_options[:max] = flat_options[length][:max]
|
93
|
-
|
94
|
-
add_error(record, attribute, error_type, **errors_options)
|
95
|
-
width_or_height_invalid = true
|
96
|
-
else
|
97
|
-
if flat_options[length][:min] && metadata[length] < flat_options[length][:min]
|
98
|
-
error_type = :"dimension_#{length}_not_greater_than_or_equal_to"
|
99
|
-
errors_options[:length] = flat_options[length][:min]
|
100
|
-
|
101
|
-
add_error(record, attribute, error_type, **errors_options)
|
102
|
-
width_or_height_invalid = true
|
103
|
-
elsif flat_options[length][:max] && metadata[length] > flat_options[length][:max]
|
104
|
-
error_type = :"dimension_#{length}_not_less_than_or_equal_to"
|
105
|
-
errors_options[:length] = flat_options[length][:max]
|
106
|
-
|
107
|
-
add_error(record, attribute, error_type, **errors_options)
|
108
|
-
width_or_height_invalid = true
|
109
|
-
end
|
110
|
-
end
|
111
|
-
else
|
112
|
-
if metadata[length] != flat_options[length]
|
113
|
-
error_type = :"dimension_#{length}_not_equal_to"
|
114
|
-
errors_options[:length] = flat_options[length]
|
115
|
-
|
116
|
-
add_error(record, attribute, error_type, **errors_options)
|
117
|
-
width_or_height_invalid = true
|
118
|
-
end
|
119
|
-
end
|
122
|
+
define_method("validate_dimension_#{bound}") do |record, attribute, dimension, metadata, flat_options, errors_options|
|
123
|
+
if send(:"invalid_dimension_#{bound}?", flat_options, dimension, metadata)
|
124
|
+
send(:"add_dimension_#{bound}_error", record, attribute, dimension, flat_options, errors_options)
|
125
|
+
false
|
126
|
+
else
|
127
|
+
true
|
120
128
|
end
|
129
|
+
end
|
130
|
+
|
131
|
+
define_method("invalid_#{bound}?") do |flat_options, metadata|
|
132
|
+
flat_options[bound] && (
|
133
|
+
send(:"invalid_dimension_#{bound}?", flat_options, :width, metadata) ||
|
134
|
+
send(:"invalid_dimension_#{bound}?", flat_options, :height, metadata)
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
define_method("invalid_dimension_#{bound}?") do |flat_options, dimension, metadata|
|
139
|
+
flat_options[dimension][bound] && metadata[dimension].public_send(bound == :min ? :< : :>, flat_options[dimension][bound])
|
140
|
+
end
|
141
|
+
|
142
|
+
define_method("add_#{bound}_error") do |record, attribute, flat_options, errors_options|
|
143
|
+
errors_options[:width] = flat_options[:width][bound]
|
144
|
+
errors_options[:height] = flat_options[:height][bound]
|
145
|
+
add_error(record, attribute, :"dimension_#{bound}_not_included_in", **errors_options)
|
146
|
+
end
|
121
147
|
|
122
|
-
|
148
|
+
define_method("add_dimension_#{bound}_error") do |record, attribute, dimension, flat_options, errors_options|
|
149
|
+
error_type = bound == :min ? :not_greater_than_or_equal_to : :not_less_than_or_equal_to
|
150
|
+
errors_options[:length] = flat_options[dimension][bound]
|
151
|
+
add_error(record, attribute, :"dimension_#{dimension}_#{error_type}", **errors_options)
|
123
152
|
end
|
153
|
+
end
|
154
|
+
# rubocop:enable Metrics/BlockLength
|
124
155
|
|
125
|
-
|
156
|
+
def validate_range(record, attribute, dimension, metadata, flat_options, errors_options)
|
157
|
+
if in_option_used?(flat_options, dimension)
|
158
|
+
return false unless validate_in(record, attribute, dimension, metadata, flat_options, errors_options)
|
159
|
+
else
|
160
|
+
return false unless validate_dimension_min_max(record, attribute, dimension, metadata, flat_options, errors_options)
|
161
|
+
end
|
162
|
+
|
163
|
+
true
|
164
|
+
end
|
165
|
+
|
166
|
+
def validate_in(record, attribute, dimension, metadata, flat_options, errors_options)
|
167
|
+
if outside_range?(metadata[dimension], flat_options[dimension])
|
168
|
+
add_range_error(record, attribute, dimension, flat_options, errors_options)
|
169
|
+
false
|
170
|
+
else
|
171
|
+
true
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def in_option_used?(flat_options, dimension)
|
176
|
+
flat_options[dimension][:in]
|
177
|
+
end
|
178
|
+
|
179
|
+
def outside_range?(value, options)
|
180
|
+
value < options[:min] || value > options[:max]
|
181
|
+
end
|
182
|
+
|
183
|
+
def add_range_error(record, attribute, dimension, flat_options, errors_options)
|
184
|
+
errors_options[:min] = flat_options[dimension][:min]
|
185
|
+
errors_options[:max] = flat_options[dimension][:max]
|
186
|
+
add_error(record, attribute, :"dimension_#{dimension}_not_included_in", **errors_options)
|
187
|
+
end
|
188
|
+
|
189
|
+
def validate_dimension_min_max(record, attribute, dimension, metadata, flat_options, errors_options)
|
190
|
+
%i[min max].each do |bound|
|
191
|
+
send(:"validate_dimension_#{bound}", record, attribute, dimension, metadata, flat_options, errors_options)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def validate_exact(record, attribute, dimension, metadata, flat_options, errors_options)
|
196
|
+
if metadata[dimension] != flat_options[dimension]
|
197
|
+
errors_options[:length] = flat_options[dimension]
|
198
|
+
add_error(record, attribute, :"dimension_#{dimension}_not_equal_to", **errors_options)
|
199
|
+
false
|
200
|
+
else
|
201
|
+
true
|
202
|
+
end
|
126
203
|
end
|
127
204
|
|
128
205
|
def process_options(record)
|
129
206
|
flat_options = set_flat_options(record)
|
130
207
|
|
131
|
-
[
|
132
|
-
if flat_options[
|
133
|
-
if (range = flat_options[
|
134
|
-
|
135
|
-
flat_options[length][:min], flat_options[length][:max] = range.min, range.max
|
208
|
+
%i[width height].each do |dimension|
|
209
|
+
if flat_options[dimension] and flat_options[dimension].is_a?(Hash)
|
210
|
+
if (range = flat_options[dimension][:in])
|
211
|
+
flat_options[dimension][:min], flat_options[dimension][:max] = range.min, range.max
|
136
212
|
end
|
137
213
|
end
|
138
214
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
flat_options[:width] = {
|
143
|
-
flat_options[:height] = {
|
215
|
+
|
216
|
+
%i[min max].each do |bound|
|
217
|
+
if (range = flat_options[bound])
|
218
|
+
flat_options[:width] = { bound => range.first }
|
219
|
+
flat_options[:height] = { bound => range.last }
|
144
220
|
end
|
145
221
|
end
|
146
222
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "base_comparison_validator"
|
4
4
|
|
5
5
|
module ActiveStorageValidations
|
6
6
|
class DurationValidator < BaseComparisonValidator
|
@@ -22,34 +22,48 @@ module ActiveStorageValidations
|
|
22
22
|
flat_options = set_flat_options(record)
|
23
23
|
|
24
24
|
attachables_and_blobs(record, attribute).each do |attachable, blob|
|
25
|
-
duration =
|
25
|
+
duration = begin
|
26
|
+
metadata_for(blob, attachable, METADATA_KEYS)&.fetch(:duration, nil)
|
27
|
+
rescue ActiveStorage::FileNotFoundError
|
28
|
+
add_attachment_missing_error(record, attribute, attachable)
|
29
|
+
next
|
30
|
+
end
|
26
31
|
|
27
32
|
if duration.to_i <= 0
|
28
|
-
|
29
|
-
add_error(record, attribute, :media_metadata_missing, **errors_options)
|
33
|
+
add_media_metadata_missing_error(record, attribute, attachable)
|
30
34
|
next
|
31
35
|
end
|
32
36
|
|
33
|
-
|
34
|
-
|
35
|
-
errors_options = initialize_error_options(options, attachable)
|
36
|
-
populate_error_options(errors_options, flat_options)
|
37
|
-
errors_options[:duration] = format_bound_value(duration)
|
38
|
-
|
39
|
-
keys = AVAILABLE_CHECKS & flat_options.keys
|
40
|
-
error_type = "duration_not_#{keys.first}".to_sym
|
41
|
-
|
42
|
-
add_error(record, attribute, error_type, **errors_options)
|
37
|
+
is_valid?(duration, flat_options) || populate_error_options_and_add_error(record, attribute, attachable, flat_options, duration)
|
43
38
|
end
|
44
39
|
end
|
45
40
|
|
46
41
|
private
|
47
42
|
|
43
|
+
def populate_error_options_and_add_error(record, attribute, attachable, flat_options, duration)
|
44
|
+
errors_options = initialize_error_options(options, attachable)
|
45
|
+
populate_error_options(errors_options, flat_options, duration)
|
46
|
+
|
47
|
+
error_type = set_error_type(flat_options)
|
48
|
+
|
49
|
+
add_error(record, attribute, error_type, **errors_options)
|
50
|
+
end
|
51
|
+
|
48
52
|
def format_bound_value(value)
|
49
53
|
return nil unless value
|
50
54
|
|
51
55
|
custom_value = value == value.to_i ? value.to_i : value
|
52
56
|
ActiveSupport::Duration.build(custom_value).inspect
|
53
57
|
end
|
58
|
+
|
59
|
+
def populate_error_options(errors_options, flat_options, duration)
|
60
|
+
super(errors_options, flat_options)
|
61
|
+
errors_options[:duration] = format_bound_value(duration)
|
62
|
+
end
|
63
|
+
|
64
|
+
def set_error_type(flat_options)
|
65
|
+
keys = AVAILABLE_CHECKS & flat_options.keys
|
66
|
+
"duration_not_#{keys.first}".to_sym
|
67
|
+
end
|
54
68
|
end
|
55
69
|
end
|
@@ -4,6 +4,7 @@ module ActiveStorageValidations
|
|
4
4
|
module ASVBlobMetadatable
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
|
+
# rubocop:disable Metrics/BlockLength
|
7
8
|
included do
|
8
9
|
# This method returns the metadata that has been set by our gem.
|
9
10
|
# The metadata is stored in the blob's custom metadata. All keys are prefixed with 'asv_'
|
@@ -14,15 +15,15 @@ module ActiveStorageValidations
|
|
14
15
|
# Because of how the metadata is stored, we need to convert the values from String
|
15
16
|
# to Integer or Boolean.
|
16
17
|
def active_storage_validations_metadata
|
17
|
-
metadata.dig(
|
18
|
-
&.select { |key, _| key.to_s.start_with?(
|
19
|
-
&.transform_keys { |key| key.to_s.delete_prefix(
|
18
|
+
metadata.dig("custom")
|
19
|
+
&.select { |key, _| key.to_s.start_with?("asv_") }
|
20
|
+
&.transform_keys { |key| key.to_s.delete_prefix("asv_") }
|
20
21
|
&.transform_values do |value|
|
21
22
|
case value
|
22
23
|
when /\A\d+\z/ then value.to_i
|
23
24
|
when /\A\d+\.\d+\z/ then value.to_f
|
24
|
-
when
|
25
|
-
when
|
25
|
+
when "true" then true
|
26
|
+
when "false" then false
|
26
27
|
else value
|
27
28
|
end
|
28
29
|
end || {}
|
@@ -34,16 +35,22 @@ module ActiveStorageValidations
|
|
34
35
|
def merge_into_active_storage_validations_metadata(hash)
|
35
36
|
aws_compatible_metadata = normalize_active_storage_validations_metadata_for_aws(hash)
|
36
37
|
|
37
|
-
metadata[
|
38
|
-
metadata[
|
38
|
+
metadata["custom"] ||= {}
|
39
|
+
metadata["custom"].merge!(aws_compatible_metadata)
|
39
40
|
|
40
41
|
active_storage_validations_metadata
|
41
42
|
end
|
42
43
|
|
43
44
|
def normalize_active_storage_validations_metadata_for_aws(hash)
|
44
|
-
hash.transform_keys { |key, _| key.to_s.start_with?(
|
45
|
+
hash.transform_keys { |key, _| key.to_s.start_with?("asv_") ? key : "asv_#{key}" }
|
45
46
|
.transform_values(&:to_s)
|
46
47
|
end
|
48
|
+
|
49
|
+
def remove_active_storage_validations_metadata!
|
50
|
+
metadata["custom"] ||= {}
|
51
|
+
metadata["custom"].delete_if { |key, _| key.to_s.start_with?("asv_") }
|
52
|
+
end
|
47
53
|
end
|
54
|
+
# rubocop:enable Metrics/BlockLength
|
48
55
|
end
|
49
56
|
end
|
@@ -9,4 +9,4 @@ Marcel::MimeType.extend "text/xml", parents: %(application/xml) # alias
|
|
9
9
|
Marcel::MimeType.extend "video/theora", parents: %(video/ogg)
|
10
10
|
|
11
11
|
# Add empty content type
|
12
|
-
Marcel::MimeType.extend "inode/x-empty", extensions: %w
|
12
|
+
Marcel::MimeType.extend "inode/x-empty", extensions: %w[empty]
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
3
|
+
require_relative "shared/asv_active_storageable"
|
4
|
+
require_relative "shared/asv_errorable"
|
5
|
+
require_relative "shared/asv_optionable"
|
6
|
+
require_relative "shared/asv_symbolizable"
|
7
7
|
|
8
8
|
module ActiveStorageValidations
|
9
9
|
class LimitValidator < ActiveModel::EachValidator # :nodoc:
|
@@ -31,18 +31,8 @@ module ActiveStorageValidations
|
|
31
31
|
|
32
32
|
return if files_count_valid?(count, flat_options)
|
33
33
|
|
34
|
-
errors_options =
|
35
|
-
|
36
|
-
errors_options[:max] = flat_options[:max]
|
37
|
-
errors_options[:count] = count
|
38
|
-
error_type = if flat_options[:min] && flat_options[:max]
|
39
|
-
:limit_out_of_range
|
40
|
-
elsif flat_options[:min] && count < flat_options[:min]
|
41
|
-
:limit_min_not_reached
|
42
|
-
else
|
43
|
-
:limit_max_exceeded
|
44
|
-
end
|
45
|
-
|
34
|
+
errors_options = initialize_and_populate_error_options(options, flat_options, count)
|
35
|
+
error_type = set_error_type(flat_options, count)
|
46
36
|
add_error(record, attribute, error_type, **errors_options)
|
47
37
|
end
|
48
38
|
|
@@ -60,15 +50,15 @@ module ActiveStorageValidations
|
|
60
50
|
|
61
51
|
def ensure_at_least_one_validator_option
|
62
52
|
unless AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
|
63
|
-
raise ArgumentError,
|
53
|
+
raise ArgumentError, "You must pass either :max or :min to the validator"
|
64
54
|
end
|
65
55
|
end
|
66
56
|
|
67
57
|
def ensure_arguments_validity
|
68
58
|
return true if min_max_are_proc? || min_or_max_is_proc_and_other_not_present?
|
69
59
|
|
70
|
-
raise ArgumentError,
|
71
|
-
raise ArgumentError,
|
60
|
+
raise ArgumentError, "You must pass integers to :min and :max" if min_or_max_defined_and_not_integer?
|
61
|
+
raise ArgumentError, "You must pass a higher value to :max than to :min" if min_higher_than_max?
|
72
62
|
end
|
73
63
|
|
74
64
|
def min_max_are_proc?
|
@@ -88,5 +78,23 @@ module ActiveStorageValidations
|
|
88
78
|
def min_higher_than_max?
|
89
79
|
options[:min] > options[:max] if options[:min].is_a?(Integer) && options[:max].is_a?(Integer)
|
90
80
|
end
|
81
|
+
|
82
|
+
def initialize_and_populate_error_options(options, flat_options, count)
|
83
|
+
errors_options = initialize_error_options(options)
|
84
|
+
errors_options[:min] = flat_options[:min]
|
85
|
+
errors_options[:max] = flat_options[:max]
|
86
|
+
errors_options[:count] = count
|
87
|
+
errors_options
|
88
|
+
end
|
89
|
+
|
90
|
+
def set_error_type(flat_options, count)
|
91
|
+
if flat_options[:min] && flat_options[:max]
|
92
|
+
:limit_out_of_range
|
93
|
+
elsif flat_options[:min] && count < flat_options[:min]
|
94
|
+
:limit_min_not_reached
|
95
|
+
else
|
96
|
+
:limit_max_exceeded
|
97
|
+
end
|
98
|
+
end
|
91
99
|
end
|
92
100
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
9
|
-
require_relative
|
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
10
|
|
11
11
|
module ActiveStorageValidations
|
12
12
|
module Matchers
|
@@ -101,17 +101,17 @@ module ActiveStorageValidations
|
|
101
101
|
|
102
102
|
def valid_width_and_height_for(aspect_ratio)
|
103
103
|
case aspect_ratio
|
104
|
-
when :square then [100, 100]
|
105
|
-
when :portrait then [100, 200]
|
106
|
-
when :landscape then [200, 100]
|
104
|
+
when :square then [ 100, 100 ]
|
105
|
+
when :portrait then [ 100, 200 ]
|
106
|
+
when :landscape then [ 200, 100 ]
|
107
107
|
when validator_class::ASPECT_RATIO_REGEX
|
108
108
|
aspect_ratio =~ validator_class::ASPECT_RATIO_REGEX
|
109
109
|
x = Regexp.last_match(1).to_i
|
110
110
|
y = Regexp.last_match(2).to_i
|
111
111
|
|
112
|
-
[100 * x, 100 * y]
|
112
|
+
[ 100 * x, 100 * y ]
|
113
113
|
else
|
114
|
-
[-1, -1]
|
114
|
+
[ -1, -1 ]
|
115
115
|
end
|
116
116
|
end
|
117
117
|
end
|