active_storage_validations 3.0.5 → 4.1.0

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +227 -116
  4. data/Rakefile +13 -15
  5. data/config/locales/da.yml +2 -0
  6. data/config/locales/de.yml +2 -0
  7. data/config/locales/en-GB.yml +2 -0
  8. data/config/locales/en.yml +2 -0
  9. data/config/locales/es.yml +2 -0
  10. data/config/locales/fr.yml +2 -0
  11. data/config/locales/it.yml +2 -0
  12. data/config/locales/ja.yml +2 -0
  13. data/config/locales/nl.yml +2 -0
  14. data/config/locales/pl.yml +2 -0
  15. data/config/locales/pt-BR.yml +2 -0
  16. data/config/locales/ru.yml +2 -0
  17. data/config/locales/sv.yml +2 -0
  18. data/config/locales/tr.yml +2 -0
  19. data/config/locales/uk.yml +2 -0
  20. data/config/locales/vi.yml +2 -0
  21. data/config/locales/zh-CN.yml +2 -0
  22. data/lib/active_storage_validations/analyzer/audio_analyzer.rb +0 -1
  23. data/lib/active_storage_validations/analyzer/content_type_analyzer/file.rb +31 -0
  24. data/lib/active_storage_validations/analyzer/content_type_analyzer/magika.rb +44 -0
  25. data/lib/active_storage_validations/analyzer/content_type_analyzer.rb +15 -24
  26. data/lib/active_storage_validations/analyzer/image_analyzer/image_magick.rb +41 -7
  27. data/lib/active_storage_validations/analyzer/image_analyzer/vips.rb +36 -10
  28. data/lib/active_storage_validations/analyzer/pdf_analyzer.rb +3 -9
  29. data/lib/active_storage_validations/analyzer/shared/asv_ff_probable.rb +5 -4
  30. data/lib/active_storage_validations/analyzer/video_analyzer.rb +0 -1
  31. data/lib/active_storage_validations/analyzer.rb +35 -3
  32. data/lib/active_storage_validations/content_type_validator.rb +24 -6
  33. data/lib/active_storage_validations/dimension_validator.rb +4 -2
  34. data/lib/active_storage_validations/form_builder.rb +43 -0
  35. data/lib/active_storage_validations/matchers/aspect_ratio_validator_matcher.rb +8 -0
  36. data/lib/active_storage_validations/matchers/attached_validator_matcher.rb +4 -0
  37. data/lib/active_storage_validations/matchers/base_comparison_validator_matcher.rb +11 -1
  38. data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +12 -0
  39. data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +8 -0
  40. data/lib/active_storage_validations/matchers/duration_validator_matcher.rb +8 -0
  41. data/lib/active_storage_validations/matchers/limit_validator_matcher.rb +4 -0
  42. data/lib/active_storage_validations/matchers/pages_validator_matcher.rb +8 -0
  43. data/lib/active_storage_validations/matchers/processable_file_validator_matcher.rb +8 -0
  44. data/lib/active_storage_validations/matchers/shared/asv_except_onable.rb +57 -0
  45. data/lib/active_storage_validations/matchers/shared/asv_spoofing_protectable.rb +44 -0
  46. data/lib/active_storage_validations/matchers/shared/asv_timeoutable.rb +32 -0
  47. data/lib/active_storage_validations/matchers/with_audio_validator_matcher.rb +114 -0
  48. data/lib/active_storage_validations/matchers.rb +10 -1
  49. data/lib/active_storage_validations/processable_file_validator.rb +60 -1
  50. data/lib/active_storage_validations/railtie.rb +6 -0
  51. data/lib/active_storage_validations/shared/asv_analyzable.rb +66 -8
  52. data/lib/active_storage_validations/shared/asv_attachable.rb +9 -10
  53. data/lib/active_storage_validations/shared/asv_commandable.rb +95 -0
  54. data/lib/active_storage_validations/shared/asv_optionable.rb +5 -1
  55. data/lib/active_storage_validations/version.rb +1 -1
  56. data/lib/active_storage_validations/with_audio_validator.rb +37 -0
  57. data/lib/active_storage_validations.rb +23 -0
  58. metadata +27 -58
@@ -6,7 +6,6 @@ require_relative "shared/asv_attachable"
6
6
  require_relative "shared/asv_errorable"
7
7
  require_relative "shared/asv_optionable"
8
8
  require_relative "shared/asv_symbolizable"
