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.

Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -8
  3. data/config/locales/da.yml +0 -1
  4. data/config/locales/de.yml +0 -1
  5. data/config/locales/en.yml +0 -1
  6. data/config/locales/es.yml +0 -1
  7. data/config/locales/fr.yml +0 -1
  8. data/config/locales/it.yml +0 -1
  9. data/config/locales/ja.yml +0 -1
  10. data/config/locales/nl.yml +0 -1
  11. data/config/locales/pl.yml +0 -1
  12. data/config/locales/pt-BR.yml +0 -1
  13. data/config/locales/ru.yml +0 -1
  14. data/config/locales/sv.yml +0 -1
  15. data/config/locales/tr.yml +0 -1
  16. data/config/locales/uk.yml +0 -1
  17. data/config/locales/vi.yml +0 -1
  18. data/config/locales/zh-CN.yml +0 -1
  19. data/lib/active_storage_validations/aspect_ratio_validator.rb +61 -46
  20. data/lib/active_storage_validations/attached_validator.rb +6 -6
  21. data/lib/active_storage_validations/base_size_validator.rb +8 -7
  22. data/lib/active_storage_validations/content_type_spoof_detector.rb +13 -49
  23. data/lib/active_storage_validations/content_type_validator.rb +112 -54
  24. data/lib/active_storage_validations/dimension_validator.rb +11 -10
  25. data/lib/active_storage_validations/limit_validator.rb +9 -8
  26. data/lib/active_storage_validations/marcel_extensor.rb +2 -0
  27. data/lib/active_storage_validations/matchers/aspect_ratio_validator_matcher.rb +14 -14
  28. data/lib/active_storage_validations/matchers/attached_validator_matcher.rb +12 -12
  29. data/lib/active_storage_validations/matchers/base_size_validator_matcher.rb +14 -14
  30. data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +40 -40
  31. data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +14 -14
  32. data/lib/active_storage_validations/matchers/limit_validator_matcher.rb +14 -14
  33. data/lib/active_storage_validations/matchers/processable_image_validator_matcher.rb +14 -14
  34. data/lib/active_storage_validations/matchers/{concerns/active_storageable.rb → shared/asv_active_storageable.rb} +4 -2
  35. data/lib/active_storage_validations/matchers/{concerns/allow_blankable.rb → shared/asv_allow_blankable.rb} +4 -2
  36. data/lib/active_storage_validations/matchers/{concerns/attachable.rb → shared/asv_attachable.rb} +7 -1
  37. data/lib/active_storage_validations/matchers/{concerns/contextable.rb → shared/asv_contextable.rb} +4 -2
  38. data/lib/active_storage_validations/matchers/{concerns/messageable.rb → shared/asv_messageable.rb} +4 -2
  39. data/lib/active_storage_validations/matchers/{concerns/rspecable.rb → shared/asv_rspecable.rb} +4 -2
  40. data/lib/active_storage_validations/matchers/{concerns/validatable.rb → shared/asv_validatable.rb} +4 -2
  41. data/lib/active_storage_validations/metadata.rb +18 -19
  42. data/lib/active_storage_validations/processable_image_validator.rb +8 -8
  43. data/lib/active_storage_validations/{concerns/active_storageable.rb → shared/asv_active_storageable.rb} +4 -2
  44. data/lib/active_storage_validations/shared/asv_attachable.rb +136 -0
  45. data/lib/active_storage_validations/{concerns/errorable.rb → shared/asv_errorable.rb} +5 -2
  46. data/lib/active_storage_validations/{concerns/loggable.rb → shared/asv_loggable.rb} +3 -1
  47. data/lib/active_storage_validations/shared/asv_optionable.rb +29 -0
  48. data/lib/active_storage_validations/{concerns/symbolizable.rb → shared/asv_symbolizable.rb} +3 -1
  49. data/lib/active_storage_validations/size_validator.rb +2 -2
  50. data/lib/active_storage_validations/total_size_validator.rb +2 -2
  51. data/lib/active_storage_validations/version.rb +1 -1
  52. data/lib/active_storage_validations.rb +0 -1
  53. metadata +65 -25
  54. data/lib/active_storage_validations/concerns/metadatable.rb +0 -31
  55. 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 'concerns/active_storageable.rb'
