active_storage_validations 1.0.4 → 3.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 +785 -245
- data/config/locales/da.yml +63 -0
- data/config/locales/de.yml +60 -19
- data/config/locales/en-GB.yml +63 -0
- data/config/locales/en.yml +60 -20
- data/config/locales/es.yml +60 -19
- data/config/locales/fr.yml +60 -19
- data/config/locales/it.yml +60 -19
- data/config/locales/ja.yml +60 -19
- data/config/locales/nl.yml +60 -19
- data/config/locales/pl.yml +60 -19
- data/config/locales/pt-BR.yml +60 -19
- data/config/locales/ru.yml +60 -19
- data/config/locales/sv.yml +63 -0
- data/config/locales/tr.yml +60 -19
- data/config/locales/uk.yml +60 -19
- data/config/locales/vi.yml +60 -19
- data/config/locales/zh-CN.yml +60 -19
- 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 +46 -0
- data/lib/active_storage_validations/analyzer/image_analyzer/vips.rb +56 -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/pdf_analyzer.rb +89 -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 +88 -0
- data/lib/active_storage_validations/aspect_ratio_validator.rb +157 -97
- data/lib/active_storage_validations/attached_validator.rb +22 -5
- data/lib/active_storage_validations/base_comparison_validator.rb +83 -0
- data/lib/active_storage_validations/content_type_validator.rb +219 -31
- data/lib/active_storage_validations/dimension_validator.rb +187 -97
- data/lib/active_storage_validations/duration_validator.rb +70 -0
- data/lib/active_storage_validations/extensors/asv_blob_metadatable.rb +56 -0
- data/lib/active_storage_validations/extensors/asv_marcelable.rb +12 -0
- data/lib/active_storage_validations/limit_validator.rb +76 -9
- 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 +150 -0
- data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +98 -39
- data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +93 -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/pages_validator_matcher.rb +39 -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 +57 -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 +17 -21
- data/lib/active_storage_validations/pages_validator.rb +61 -0
- data/lib/active_storage_validations/processable_file_validator.rb +37 -0
- data/lib/active_storage_validations/railtie.rb +14 -0
- data/lib/active_storage_validations/shared/asv_active_storageable.rb +30 -0
- data/lib/active_storage_validations/shared/asv_analyzable.rb +89 -0
- data/lib/active_storage_validations/shared/asv_attachable.rb +236 -0
- data/lib/active_storage_validations/shared/asv_errorable.rb +64 -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 -41
- data/lib/active_storage_validations/total_size_validator.rb +52 -0
- data/lib/active_storage_validations/version.rb +1 -1
- data/lib/active_storage_validations.rb +27 -13
- metadata +113 -31
- data/lib/active_storage_validations/metadata.rb +0 -151
- data/lib/active_storage_validations/option_proc_unfolding.rb +0 -16
- data/lib/active_storage_validations/processable_image_validator.rb +0 -43
|
@@ -1,14 +1,31 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "shared/asv_active_storageable"
|
|
4
|
+
require_relative "shared/asv_errorable"
|
|
5
|
+
require_relative "shared/asv_symbolizable"
|
|
6
|
+
|
|
3
7
|
module ActiveStorageValidations
|
|
4
8
|
class AttachedValidator < ActiveModel::EachValidator # :nodoc:
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
include ASVActiveStorageable
|
|
10
|
+
include ASVErrorable
|
|
11
|
+
include ASVSymbolizable
|
|
12
|
+
|
|
13
|
+
ERROR_TYPES = %i[blank].freeze
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
|
|
15
|
+
def check_validity!
|
|
16
|
+
%i[allow_nil allow_blank].each do |not_authorized_option|
|
|
17
|
+
if options.include?(not_authorized_option)
|
|
18
|
+
raise ArgumentError, "You cannot pass the :#{not_authorized_option} option to the #{self.class.to_sym} validator"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def validate_each(record, attribute, _value)
|
|
24
|
+
return if attachments_present?(record, attribute) &&
|
|
25
|
+
will_have_attachments_after_save?(record, attribute)
|
|
10
26
|
|
|
11
|
-
|
|
27
|
+
errors_options = initialize_error_options(options)
|
|
28
|
+
add_error(record, attribute, ERROR_TYPES.first, **errors_options)
|
|
12
29
|
end
|
|
13
30
|
end
|
|
14
31
|
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
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
|
+
|
|
8
|
+
module ActiveStorageValidations
|
|
9
|
+
class BaseComparisonValidator < ActiveModel::EachValidator # :nodoc:
|
|
10
|
+
include ASVActiveStorageable
|
|
11
|
+
include ASVErrorable
|
|
12
|
+
include ASVOptionable
|
|
13
|
+
include ASVSymbolizable
|
|
14
|
+
|
|
15
|
+
AVAILABLE_CHECKS = %i[
|
|
16
|
+
less_than
|
|
17
|
+
less_than_or_equal_to
|
|
18
|
+
greater_than
|
|
19
|
+
greater_than_or_equal_to
|
|
20
|
+
between
|
|
21
|
+
equal_to
|
|
22
|
+
].freeze
|
|
23
|
+
|
|
24
|
+
def initialize(*args)
|
|
25
|
+
if self.class == BaseComparisonValidator
|
|
26
|
+
raise NotImplementedError, "BaseComparisonValidator is an abstract class and cannot be instantiated directly."
|
|
27
|
+
end
|
|
28
|
+
super
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def check_validity!
|
|
32
|
+
unless AVAILABLE_CHECKS.one? { |argument| options.key?(argument) }
|
|
33
|
+
raise ArgumentError, "You must pass either :less_than(_or_equal_to), :greater_than(_or_equal_to), :between or :equal_to to the validator"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def validate_each(record, attribute, value)
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def is_valid?(value, flat_options)
|
|
44
|
+
return false if value < 0
|
|
45
|
+
|
|
46
|
+
if flat_options[:between].present?
|
|
47
|
+
flat_options[:between].include?(value)
|
|
48
|
+
elsif flat_options[:less_than].present?
|
|
49
|
+
value < flat_options[:less_than]
|
|
50
|
+
elsif flat_options[:less_than_or_equal_to].present?
|
|
51
|
+
value <= flat_options[:less_than_or_equal_to]
|
|
52
|
+
elsif flat_options[:greater_than].present?
|
|
53
|
+
value > flat_options[:greater_than]
|
|
54
|
+
elsif flat_options[:greater_than_or_equal_to].present?
|
|
55
|
+
value >= flat_options[:greater_than_or_equal_to]
|
|
56
|
+
elsif flat_options[:equal_to].present?
|
|
57
|
+
value == flat_options[:equal_to]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def populate_error_options(errors_options, flat_options)
|
|
62
|
+
errors_options[:min] = format_bound_value(min(flat_options))
|
|
63
|
+
errors_options[:exact] = format_bound_value(exact(flat_options))
|
|
64
|
+
errors_options[:max] = format_bound_value(max(flat_options))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def format_bound_value
|
|
68
|
+
raise NotImplementedError
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def min(flat_options)
|
|
72
|
+
flat_options[:between]&.min || flat_options[:greater_than] || flat_options[:greater_than_or_equal_to]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def exact(flat_options)
|
|
76
|
+
flat_options[:equal_to]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def max(flat_options)
|
|
80
|
+
flat_options[:between]&.max || flat_options[:less_than] || flat_options[:less_than_or_equal_to]
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -1,57 +1,245 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
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
|
+
|
|
3
11
|
module ActiveStorageValidations
|
|
4
12
|
class ContentTypeValidator < ActiveModel::EachValidator # :nodoc:
|
|
5
|
-
include
|
|
13
|
+
include ASVActiveStorageable
|
|
14
|
+
include ASVAnalyzable
|
|
15
|
+
include ASVAttachable
|
|
16
|
+
include ASVErrorable
|
|
17
|
+
include ASVOptionable
|
|
18
|
+
include ASVSymbolizable
|
|
6
19
|
|
|
7
20
|
AVAILABLE_CHECKS = %i[with in].freeze
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
21
|
+
ERROR_TYPES = %i[
|
|
22
|
+
content_type_invalid
|
|
23
|
+
content_type_spoofed
|
|
24
|
+
].freeze
|
|
25
|
+
METADATA_KEYS = %i[content_type].freeze
|
|
11
26
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
27
|
+
def check_validity!
|
|
28
|
+
ensure_exactly_one_validator_option
|
|
29
|
+
ensure_content_types_validity
|
|
30
|
+
end
|
|
16
31
|
|
|
17
|
-
|
|
18
|
-
|
|
32
|
+
def validate_each(record, attribute, _value)
|
|
33
|
+
return if no_attachments?(record, attribute)
|
|
19
34
|
|
|
20
|
-
|
|
21
|
-
|
|
35
|
+
@authorized_content_types = authorized_content_types_from_options(record)
|
|
36
|
+
return if @authorized_content_types.empty?
|
|
22
37
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
38
|
+
attachables_and_blobs(record, attribute).each do |attachable, blob|
|
|
39
|
+
set_attachable_cached_values(blob)
|
|
40
|
+
is_valid?(record, attribute, attachable, blob)
|
|
26
41
|
end
|
|
27
42
|
end
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def authorized_content_types_from_options(record)
|
|
47
|
+
flat_options = set_flat_options(record)
|
|
48
|
+
|
|
31
49
|
(Array.wrap(flat_options[:with]) + Array.wrap(flat_options[:in])).compact.map do |type|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
50
|
+
case type
|
|
51
|
+
when String, Symbol then Marcel::MimeType.for(declared_type: type.to_s, extension: type.to_s)
|
|
52
|
+
when Regexp then type
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def set_attachable_cached_values(blob)
|
|
58
|
+
@attachable_content_type = blob.content_type
|
|
59
|
+
@attachable_filename = blob.filename.to_s
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Check if the provided content_type is authorized and not spoofed against
|
|
63
|
+
# the file io.
|
|
64
|
+
def is_valid?(record, attribute, attachable, blob)
|
|
65
|
+
authorized_content_type?(record, attribute, attachable) &&
|
|
66
|
+
not_spoofing_content_type?(record, attribute, attachable, blob)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Dead code that we keep here for some time, maybe we will find a solution
|
|
70
|
+
# to this check later? (November 2024)
|
|
71
|
+
#
|
|
72
|
+
# We do not perform any validations against the extension because it is an
|
|
73
|
+
# unreliable source of truth. For example, a `.csv` file could have its
|
|
74
|
+
# `text/csv` content_type changed to `application/vnd.ms-excel` because
|
|
75
|
+
# it had been opened by Excel at some point, making the file extension vs
|
|
76
|
+
# file content_type check invalid.
|
|
77
|
+
# def extension_matches_content_type?(record, attribute, attachable)
|
|
78
|
+
# return true if !@attachable_filename || !@attachable_content_type
|
|
79
|
+
|
|
80
|
+
# extension = @attachable_filename.split('.').last
|
|
81
|
+
# possible_extensions = Marcel::TYPE_EXTS[@attachable_content_type]
|
|
82
|
+
# return true if possible_extensions && extension.downcase.in?(possible_extensions)
|
|
83
|
+
|
|
84
|
+
# errors_options = initialize_and_populate_error_options(options, attachable)
|
|
85
|
+
# add_error(record, attribute, ERROR_TYPES.first, **errors_options)
|
|
86
|
+
# false
|
|
87
|
+
# end
|
|
88
|
+
|
|
89
|
+
def authorized_content_type?(record, attribute, attachable)
|
|
90
|
+
attachable_content_type_is_authorized = @authorized_content_types.any? do |authorized_content_type|
|
|
91
|
+
case authorized_content_type
|
|
92
|
+
when String then authorized_content_type == marcel_attachable_content_type(attachable)
|
|
93
|
+
when Regexp then authorized_content_type.match?(marcel_attachable_content_type(attachable).to_s)
|
|
36
94
|
end
|
|
37
95
|
end
|
|
96
|
+
|
|
97
|
+
return true if attachable_content_type_is_authorized
|
|
98
|
+
|
|
99
|
+
add_content_type_invalid_error(record, attribute, attachable)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def marcel_attachable_content_type(attachable)
|
|
103
|
+
Marcel::MimeType.for(declared_type: @attachable_content_type, name: @attachable_filename)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def not_spoofing_content_type?(record, attribute, attachable, blob)
|
|
107
|
+
return true unless enable_spoofing_protection?
|
|
108
|
+
|
|
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
|
|
115
|
+
|
|
116
|
+
if attachable_content_type_vs_detected_content_type_mismatch?
|
|
117
|
+
add_content_type_spoofed_error(record, attribute, attachable, @detected_content_type)
|
|
118
|
+
else
|
|
119
|
+
true
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def disable_spoofing_protection?
|
|
124
|
+
!enable_spoofing_protection?
|
|
38
125
|
end
|
|
39
126
|
|
|
40
|
-
def
|
|
41
|
-
|
|
42
|
-
.map { |type| type.to_s.split('/').last.upcase }
|
|
43
|
-
.join(', ')
|
|
127
|
+
def enable_spoofing_protection?
|
|
128
|
+
options[:spoofing_protection] == true
|
|
44
129
|
end
|
|
45
130
|
|
|
46
|
-
def
|
|
47
|
-
|
|
131
|
+
def attachable_content_type_vs_detected_content_type_mismatch?
|
|
132
|
+
@attachable_content_type.present? &&
|
|
133
|
+
!attachable_content_type_intersects_detected_content_type?
|
|
48
134
|
end
|
|
49
135
|
|
|
50
|
-
def
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
136
|
+
def attachable_content_type_intersects_detected_content_type?
|
|
137
|
+
# Ruby intersects? method is only available from 3.1
|
|
138
|
+
enlarged_content_type(content_type_without_parameters(@attachable_content_type)).any? do |item|
|
|
139
|
+
enlarged_content_type(content_type_without_parameters(@detected_content_type)).include?(item)
|
|
54
140
|
end
|
|
55
141
|
end
|
|
142
|
+
|
|
143
|
+
def enlarged_content_type(content_type)
|
|
144
|
+
[ content_type, *parent_content_types(content_type) ].compact.uniq
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def parent_content_types(content_type)
|
|
148
|
+
Marcel::TYPE_PARENTS[content_type] || []
|
|
149
|
+
end
|
|
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
|
+
|
|
165
|
+
def initialize_and_populate_error_options(options, attachable)
|
|
166
|
+
errors_options = initialize_error_options(options, attachable)
|
|
167
|
+
errors_options[:content_type] = @attachable_content_type
|
|
168
|
+
errors_options[:human_content_type] = content_type_to_human_format(@attachable_content_type)
|
|
169
|
+
errors_options[:authorized_human_content_types] = content_type_to_human_format(@authorized_content_types)
|
|
170
|
+
errors_options[:count] = @authorized_content_types.size
|
|
171
|
+
errors_options
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def content_type_to_human_format(content_type)
|
|
175
|
+
Array(content_type)
|
|
176
|
+
.map do |content_type|
|
|
177
|
+
case content_type
|
|
178
|
+
when String, Symbol
|
|
179
|
+
content_type.to_s.match?(/\//) ? Marcel::TYPE_EXTS[content_type.to_s]&.first&.upcase : content_type.upcase
|
|
180
|
+
when Regexp
|
|
181
|
+
content_type.source
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
.flatten
|
|
185
|
+
.compact
|
|
186
|
+
.join(", ")
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def ensure_exactly_one_validator_option
|
|
190
|
+
unless AVAILABLE_CHECKS.one? { |argument| options.key?(argument) }
|
|
191
|
+
raise ArgumentError, "You must pass either :with or :in to the validator"
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def ensure_content_types_validity
|
|
196
|
+
return true if options[:with]&.is_a?(Proc) || options[:in]&.is_a?(Proc)
|
|
197
|
+
|
|
198
|
+
(Array(options[:with]) + Array(options[:in])).each do |content_type|
|
|
199
|
+
raise ArgumentError, invalid_content_type_option_message(content_type) if invalid_option?(content_type)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def invalid_content_type_option_message(content_type)
|
|
204
|
+
if content_type.to_s.match?(/\//)
|
|
205
|
+
<<~ERROR_MESSAGE
|
|
206
|
+
You must pass valid content types to the validator
|
|
207
|
+
'#{content_type}' is not found in Marcel content types (Marcel::TYPE_EXTS + Marcel::MAGIC)
|
|
208
|
+
ERROR_MESSAGE
|
|
209
|
+
else
|
|
210
|
+
<<~ERROR_MESSAGE
|
|
211
|
+
You must pass valid content types extensions to the validator
|
|
212
|
+
'#{content_type}' is not found in Marcel::EXTENSIONS
|
|
213
|
+
ERROR_MESSAGE
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def invalid_option?(content_type)
|
|
218
|
+
case content_type
|
|
219
|
+
when String, Symbol
|
|
220
|
+
content_type.to_s.match?(/\//) ? invalid_content_type?(content_type) : invalid_extension?(content_type)
|
|
221
|
+
when Regexp
|
|
222
|
+
false # We always validate regexes
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def invalid_content_type?(content_type)
|
|
227
|
+
if content_type == "image/jpg"
|
|
228
|
+
raise ArgumentError, "'image/jpg' is not a valid content type, you should use 'image/jpeg' instead"
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
all_available_marcel_content_types.exclude?(content_type.to_s)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def all_available_marcel_content_types
|
|
235
|
+
@all_available_marcel_content_types ||= Marcel::TYPE_EXTS
|
|
236
|
+
.keys
|
|
237
|
+
.push(*Marcel::MAGIC.map(&:first))
|
|
238
|
+
.tap(&:uniq!)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def invalid_extension?(content_type)
|
|
242
|
+
Marcel::MimeType.for(extension: content_type.to_s) == "application/octet-stream"
|
|
243
|
+
end
|
|
56
244
|
end
|
|
57
245
|
end
|