9
- require_relative "analyzer/content_type_analyzer"
10
9
 
11
10
  module ActiveStorageValidations
12
11
  class ContentTypeValidator < ActiveModel::EachValidator # :nodoc:
@@ -18,6 +17,7 @@ module ActiveStorageValidations
18
17
  include ASVSymbolizable
19
18
 
20
19
  AVAILABLE_CHECKS = %i[with in].freeze
20
+ AVAILABLE_SPOOFING_PROTECTION_VALUES = [ true, false, :file, :magika ].freeze
21
21
  ERROR_TYPES = %i[
22
22
  content_type_invalid
23
23
  content_type_spoofed
@@ -27,6 +27,7 @@ module ActiveStorageValidations
27
27
  def check_validity!
28
28
  ensure_exactly_one_validator_option
29
29
  ensure_content_types_validity
30
+ ensure_spoofing_protection_validity
30
31
  end
31
32
 
32
33
  def validate_each(record, attribute, _value)
@@ -125,7 +126,25 @@ module ActiveStorageValidations
125
126
  end
126
127
 
127
128
  def enable_spoofing_protection?
128
- options[:spoofing_protection] == true
129
+ spoofing_protection_backend.present?
130
+ end
131
+
132
+ # +true+ and +:file+ both select the UNIX +file+ CLI; +:magika+ selects Magika.
133
+ def spoofing_protection_backend
134
+ case options[:spoofing_protection]
135
+ when true, :file then :file
136
+ when :magika then :magika
137
+ end
138
+ end
139
+
140
+ def ensure_spoofing_protection_validity
141
+ return unless options.key?(:spoofing_protection)
142
+ return if AVAILABLE_SPOOFING_PROTECTION_VALUES.include?(options[:spoofing_protection])
143
+
144
+ raise ArgumentError, <<~ERROR_MESSAGE
145
+ Unknown spoofing_protection option: #{options[:spoofing_protection].inspect}.
146
+ Allowed values: #{AVAILABLE_SPOOFING_PROTECTION_VALUES.map(&:inspect).join(", ")}
147
+ ERROR_MESSAGE
129
148
  end
130
149
 
131
150
  def attachable_content_type_vs_detected_content_type_mismatch?
@@ -134,10 +153,9 @@ module ActiveStorageValidations
134
153
  end
135
154
 
136
155
  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)
140
- end
156
+ enlarged_content_type(content_type_without_parameters(@attachable_content_type)).intersect?(
157
+ enlarged_content_type(content_type_without_parameters(@detected_content_type))
158
+ )
141
159
  end
142
160
 
143
161
  def enlarged_content_type(content_type)
@@ -215,8 +215,10 @@ module ActiveStorageValidations
215
215
 
216
216
  %i[min max].each do |bound|
217
217
  if (range = flat_options[bound])
218
- flat_options[:width] = { bound => range.first }
219
- flat_options[:height] = { bound => range.last }
218
+ flat_options[:width] ||= {}
219
+ flat_options[:height] ||= {}
220
+ flat_options[:width][bound] = range.first
221
+ flat_options[:height][bound] = range.last
220
222
  end
221
223
  end
222
224
 
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveStorageValidations
4
+ module FormBuilder
5
+ def file_field(method, options = {})
6
+ options = options.symbolize_keys
7
+ infer_accept = options.delete(:infer_accept)
8
+ infer_accept = ActiveStorageValidations.infer_file_field_accept if infer_accept.nil?
9
+
10
+ if infer_accept && !options.key?(:accept)
11
+ accept = inferred_accept_types(method)
12
+ options[:accept] = accept.join(",") if accept.any?
13
+ end
14
+
15
+ super(method, options)
16
+ end
17
+
18
+ private
19
+
20
+ def inferred_accept_types(method)
21
+ return [] unless @object.class.respond_to?(:validators_on)
22
+
23
+ content_type_validators = @object.class.validators_on(method).select do |v|
24
+ v.is_a?(ActiveStorageValidations::ContentTypeValidator)
25
+ end
26
+
27
+ content_type_validators.flat_map do |validator|
28
+ types = Array(validator.options[:with]) + Array(validator.options[:in])
29
+ types.filter_map do |type|
30
+ case type
31
+ when String
32
+ type.include?("/") ? type : Marcel::MimeType.for(declared_type: type, extension: type)
33
+ when Symbol
34
+ Marcel::MimeType.for(declared_type: type.to_s, extension: type.to_s)
35
+ when Regexp
36
+ match = type.source.match(%r{\A\\A([a-z]+)/\.\*\\z\z})
37
+ "#{match[1]}/*" if match
38
+ end
39
+ end
40
+ end.uniq
41
+ end
42
+ end
43
+ end
@@ -4,8 +4,10 @@ require_relative "shared/asv_active_storageable"
4
4
  require_relative "shared/asv_allow_blankable"