4
- require_relative 'concerns/errorable.rb'
5
- require_relative 'concerns/symbolizable.rb'
6
- require_relative 'content_type_spoof_detector.rb'
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 ActiveStorageable
11
- include OptionProcUnfolding
12
- include Errorable
13
- include Symbolizable
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
- types = authorized_types(record)
30
- return if types.empty?
32
+ @authorized_content_types = authorized_content_types_from_options(record)
33
+ return if @authorized_content_types.empty?
31
34
 
32
- attached_files(record, attribute).each do |file|
33
- is_valid?(record, attribute, file, types)
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 authorized_types(record)
40
- flat_options = unfold_procs(record, self.options, AVAILABLE_CHECKS)
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 types_to_human_format(types)
50
- types
51
- .map { |type| type.is_a?(Regexp) ? type.source : type.to_s.split('/').last.upcase }
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
- def is_valid?(record, attribute, file, types)
61
- file_type_in_authorized_types?(record, attribute, file, types) &&
62
- not_spoofing_content_type?(record, attribute, file)
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
- def file_type_in_authorized_types?(record, attribute, file, types)
66
- file_type = content_type(file)
67
- file_type_is_authorized = types.any? do |type|
68
- case type
69
- when String then type == file_type
70
- when Regexp then type.match?(file_type.to_s)
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 file_type_is_authorized
75
- true
76
- else
77
- errors_options = initialize_error_options(options, file)
78
- errors_options[:authorized_types] = types_to_human_format(types)
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, file)
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, file).spoofed?
89
- errors_options = initialize_error_options(options, file)
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, invalid_content_type_message(content_type) if invalid_content_type?(content_type)
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 invalid_content_type_message(content_type)
112
- <<~ERROR_MESSAGE
113
- You must pass valid content types to the validator
114
- '#{content_type}' is not found in Marcel::EXTENSIONS mimes
115
- ERROR_MESSAGE
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 invalid_content_type?(content_type)
172
+ def invalid_option?(content_type)
119
173
  case content_type
120
174
  when String, Symbol
121
- Marcel::MimeType.for(declared_type: content_type.to_s, extension: content_type.to_s) == 'application/octet-stream'
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 enable_spoofing_protection?
128
- options[:spoofing_protection] == true
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 'concerns/active_storageable.rb'
4
- require_relative 'concerns/errorable.rb'
5
- require_relative 'concerns/metadatable.rb'
6
- require_relative 'concerns/symbolizable.rb'
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 ActiveStorageable
11
- include Errorable
12
- include OptionProcUnfolding
13
- include Metadatable
14
- include Symbolizable
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 = unfold_procs(record, self.options, AVAILABLE_CHECKS)
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 'concerns/active_storageable.rb'
4
- require_relative 'concerns/errorable.rb'
5
- require_relative 'concerns/symbolizable.rb'
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 ActiveStorageable
10
- include OptionProcUnfolding
11
- include Errorable
12
- include Symbolizable
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 = unfold_procs(record, self.options, AVAILABLE_CHECKS)
27
+ flat_options = set_flat_options(record)
27
28
 
28
29
  return if files_count_valid?(files.count, flat_options)
29
30
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Marcel::MimeType.extend "application/x-rar-compressed", parents: %(application/x-rar)
2
4
  Marcel::MimeType.extend "audio/x-hx-aac-adts", parents: %(audio/x-aac)
