active_storage_validations 1.3.0 → 1.3.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of active_storage_validations might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +12 -8
- data/config/locales/da.yml +0 -1
- data/config/locales/de.yml +0 -1
- data/config/locales/en.yml +0 -1
- data/config/locales/es.yml +0 -1
- data/config/locales/fr.yml +0 -1
- data/config/locales/it.yml +0 -1
- data/config/locales/ja.yml +0 -1
- data/config/locales/nl.yml +0 -1
- data/config/locales/pl.yml +0 -1
- data/config/locales/pt-BR.yml +0 -1
- data/config/locales/ru.yml +0 -1
- data/config/locales/sv.yml +0 -1
- data/config/locales/tr.yml +0 -1
- data/config/locales/uk.yml +0 -1
- data/config/locales/vi.yml +0 -1
- data/config/locales/zh-CN.yml +0 -1
- data/lib/active_storage_validations/aspect_ratio_validator.rb +61 -46
- data/lib/active_storage_validations/attached_validator.rb +6 -6
- data/lib/active_storage_validations/base_size_validator.rb +8 -7
- data/lib/active_storage_validations/content_type_spoof_detector.rb +13 -49
- data/lib/active_storage_validations/content_type_validator.rb +112 -54
- data/lib/active_storage_validations/dimension_validator.rb +11 -10
- data/lib/active_storage_validations/limit_validator.rb +9 -8
- data/lib/active_storage_validations/marcel_extensor.rb +2 -0
- data/lib/active_storage_validations/matchers/aspect_ratio_validator_matcher.rb +14 -14
- data/lib/active_storage_validations/matchers/attached_validator_matcher.rb +12 -12
- data/lib/active_storage_validations/matchers/base_size_validator_matcher.rb +14 -14
- data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +40 -40
- data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +14 -14
- data/lib/active_storage_validations/matchers/limit_validator_matcher.rb +14 -14
- data/lib/active_storage_validations/matchers/processable_image_validator_matcher.rb +14 -14
- data/lib/active_storage_validations/matchers/{concerns/active_storageable.rb → shared/asv_active_storageable.rb} +4 -2
- data/lib/active_storage_validations/matchers/{concerns/allow_blankable.rb → shared/asv_allow_blankable.rb} +4 -2
- data/lib/active_storage_validations/matchers/{concerns/attachable.rb → shared/asv_attachable.rb} +7 -1
- data/lib/active_storage_validations/matchers/{concerns/contextable.rb → shared/asv_contextable.rb} +4 -2
- data/lib/active_storage_validations/matchers/{concerns/messageable.rb → shared/asv_messageable.rb} +4 -2
- data/lib/active_storage_validations/matchers/{concerns/rspecable.rb → shared/asv_rspecable.rb} +4 -2
- data/lib/active_storage_validations/matchers/{concerns/validatable.rb → shared/asv_validatable.rb} +4 -2
- data/lib/active_storage_validations/metadata.rb +18 -19
- data/lib/active_storage_validations/processable_image_validator.rb +8 -8
- data/lib/active_storage_validations/{concerns/active_storageable.rb → shared/asv_active_storageable.rb} +4 -2
- data/lib/active_storage_validations/shared/asv_attachable.rb +136 -0
- data/lib/active_storage_validations/{concerns/errorable.rb → shared/asv_errorable.rb} +5 -2
- data/lib/active_storage_validations/{concerns/loggable.rb → shared/asv_loggable.rb} +3 -1
- data/lib/active_storage_validations/shared/asv_optionable.rb +29 -0
- data/lib/active_storage_validations/{concerns/symbolizable.rb → shared/asv_symbolizable.rb} +3 -1
- data/lib/active_storage_validations/size_validator.rb +2 -2
- 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 +0 -1
- metadata +65 -25
- data/lib/active_storage_validations/concerns/metadatable.rb +0 -31
- data/lib/active_storage_validations/option_proc_unfolding.rb +0 -16
@@ -1,16 +1,19 @@
|
|
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_attachable'
|
5
|
+
require_relative 'shared/asv_errorable'
|
6
|
+
require_relative 'shared/asv_optionable'
|
7
|
+
require_relative 'shared/asv_symbolizable'
|
8
|
+
require_relative 'content_type_spoof_detector'
|
7
9
|
|
8
10
|
module ActiveStorageValidations
|
9
11
|
class ContentTypeValidator < ActiveModel::EachValidator # :nodoc:
|
10
|
-
include
|
11
|
-
include
|
12
|
-
include
|
13
|
-
include
|
12
|
+
include ASVActiveStorageable
|
13
|
+
include ASVAttachable
|
14
|
+
include ASVErrorable
|
15
|
+
include ASVOptionable
|
16
|
+
include ASVSymbolizable
|
14
17
|
|
15
18
|
AVAILABLE_CHECKS = %i[with in].freeze
|
16
19
|
ERROR_TYPES = %i[
|
@@ -26,18 +29,20 @@ module ActiveStorageValidations
|
|
26
29
|
def validate_each(record, attribute, _value)
|
27
30
|
return if no_attachments?(record, attribute)
|
28
31
|
|
29
|
-
|
30
|
-
return if
|
32
|
+
@authorized_content_types = authorized_content_types_from_options(record)
|
33
|
+
return if @authorized_content_types.empty?
|
31
34
|
|
32
|
-
|
33
|
-
|
35
|
+
attachables_from_changes(record, attribute).each do |attachable|
|
36
|
+
set_attachable_cached_values(attachable)
|
37
|
+
is_valid?(record, attribute, attachable)
|
34
38
|
end
|
35
39
|
end
|
36
40
|
|
37
41
|
private
|
38
42
|
|
39
|
-
def
|
40
|
-
flat_options =
|
43
|
+
def authorized_content_types_from_options(record)
|
44
|
+
flat_options = set_flat_options(record)
|
45
|
+
|
41
46
|
(Array.wrap(flat_options[:with]) + Array.wrap(flat_options[:in])).compact.map do |type|
|
42
47
|
case type
|
43
48
|
when String, Symbol then Marcel::MimeType.for(declared_type: type.to_s, extension: type.to_s)
|
@@ -46,47 +51,58 @@ module ActiveStorageValidations
|
|
46
51
|
end
|
47
52
|
end
|
48
53
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
.join(', ')
|
53
|
-
end
|
54
|
-
|
55
|
-
def content_type(file)
|
56
|
-
# We remove potential mime type parameters
|
57
|
-
file.blob.present? && file.blob.content_type.downcase.split(/[;,\s]/, 2).first
|
54
|
+
def set_attachable_cached_values(attachable)
|
55
|
+
@attachable_content_type = attachable_content_type(attachable)
|
56
|
+
@attachable_filename = attachable_filename(attachable).to_s
|
58
57
|
end
|
59
58
|
|
60
|
-
|
61
|
-
|
62
|
-
|
59
|
+
# Check if the provided content_type is authorized and not spoofed against
|
60
|
+
# the file io.
|
61
|
+
def is_valid?(record, attribute, attachable)
|
62
|
+
authorized_content_type?(record, attribute, attachable) &&
|
63
|
+
not_spoofing_content_type?(record, attribute, attachable)
|
63
64
|
end
|
64
65
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
66
|
+
# Dead code that we keep here for some time, maybe we will find a solution
|
67
|
+
# to this check later? (November 2024)
|
68
|
+
#
|
69
|
+
# We do not perform any validations against the extension because it is an
|
70
|
+
# unreliable source of truth. For example, a `.csv` file could have its
|
71
|
+
# `text/csv` content_type changed to `application/vnd.ms-excel` because
|
72
|
+
# it had been opened by Excel at some point, making the file extension vs
|
73
|
+
# file content_type check invalid.
|
74
|
+
# def extension_matches_content_type?(record, attribute, attachable)
|
75
|
+
# return true if !@attachable_filename || !@attachable_content_type
|
76
|
+
|
77
|
+
# extension = @attachable_filename.split('.').last
|
78
|
+
# possible_extensions = Marcel::TYPE_EXTS[@attachable_content_type]
|
79
|
+
# return true if possible_extensions && extension.downcase.in?(possible_extensions)
|
80
|
+
|
81
|
+
# errors_options = initialize_and_populate_error_options(options, attachable)
|
82
|
+
# add_error(record, attribute, ERROR_TYPES.first, **errors_options)
|
83
|
+
# false
|
84
|
+
# end
|
85
|
+
|
86
|
+
def authorized_content_type?(record, attribute, attachable)
|
87
|
+
attachable_content_type_is_authorized = @authorized_content_types.any? do |authorized_content_type|
|
88
|
+
case authorized_content_type
|
89
|
+
when String then authorized_content_type == marcel_attachable_content_type(attachable)
|
90
|
+
when Regexp then authorized_content_type.match?(marcel_attachable_content_type(attachable).to_s)
|
71
91
|
end
|
72
92
|
end
|
73
93
|
|
74
|
-
if
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
errors_options[:content_type] = content_type(file)
|
80
|
-
add_error(record, attribute, ERROR_TYPES.first, **errors_options)
|
81
|
-
false
|
82
|
-
end
|
94
|
+
return true if attachable_content_type_is_authorized
|
95
|
+
|
96
|
+
errors_options = initialize_and_populate_error_options(options, attachable)
|
97
|
+
add_error(record, attribute, ERROR_TYPES.first, **errors_options)
|
98
|
+
false
|
83
99
|
end
|
84
100
|
|
85
|
-
def not_spoofing_content_type?(record, attribute,
|
101
|
+
def not_spoofing_content_type?(record, attribute, attachable)
|
86
102
|
return true unless enable_spoofing_protection?
|
87
103
|
|
88
|
-
if ContentTypeSpoofDetector.new(record, attribute,
|
89
|
-
errors_options = initialize_error_options(options,
|
104
|
+
if ContentTypeSpoofDetector.new(record, attribute, attachable).spoofed?
|
105
|
+
errors_options = initialize_error_options(options, attachable)
|
90
106
|
add_error(record, attribute, ERROR_TYPES.second, **errors_options)
|
91
107
|
false
|
92
108
|
else
|
@@ -94,6 +110,37 @@ module ActiveStorageValidations
|
|
94
110
|
end
|
95
111
|
end
|
96
112
|
|
113
|
+
def marcel_attachable_content_type(attachable)
|
114
|
+
Marcel::MimeType.for(declared_type: @attachable_content_type, name: @attachable_filename)
|
115
|
+
end
|
116
|
+
|
117
|
+
def enable_spoofing_protection?
|
118
|
+
options[:spoofing_protection] == true
|
119
|
+
end
|
120
|
+
|
121
|
+
def initialize_and_populate_error_options(options, attachable)
|
122
|
+
errors_options = initialize_error_options(options, attachable)
|
123
|
+
errors_options[:content_type] = @attachable_content_type
|
124
|
+
errors_options[:human_content_type] = content_type_to_human_format(@attachable_content_type)
|
125
|
+
errors_options[:authorized_types] = content_type_to_human_format(@authorized_content_types)
|
126
|
+
errors_options
|
127
|
+
end
|
128
|
+
|
129
|
+
def content_type_to_human_format(content_type)
|
130
|
+
Array(content_type)
|
131
|
+
.map do |content_type|
|
132
|
+
case content_type
|
133
|
+
when String, Symbol
|
134
|
+
content_type.to_s.match?(/\//) ? Marcel::TYPE_EXTS[content_type.to_s]&.first&.upcase : content_type.upcase
|
135
|
+
when Regexp
|
136
|
+
content_type.source
|
137
|
+
end
|
138
|
+
end
|
139
|
+
.flatten
|
140
|
+
.compact
|
141
|
+
.join(', ')
|
142
|
+
end
|
143
|
+
|
97
144
|
def ensure_exactly_one_validator_option
|
98
145
|
unless AVAILABLE_CHECKS.one? { |argument| options.key?(argument) }
|
99
146
|
raise ArgumentError, 'You must pass either :with or :in to the validator'
|
@@ -104,28 +151,39 @@ module ActiveStorageValidations
|
|
104
151
|
return true if options[:with]&.is_a?(Proc) || options[:in]&.is_a?(Proc)
|
105
152
|
|
106
153
|
([options[:with]] || options[:in]).each do |content_type|
|
107
|
-
raise ArgumentError,
|
154
|
+
raise ArgumentError, invalid_content_type_option_message(content_type) if invalid_option?(content_type)
|
108
155
|
end
|
109
156
|
end
|
110
157
|
|
111
|
-
def
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
158
|
+
def invalid_content_type_option_message(content_type)
|
159
|
+
if content_type.to_s.match?(/\//)
|
160
|
+
<<~ERROR_MESSAGE
|
161
|
+
You must pass valid content types to the validator
|
162
|
+
'#{content_type}' is not found in Marcel::TYPE_EXTS
|
163
|
+
ERROR_MESSAGE
|
164
|
+
else
|
165
|
+
<<~ERROR_MESSAGE
|
166
|
+
You must pass valid content types extensions to the validator
|
167
|
+
'#{content_type}' is not found in Marcel::EXTENSIONS
|
168
|
+
ERROR_MESSAGE
|
169
|
+
end
|
116
170
|
end
|
117
171
|
|
118
|
-
def
|
172
|
+
def invalid_option?(content_type)
|
119
173
|
case content_type
|
120
174
|
when String, Symbol
|
121
|
-
|
175
|
+
content_type.to_s.match?(/\//) ? invalid_content_type?(content_type) : invalid_extension?(content_type)
|
122
176
|
when Regexp
|
123
177
|
false # We always validate regexes
|
124
178
|
end
|
125
179
|
end
|
126
180
|
|
127
|
-
def
|
128
|
-
|
181
|
+
def invalid_content_type?(content_type)
|
182
|
+
Marcel::TYPE_EXTS[content_type.to_s] == nil
|
183
|
+
end
|
184
|
+
|
185
|
+
def invalid_extension?(content_type)
|
186
|
+
Marcel::MimeType.for(extension: content_type.to_s) == 'application/octet-stream'
|
129
187
|
end
|
130
188
|
end
|
131
189
|
end
|
@@ -1,17 +1,18 @@
|
|
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_attachable'
|
5
|
+
require_relative 'shared/asv_errorable'
|
6
|
+
require_relative 'shared/asv_optionable'
|
7
|
+
require_relative 'shared/asv_symbolizable'
|
7
8
|
|
8
9
|
module ActiveStorageValidations
|
9
10
|
class DimensionValidator < ActiveModel::EachValidator # :nodoc
|
10
|
-
include
|
11
|
-
include
|
12
|
-
include
|
13
|
-
include
|
14
|
-
include
|
11
|
+
include ASVActiveStorageable
|
12
|
+
include ASVAttachable
|
13
|
+
include ASVErrorable
|
14
|
+
include ASVOptionable
|
15
|
+
include ASVSymbolizable
|
15
16
|
|
16
17
|
AVAILABLE_CHECKS = %i[width height min max].freeze
|
17
18
|
ERROR_TYPES = %i[
|
@@ -122,7 +123,7 @@ module ActiveStorageValidations
|
|
122
123
|
end
|
123
124
|
|
124
125
|
def process_options(record)
|
125
|
-
flat_options =
|
126
|
+
flat_options = set_flat_options(record)
|
126
127
|
|
127
128
|
[:width, :height].each do |length|
|
128
129
|
if flat_options[length] and flat_options[length].is_a?(Hash)
|
@@ -1,15 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
5
|
-
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'
|
6
7
|
|
7
8
|
module ActiveStorageValidations
|
8
9
|
class LimitValidator < ActiveModel::EachValidator # :nodoc:
|
9
|
-
include
|
10
|
-
include
|
11
|
-
include
|
12
|
-
include
|
10
|
+
include ASVActiveStorageable
|
11
|
+
include ASVErrorable
|
12
|
+
include ASVOptionable
|
13
|
+
include ASVSymbolizable
|
13
14
|
|
14
15
|
AVAILABLE_CHECKS = %i[max min].freeze
|
15
16
|
ERROR_TYPES = %i[
|
@@ -23,7 +24,7 @@ module ActiveStorageValidations
|
|
23
24
|
|
24
25
|
def validate_each(record, attribute, _value)
|
25
26
|
files = attached_files(record, attribute).reject(&:blank?)
|
26
|
-
flat_options =
|
27
|
+
flat_options = set_flat_options(record)
|
27
28
|
|
28
29
|
return if files_count_valid?(files.count, flat_options)
|
29
30
|
|
@@ -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
|
@@ -15,13 +15,13 @@ module ActiveStorageValidations
|
|
15
15
|
end
|
16
16
|
|
17
17
|
class AspectRatioValidatorMatcher
|
18
|
-
include
|
19
|
-
include
|
20
|
-
include
|
21
|
-
include
|
22
|
-
include
|
23
|
-
include
|
24
|
-
include
|
18
|
+
include ASVActiveStorageable
|
19
|
+
include ASVAllowBlankable
|
20
|
+
include ASVAttachable
|
21
|
+
include ASVContextable
|
22
|
+
include ASVMessageable
|
23
|
+
include ASVRspecable
|
24
|
+
include ASVValidatable
|
25
25
|
|
26
26
|
def initialize(attribute_name)
|
27
27
|
initialize_allow_blankable
|
@@ -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_attachable'
|
5
|
+
require_relative 'shared/asv_contextable'
|
6
|
+
require_relative 'shared/asv_messageable'
|
7
|
+
require_relative 'shared/asv_rspecable'
|
8
|
+
require_relative 'shared/asv_validatable'
|
9
9
|
|
10
10
|
module ActiveStorageValidations
|
11
11
|
module Matchers
|
@@ -14,12 +14,12 @@ module ActiveStorageValidations
|
|
14
14
|
end
|
15
15
|
|
16
16
|
class AttachedValidatorMatcher
|
17
|
-
include
|
18
|
-
include
|
19
|
-
include
|
20
|
-
include
|
21
|
-
include
|
22
|
-
include
|
17
|
+
include ASVActiveStorageable
|
18
|
+
include ASVAttachable
|
19
|
+
include ASVContextable
|
20
|
+
include ASVMessageable
|
21
|
+
include ASVRspecable
|
22
|
+
include ASVValidatable
|
23
23
|
|
24
24
|
def initialize(attribute_name)
|
25
25
|
initialize_contextable
|
@@ -3,26 +3,26 @@
|
|
3
3
|
# Big thank you to the paperclip validation matchers:
|
4
4
|
# https://github.com/thoughtbot/paperclip/blob/v6.1.0/lib/paperclip/matchers/validate_attachment_size_matcher.rb
|
5
5
|
|
6
|
-
require_relative '
|
7
|
-
require_relative '
|
8
|
-
require_relative '
|
9
|
-
require_relative '
|
10
|
-
require_relative '
|
11
|
-
require_relative '
|
12
|
-
require_relative '
|
6
|
+
require_relative 'shared/asv_active_storageable'
|
7
|
+
require_relative 'shared/asv_allow_blankable'
|
8
|
+
require_relative 'shared/asv_attachable'
|
9
|
+
require_relative 'shared/asv_contextable'
|
10
|
+
require_relative 'shared/asv_messageable'
|
11
|
+
require_relative 'shared/asv_rspecable'
|
12
|
+
require_relative 'shared/asv_validatable'
|
13
13
|
|
14
14
|
module ActiveStorageValidations
|
15
15
|
module Matchers
|
16
16
|
class BaseSizeValidatorMatcher
|
17
17
|
# BaseSizeValidatorMatcher is an abstract class and shouldn't be instantiated directly.
|
18
18
|
|
19
|
-
include
|
20
|
-
include
|
21
|
-
include
|
22
|
-
include
|
23
|
-
include
|
24
|
-
include
|
25
|
-
include
|
19
|
+
include ASVActiveStorageable
|
20
|
+
include ASVAllowBlankable
|
21
|
+
include ASVAttachable
|
22
|
+
include ASVContextable
|
23
|
+
include ASVMessageable
|
24
|
+
include ASVRspecable
|
25
|
+
include ASVValidatable
|
26
26
|
|
27
27
|
def initialize(attribute_name)
|
28
28
|
initialize_allow_blankable
|
@@ -3,13 +3,13 @@
|
|
3
3
|
# Big thank you to the paperclip validation matchers:
|
4
4
|
# https://github.com/thoughtbot/paperclip/blob/v6.1.0/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb
|
5
5
|
|
6
|
-
require_relative '
|
7
|
-
require_relative '
|
8
|
-
require_relative '
|
9
|
-
require_relative '
|
10
|
-
require_relative '
|
11
|
-
require_relative '
|
12
|
-
require_relative '
|
6
|
+
require_relative 'shared/asv_active_storageable'
|
7
|
+
require_relative 'shared/asv_allow_blankable'
|
8
|
+
require_relative 'shared/asv_attachable'
|
9
|
+
require_relative 'shared/asv_contextable'
|
10
|
+
require_relative 'shared/asv_messageable'
|
11
|
+
require_relative 'shared/asv_rspecable'
|
12
|
+
require_relative 'shared/asv_validatable'
|
13
13
|
|
14
14
|
module ActiveStorageValidations
|
15
15
|
module Matchers
|
@@ -18,13 +18,13 @@ module ActiveStorageValidations
|
|
18
18
|
end
|
19
19
|
|
20
20
|
class ContentTypeValidatorMatcher
|
21
|
-
include
|
22
|
-
include
|
23
|
-
include
|
24
|
-
include
|
25
|
-
include
|
26
|
-
include
|
27
|
-
include
|
21
|
+
include ASVActiveStorageable
|
22
|
+
include ASVAllowBlankable
|
23
|
+
include ASVAttachable
|
24
|
+
include ASVContextable
|
25
|
+
include ASVMessageable
|
26
|
+
include ASVRspecable
|
27
|
+
include ASVValidatable
|
28
28
|
|
29
29
|
def initialize(attribute_name)
|
30
30
|
initialize_allow_blankable
|
@@ -32,7 +32,7 @@ module ActiveStorageValidations
|
|
32
32
|
initialize_messageable
|
33
33
|
initialize_rspecable
|
34
34
|
@attribute_name = attribute_name
|
35
|
-
@
|
35
|
+
@allowed_content_types = @rejected_content_types = []
|
36
36
|
end
|
37
37
|
|
38
38
|
def description
|
@@ -45,13 +45,13 @@ module ActiveStorageValidations
|
|
45
45
|
message.join("\n")
|
46
46
|
end
|
47
47
|
|
48
|
-
def allowing(*
|
49
|
-
@
|
48
|
+
def allowing(*content_types)
|
49
|
+
@allowed_content_types = content_types.flatten
|
50
50
|
self
|
51
51
|
end
|
52
52
|
|
53
|
-
def rejecting(*
|
54
|
-
@
|
53
|
+
def rejecting(*content_types)
|
54
|
+
@rejected_content_types = content_types.flatten
|
55
55
|
self
|
56
56
|
end
|
57
57
|
|
@@ -62,21 +62,21 @@ module ActiveStorageValidations
|
|
62
62
|
is_context_valid? &&
|
63
63
|
is_allowing_blank? &&
|
64
64
|
is_custom_message_valid? &&
|
65
|
-
|
66
|
-
|
65
|
+
all_allowed_content_types_allowed? &&
|
66
|
+
all_rejected_content_types_rejected?
|
67
67
|
end
|
68
68
|
|
69
69
|
protected
|
70
70
|
|
71
71
|
def build_failure_message(message)
|
72
|
-
if @
|
73
|
-
message << " the following content type#{'s' if @
|
74
|
-
message << " but #{pluralize(@
|
72
|
+
if @allowed_content_types_not_allowed.present?
|
73
|
+
message << " the following content type#{'s' if @allowed_content_types.count > 1} should be allowed: :#{@allowed_content_types.join(", :")}"
|
74
|
+
message << " but #{pluralize(@allowed_content_types_not_allowed)} rejected"
|
75
75
|
end
|
76
76
|
|
77
|
-
if @
|
78
|
-
message << " the following content type#{'s' if @
|
79
|
-
message << " but #{pluralize(@
|
77
|
+
if @rejected_content_types_not_rejected.present?
|
78
|
+
message << " the following content type#{'s' if @rejected_content_types.count > 1} should be rejected: :#{@rejected_content_types.join(", :")}"
|
79
|
+
message << " but #{pluralize(@rejected_content_types_not_rejected)} accepted"
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
@@ -88,25 +88,25 @@ module ActiveStorageValidations
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
-
def
|
92
|
-
@
|
93
|
-
@
|
91
|
+
def all_allowed_content_types_allowed?
|
92
|
+
@allowed_content_types_not_allowed ||= @allowed_content_types.reject { |type| type_allowed?(type) }
|
93
|
+
@allowed_content_types_not_allowed.empty?
|
94
94
|
end
|
95
95
|
|
96
|
-
def
|
97
|
-
@
|
98
|
-
@
|
96
|
+
def all_rejected_content_types_rejected?
|
97
|
+
@rejected_content_types_not_rejected ||= @rejected_content_types.select { |type| type_allowed?(type) }
|
98
|
+
@rejected_content_types_not_rejected.empty?
|
99
99
|
end
|
100
100
|
|
101
|
-
def type_allowed?(
|
102
|
-
|
101
|
+
def type_allowed?(content_type)
|
102
|
+
attach_file_with_content_type(content_type)
|
103
103
|
validate
|
104
104
|
detach_file
|
105
105
|
is_valid?
|
106
106
|
end
|
107
107
|
|
108
|
-
def
|
109
|
-
@subject.public_send(@attribute_name).attach(attachment_for(
|
108
|
+
def attach_file_with_content_type(content_type)
|
109
|
+
@subject.public_send(@attribute_name).attach(attachment_for(content_type))
|
110
110
|
end
|
111
111
|
|
112
112
|
def is_custom_message_valid?
|
@@ -121,13 +121,13 @@ module ActiveStorageValidations
|
|
121
121
|
@subject.public_send(@attribute_name).attach(attachment_for('fake/fake'))
|
122
122
|
end
|
123
123
|
|
124
|
-
def attachment_for(
|
125
|
-
suffix =
|
124
|
+
def attachment_for(content_type)
|
125
|
+
suffix = Marcel::TYPE_EXTS[content_type.to_s]&.first || 'fake'
|
126
126
|
|
127
127
|
{
|
128
128
|
io: Tempfile.new('.'),
|
129
129
|
filename: "test.#{suffix}",
|
130
|
-
content_type:
|
130
|
+
content_type: content_type
|
131
131
|
}
|
132
132
|
end
|
133
133
|
|
@@ -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
|
@@ -15,13 +15,13 @@ module ActiveStorageValidations
|
|
15
15
|
end
|
16
16
|
|
17
17
|
class DimensionValidatorMatcher
|
18
|
-
include
|
19
|
-
include
|
20
|
-
include
|
21
|
-
include
|
22
|
-
include
|
23
|
-
include
|
24
|
-
include
|
18
|
+
include ASVActiveStorageable
|
19
|
+
include ASVAllowBlankable
|
20
|
+
include ASVAttachable
|
21
|
+
include ASVContextable
|
22
|
+
include ASVMessageable
|
23
|
+
include ASVRspecable
|
24
|
+
include ASVValidatable
|
25
25
|
|
26
26
|
def initialize(attribute_name)
|
27
27
|
initialize_allow_blankable
|