5
5
  require_relative "shared/asv_attachable"
6
6
  require_relative "shared/asv_contextable"
7
+ require_relative "shared/asv_except_onable"
7
8
  require_relative "shared/asv_messageable"
8
9
  require_relative "shared/asv_rspecable"
10
+ require_relative "shared/asv_timeoutable"
9
11
  require_relative "shared/asv_validatable"
10
12
 
11
13
  module ActiveStorageValidations
@@ -19,15 +21,19 @@ module ActiveStorageValidations
19
21
  include ASVAllowBlankable
20
22
  include ASVAttachable
21
23
  include ASVContextable
24
+ include ASVExceptOnable
22
25
  include ASVMessageable
23
26
  include ASVRspecable
27
+ include ASVTimeoutable
24
28
  include ASVValidatable
25
29
 
26
30
  def initialize(attribute_name)
27
31
  initialize_allow_blankable
28
32
  initialize_contextable
33
+ initialize_except_onable
29
34
  initialize_messageable
30
35
  initialize_rspecable
36
+ initialize_timeoutable
31
37
  @attribute_name = attribute_name
32
38
  @allowed_aspect_ratios = @rejected_aspect_ratios = []
33
39
  end
@@ -55,7 +61,9 @@ module ActiveStorageValidations
55
61
 
56
62
  is_a_valid_active_storage_attribute? &&
57
63
  is_context_valid? &&
64
+ is_except_on_valid? &&
58
65
  is_allowing_blank? &&
66
+ is_timeout_valid? &&
59
67
  is_custom_message_valid? &&
60
68
  all_allowed_aspect_ratios_allowed? &&
61
69
  all_rejected_aspect_ratios_rejected?
@@ -3,6 +3,7 @@
3
3
  require_relative "shared/asv_active_storageable"
4
4
  require_relative "shared/asv_attachable"
5
5
  require_relative "shared/asv_contextable"
6
+ require_relative "shared/asv_except_onable"
6
7
  require_relative "shared/asv_messageable"
7
8
  require_relative "shared/asv_rspecable"
8
9
  require_relative "shared/asv_validatable"
@@ -17,12 +18,14 @@ module ActiveStorageValidations
17
18
  include ASVActiveStorageable
18
19
  include ASVAttachable
19
20
  include ASVContextable
21
+ include ASVExceptOnable
20
22
  include ASVMessageable
21
23
  include ASVRspecable
22
24
  include ASVValidatable
23
25
 
24
26
  def initialize(attribute_name)
25
27
  initialize_contextable
28
+ initialize_except_onable
26
29
  initialize_messageable
27
30
  initialize_rspecable
28
31
  @attribute_name = attribute_name
@@ -41,6 +44,7 @@ module ActiveStorageValidations
41
44
 
42
45
  is_a_valid_active_storage_attribute? &&
43
46
  is_context_valid? &&
47
+ is_except_on_valid? &&
44
48
  is_custom_message_valid? &&
45
49
  is_valid_when_file_attached? &&
46
50
  is_invalid_when_file_not_attached?
@@ -7,6 +7,7 @@ require_relative "shared/asv_active_storageable"
7
7
  require_relative "shared/asv_allow_blankable"
8
8
  require_relative "shared/asv_attachable"
9
9
  require_relative "shared/asv_contextable"
10
+ require_relative "shared/asv_except_onable"
10
11
  require_relative "shared/asv_messageable"
11
12
  require_relative "shared/asv_rspecable"
12
13
  require_relative "shared/asv_validatable"
@@ -20,6 +21,7 @@ module ActiveStorageValidations
20
21
  include ASVAllowBlankable
21
22
  include ASVAttachable
22
23
  include ASVContextable
24
+ include ASVExceptOnable
23
25
  include ASVMessageable
24
26
  include ASVRspecable
25
27
  include ASVValidatable
@@ -27,6 +29,7 @@ module ActiveStorageValidations
27
29
  def initialize(attribute_name)
