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.

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