3
5
  Marcel::MimeType.extend "audio/x-m4a", parents: %(audio/mp4)
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'concerns/active_storageable.rb'
4
- require_relative 'concerns/allow_blankable.rb'
5
- require_relative 'concerns/attachable.rb'
6
- require_relative 'concerns/contextable.rb'
7
- require_relative 'concerns/messageable.rb'
8
- require_relative 'concerns/rspecable.rb'
9
- require_relative 'concerns/validatable.rb'
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 ActiveStorageable
19
- include AllowBlankable
20
- include Attachable
21
- include Contextable
22
- include Messageable
23
- include Rspecable
24
- include Validatable
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 'concerns/active_storageable.rb'
4
- require_relative 'concerns/attachable.rb'
5
- require_relative 'concerns/contextable.rb'
6
- require_relative 'concerns/messageable.rb'
7
- require_relative 'concerns/rspecable.rb'
8
- require_relative 'concerns/validatable.rb'
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 ActiveStorageable
18
- include Attachable
19
- include Contextable
20
- include Messageable
21
- include Rspecable
22
- include Validatable
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 'concerns/active_storageable.rb'
7
- require_relative 'concerns/allow_blankable.rb'
8
- require_relative 'concerns/attachable.rb'
9
- require_relative 'concerns/contextable.rb'
10
- require_relative 'concerns/messageable.rb'
11
- require_relative 'concerns/rspecable.rb'
12
- require_relative 'concerns/validatable.rb'
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 ActiveStorageable
20
- include AllowBlankable
21
- include Attachable
22
- include Contextable
23
- include Messageable
24
- include Rspecable
25
- include Validatable
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 'concerns/active_storageable.rb'
7
- require_relative 'concerns/allow_blankable.rb'
8
- require_relative 'concerns/attachable.rb'
9
- require_relative 'concerns/contextable.rb'
10
- require_relative 'concerns/messageable.rb'
11
- require_relative 'concerns/rspecable.rb'
12
- require_relative 'concerns/validatable.rb'
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 ActiveStorageable
22
- include AllowBlankable
23
- include Attachable
24
- include Contextable
25
- include Messageable
26
- include Rspecable
27
- include Validatable
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
- @allowed_types = @rejected_types = []
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(*types)
49
- @allowed_types = types.flatten
48
+ def allowing(*content_types)
49
+ @allowed_content_types = content_types.flatten
50
50
  self
51
51
  end
52
52
 
53
- def rejecting(*types)
54
- @rejected_types = types.flatten
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
- all_allowed_types_allowed? &&
66
- all_rejected_types_rejected?
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 @allowed_types_not_allowed.present?
73
- message << " the following content type#{'s' if @allowed_types.count > 1} should be allowed: :#{@allowed_types.join(", :")}"
74
- message << " but #{pluralize(@allowed_types_not_allowed)} rejected"
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 @rejected_types_not_rejected.present?
78
- message << " the following content type#{'s' if @rejected_types.count > 1} should be rejected: :#{@rejected_types.join(", :")}"
79
- message << " but #{pluralize(@rejected_types_not_rejected)} accepted"
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 all_allowed_types_allowed?
92
- @allowed_types_not_allowed ||= @allowed_types.reject { |type| type_allowed?(type) }
93
- @allowed_types_not_allowed.empty?
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 all_rejected_types_rejected?
97
- @rejected_types_not_rejected ||= @rejected_types.select { |type| type_allowed?(type) }
98
- @rejected_types_not_rejected.empty?
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?(type)
102
- attach_file_of_type(type)
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 attach_file_of_type(type)
109
- @subject.public_send(@attribute_name).attach(attachment_for(type))
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(type)
125
- suffix = type.to_s.split('/').last
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: 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 'concerns/active_storageable.rb'
4
- require_relative 'concerns/allow_blankable.rb'
5
- require_relative 'concerns/attachable'
6
- require_relative 'concerns/contextable.rb'
7
- require_relative 'concerns/messageable.rb'
8
- require_relative 'concerns/rspecable.rb'
9
- require_relative 'concerns/validatable.rb'
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 ActiveStorageable
19
- include AllowBlankable
20
- include Attachable
21
- include Contextable
22
- include Messageable
23
- include Rspecable
24
- include Validatable
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