28
30
  initialize_allow_blankable
29
31
  initialize_contextable
32
+ initialize_except_onable
30
33
  initialize_messageable
31
34
  initialize_rspecable
32
35
  @attribute_name = attribute_name
@@ -68,17 +71,24 @@ module ActiveStorageValidations
68
71
 
69
72
  is_a_valid_active_storage_attribute? &&
70
73
  is_context_valid? &&
74
+ is_except_on_valid? &&
71
75
  is_allowing_blank? &&
72
76
  is_custom_message_valid? &&
73
77
  not_lower_than_min? &&
74
78
  higher_than_min? &&
75
79
  lower_than_max? &&
76
80
  not_higher_than_max? &&
77
- equal_to_exact?
81
+ equal_to_exact? &&
82
+ is_timeout_valid?
78
83
  end
79
84
 
80
85
  protected
81
86
 
87
+ # Hook for ASVTimeoutable (Duration / Pages). Size / TotalSize leave this as-is.
88
+ def is_timeout_valid?
89
+ true
90
+ end
91
+
82
92
  def build_failure_message(message)
83
93
  return unless @failure_message_artefacts.present?
84
94
 
@@ -7,8 +7,11 @@ require_relative "shared/asv_active_storageable"
7
7
  require_relative "shared/asv_allow_blankable"
8
8
  require_relative "shared/asv_attachable"
9
9
  require_relative "shared/asv_contextable"
10
+ require_relative "shared/asv_except_onable"
10
11
  require_relative "shared/asv_messageable"
11
12
  require_relative "shared/asv_rspecable"
13
+ require_relative "shared/asv_spoofing_protectable"
14
+ require_relative "shared/asv_timeoutable"
12
15
  require_relative "shared/asv_validatable"
13
16
 
14
17
  module ActiveStorageValidations
@@ -22,15 +25,21 @@ module ActiveStorageValidations
22
25
  include ASVAllowBlankable
23
26
  include ASVAttachable
24
27
  include ASVContextable
28
+ include ASVExceptOnable
25
29
  include ASVMessageable
26
30
  include ASVRspecable
31
+ include ASVSpoofingProtectable
32
+ include ASVTimeoutable
27
33
  include ASVValidatable
28
34
 
29
35
  def initialize(attribute_name)
30
36
  initialize_allow_blankable
31
37
  initialize_contextable
38
+ initialize_except_onable
32
39
  initialize_messageable
33
40
  initialize_rspecable
41
+ initialize_spoofing_protectable
42
+ initialize_timeoutable
34
43
  @attribute_name = attribute_name
35
44
  @allowed_content_types = @rejected_content_types = []
36
45
  end
@@ -62,7 +71,10 @@ module ActiveStorageValidations
62
71
 
63
72
  is_a_valid_active_storage_attribute? &&
64
73
  is_context_valid? &&
74
+ is_except_on_valid? &&
65
75
  is_allowing_blank? &&
76
+ is_timeout_valid? &&
77
+ is_spoofing_protection_valid? &&
66
78
  is_custom_message_valid? &&
67
79
  all_allowed_content_types_allowed? &&
68
80
  all_rejected_content_types_rejected?
@@ -4,8 +4,10 @@ require_relative "shared/asv_active_storageable"
4
4
  require_relative "shared/asv_allow_blankable"
5
5
  require_relative "shared/asv_attachable"
6
6
  require_relative "shared/asv_contextable"
7
+ require_relative "shared/asv_except_onable"
7
8
  require_relative "shared/asv_messageable"
8
9
  require_relative "shared/asv_rspecable"
10
+ require_relative "shared/asv_timeoutable"
9
11
  require_relative "shared/asv_validatable"
10
12
 
11
13
  module ActiveStorageValidations
@@ -19,15 +21,19 @@ module ActiveStorageValidations
19
21
  include ASVAllowBlankable
20
22
  include ASVAttachable
21
23
  include ASVContextable
24
+ include ASVExceptOnable
22
25
  include ASVMessageable
23
26
  include ASVRspecable
27
+ include ASVTimeoutable
24
28
  include ASVValidatable
25
29
 
26
30
  def initialize(attribute_name)
27
31
  initialize_allow_blankable
28
32
  initialize_contextable
33
+ initialize_except_onable
29
34
  initialize_messageable
30
35
  initialize_rspecable
