active_storage_validations 1.3.0 → 1.3.4
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.
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
|