36
+ initialize_timeoutable
31
37
  @attribute_name = attribute_name
32
38
  @width_min = @width_max = @height_min = @height_max = nil
33
39
  end
@@ -87,7 +93,9 @@ module ActiveStorageValidations
87
93
 
88
94
  is_a_valid_active_storage_attribute? &&
89
95
  is_context_valid? &&
96
+ is_except_on_valid? &&
90
97
  is_allowing_blank? &&
98
+ is_timeout_valid? &&
91
99
  is_custom_message_valid? &&
92
100
  width_not_smaller_than_min? &&
93
101
  width_larger_than_min? &&
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "base_comparison_validator_matcher"
4
+ require_relative "shared/asv_timeoutable"
4
5
 
5
6
  module ActiveStorageValidations
6
7
  module Matchers
@@ -9,6 +10,13 @@ module ActiveStorageValidations
9
10
  end
10
11
 
11
12
  class DurationValidatorMatcher < BaseComparisonValidatorMatcher
13
+ include ASVTimeoutable
14
+
15
+ def initialize(attribute_name)
16
+ super
17
+ initialize_timeoutable
18
+ end
19
+
12
20
  def description
13
21
  "validate file duration of :#{@attribute_name}"
14
22
  end
@@ -4,6 +4,7 @@ require_relative "shared/asv_active_storageable"
4
4
  require_relative "shared/asv_allow_blankable"
5
5
  require_relative "shared/asv_attachable"
6
6
  require_relative "shared/asv_contextable"
7
+ require_relative "shared/asv_except_onable"
7
8
  require_relative "shared/asv_messageable"
8
9
  require_relative "shared/asv_rspecable"
9
10
  require_relative "shared/asv_validatable"
@@ -19,6 +20,7 @@ module ActiveStorageValidations
19
20
  include ASVAllowBlankable
20
21
  include ASVAttachable
21
22
  include ASVContextable
23
+ include ASVExceptOnable
22
24
  include ASVMessageable
23
25
  include ASVRspecable
24
26
  include ASVValidatable
@@ -26,6 +28,7 @@ module ActiveStorageValidations
26
28
  def initialize(attribute_name)
27
29
  initialize_allow_blankable
28
30
  initialize_contextable
31
+ initialize_except_onable
29
32
  initialize_messageable
30
33
  initialize_rspecable
31
34
  @attribute_name = attribute_name
@@ -57,6 +60,7 @@ module ActiveStorageValidations
57
60
 
58
61
  is_a_valid_active_storage_attribute? &&
59
62
  is_context_valid? &&
63
+ is_except_on_valid? &&
60
64
  is_custom_message_valid? &&
61
65
  file_count_not_smaller_than_min? &&
62
66
  file_count_equal_min? &&
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "base_comparison_validator_matcher"
4
+ require_relative "shared/asv_timeoutable"
4
5
 
5
6
  module ActiveStorageValidations
6
7
  module Matchers
@@ -9,6 +10,13 @@ module ActiveStorageValidations
9
10
  end
10
11
 
11
12
  class PagesValidatorMatcher < BaseComparisonValidatorMatcher
13
+ include ASVTimeoutable
14
+
15
+ def initialize(attribute_name)
16
+ super
17
+ initialize_timeoutable
18
+ end
19
+
12
20
  def description
13
21
  "validate file number of pages of :#{@attribute_name}"
14
22
  end
@@ -4,8 +4,10 @@ require_relative "shared/asv_active_storageable"
4
4
  require_relative "shared/asv_allow_blankable"
5
5
  require_relative "shared/asv_attachable"
6
6
  require_relative "shared/asv_contextable"
7
+ require_relative "shared/asv_except_onable"
7
8
  require_relative "shared/asv_messageable"
8
9
  require_relative "shared/asv_rspecable"
10
+ require_relative "shared/asv_timeoutable"
9
11
  require_relative "shared/asv_validatable"
10
12
 
11
13
  module ActiveStorageValidations
@@ -19,15 +21,19 @@ module ActiveStorageValidations
19
21
  include ASVAllowBlankable
20
22
  include ASVAttachable
21
23
  include ASVContextable
24
+ include ASVExceptOnable
22
25
  include ASVMessageable
23
26
  include ASVRspecable
27
+ include ASVTimeoutable
24
28
  include ASVValidatable
25
29
 
26
30
  def initialize(attribute_name)
27
31
  initialize_allow_blankable
28
32
  initialize_contextable
33
+ initialize_except_onable
29
34
  initialize_messageable
30
35
  initialize_rspecable
36
+ initialize_timeoutable
31
37
  @attribute_name = attribute_name
32
38
  end
33
39
 
@@ -44,6 +50,8 @@ module ActiveStorageValidations
44
50
 
45
51
  is_a_valid_active_storage_attribute? &&
46
52
  is_context_valid? &&
53
+ is_except_on_valid? &&
54
+ is_timeout_valid? &&
47
55
  is_custom_message_valid? &&
48
56
  is_valid_when_image_processable? &&
49
57
  is_invalid_when_file_not_processable?
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module ActiveStorageValidations
6
+ module Matchers
7
+ module ASVExceptOnable
8
+ extend ActiveSupport::Concern
9
+
10
+ def initialize_except_onable
11
+ @except_on = nil
12
+ end
13
+
14
+ def except_on(context)
15
+ @except_on = context
16
+ self
17
+ end
18
+
19
+ private
20
+
21
+ def is_except_on_valid?
22
+ return true if !@except_on && attribute_validators.none? { |validator| validator.options[:except_on] }
23
+
24
+ ensure_except_on_present!
25
+ ensure_except_on_valid!
26
+
27
+ if @except_on.is_a?(Array)
28
+ (validator_except_on_contexts & @except_on.map(&:to_s)) == validator_except_on_contexts || raise_except_on_not_listed_error
29
+ elsif @except_on.is_a?(Symbol)
30
+ validator_except_on_contexts.include?(@except_on.to_s) || raise_except_on_not_listed_error
31
+ end
32
+ end
33
+
34
+ def ensure_except_on_present!
35
+ raise ArgumentError, "This validator matcher needs the #except_on option to work since its validator has one" if !@except_on && attribute_validators.all? { |validator| validator.options[:except_on] }
36
+ end
37
+
38
+ def ensure_except_on_valid!
39
+ raise ArgumentError, "This validator matcher option only allows a symbol or an array" if !(@except_on.is_a?(Symbol) || @except_on.is_a?(Array))
40
+ end
41
+
42
+ def validator_except_on_contexts
43
+ attribute_validators.map do |validator|
44
+ case validator.options[:except_on]
45
+ when Array then validator.options[:except_on].map { |context| context.to_s }
46
+ when NilClass then nil
47
+ else validator.options[:except_on].to_s
48
+ end
49
+ end.flatten.compact
50
+ end
51
+
52
+ def raise_except_on_not_listed_error
53
+ raise ArgumentError, "One of the provided contexts to the #except_on method is not found in any of the listed contexts for this attribute"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module ActiveStorageValidations
6
+ module Matchers
7
+ module ASVSpoofingProtectable
8
+ extend ActiveSupport::Concern
9
+
10
+ UNSET = Object.new.freeze
11
+
12
+ def initialize_spoofing_protectable
13
+ @spoofing_protection = UNSET
14
+ end
15
+
16
+ def spoofing_protection(value = true)
17
+ @spoofing_protection = value
18
+ self
19
+ end
20
+
21
+ private
22
+
23
+ def is_spoofing_protection_valid?
24
+ return true if @spoofing_protection.equal?(UNSET)
25
+
26
+ expected = normalize_spoofing_protection(@spoofing_protection)
27
+ attribute_validators.any? do |validator|
28
+ next false unless validator.options.key?(:spoofing_protection)
29
+
30
+ normalize_spoofing_protection(validator.options[:spoofing_protection]) == expected
31
+ end
32
+ end
33
+
34
+ def normalize_spoofing_protection(value)
35
+ case value
36
+ when true, :file then :file
37
+ when :magika then :magika
38
+ when false then false
39
+ else value
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module ActiveStorageValidations
6
+ module Matchers
7
+ module ASVTimeoutable
8
+ extend ActiveSupport::Concern
9
+
10
+ UNSET = Object.new.freeze
11
+
12
+ def initialize_timeoutable
13
+ @timeout = UNSET
14
+ end
15
+
16
+ def timeout(value)
17
+ @timeout = value
18
+ self
19
+ end
20
+
21
+ private
22
+
23
+ def is_timeout_valid?
24
+ return true if @timeout.equal?(UNSET)
25
+
26
+ attribute_validators.any? do |validator|
27
+ validator.options.key?(:timeout) && validator.options[:timeout] == @timeout
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end