active_storage_validations 1.3.0 → 1.3.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.
Files changed (54) 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 +58 -43
  20. data/lib/active_storage_validations/attached_validator.rb +3 -3
  21. data/lib/active_storage_validations/base_size_validator.rb +5 -4
  22. data/lib/active_storage_validations/content_type_spoof_detector.rb +12 -48
  23. data/lib/active_storage_validations/content_type_validator.rb +97 -49
  24. data/lib/active_storage_validations/dimension_validator.rb +8 -7
  25. data/lib/active_storage_validations/limit_validator.rb +6 -5
  26. data/lib/active_storage_validations/matchers/aspect_ratio_validator_matcher.rb +7 -7
  27. data/lib/active_storage_validations/matchers/attached_validator_matcher.rb +6 -6
  28. data/lib/active_storage_validations/matchers/base_size_validator_matcher.rb +7 -7
  29. data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +33 -33
  30. data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +7 -7
  31. data/lib/active_storage_validations/matchers/limit_validator_matcher.rb +7 -7
  32. data/lib/active_storage_validations/matchers/processable_image_validator_matcher.rb +7 -7
  33. data/lib/active_storage_validations/metadata.rb +17 -18
  34. data/lib/active_storage_validations/processable_image_validator.rb +5 -5
  35. data/lib/active_storage_validations/shared/attachable.rb +134 -0
  36. data/lib/active_storage_validations/{concerns → shared}/errorable.rb +2 -1
  37. data/lib/active_storage_validations/shared/optionable.rb +27 -0
  38. data/lib/active_storage_validations/size_validator.rb +2 -2
  39. data/lib/active_storage_validations/total_size_validator.rb +2 -2
  40. data/lib/active_storage_validations/version.rb +1 -1
  41. data/lib/active_storage_validations.rb +0 -1
  42. metadata +65 -25
  43. data/lib/active_storage_validations/concerns/metadatable.rb +0 -31
  44. data/lib/active_storage_validations/option_proc_unfolding.rb +0 -16
  45. /data/lib/active_storage_validations/matchers/{concerns → shared}/active_storageable.rb +0 -0
  46. /data/lib/active_storage_validations/matchers/{concerns → shared}/allow_blankable.rb +0 -0
  47. /data/lib/active_storage_validations/matchers/{concerns → shared}/attachable.rb +0 -0
  48. /data/lib/active_storage_validations/matchers/{concerns → shared}/contextable.rb +0 -0
  49. /data/lib/active_storage_validations/matchers/{concerns → shared}/messageable.rb +0 -0
  50. /data/lib/active_storage_validations/matchers/{concerns → shared}/rspecable.rb +0 -0
  51. /data/lib/active_storage_validations/matchers/{concerns → shared}/validatable.rb +0 -0
  52. /data/lib/active_storage_validations/{concerns → shared}/active_storageable.rb +0 -0
  53. /data/lib/active_storage_validations/{concerns → shared}/loggable.rb +0 -0
  54. /data/lib/active_storage_validations/{concerns → shared}/symbolizable.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09b483aef23513aaf10b56c6df6046078bf14e93fcfdebe2e82f2db862b85514'
4
- data.tar.gz: d040c36c24fc1fb2b4d1a2e6bb92bde86521cedfda4b4e96436a10b364499f63
3
+ metadata.gz: 6e1145fabe1ee64e7f7033a38c5b1de1114b364e5e2d9eb010628afde0a3e0b3
4
+ data.tar.gz: 497b550e5d98bf96e4398969de34022b8e8f828003a620261909a6d39e349aff
5
5
  SHA512:
6
- metadata.gz: eb6df31c0374b1bfa804281f0effbaa0443ff6a812abd4a46e1ac4293a881e691fd5c61cd4c2622b027085acb3d10c14255f04cac3bab01f13f133d2f26c60af
7
- data.tar.gz: b5553a4a7a2f08542c8e6f9dd928b0cc78a09eb846f73346dcdec1b3826bb4a5f04ac200c21e6e2f0f58f6dcc173f226a04e7a2c85d7c15a24b5b827bfb04dcb
6
+ metadata.gz: b671b5b33834ff904de1dea6dca51bfd741eaf2e10d931896a6fc4ff9ff83fc213f1581d59c562a806640cdc7a10537fa5ddb240051152ff38b5933ea7c7dc33
7
+ data.tar.gz: 381da6fbfb0fa544357bc492cfc9535992097ed7118846bae62f1b1953e5058f7b1bbc389ebd0dd8543fb035edaba55cee34a8c73a1b4fa84fc2ffc3e8bf9654
data/README.md CHANGED
@@ -91,9 +91,10 @@ Marcel::MimeType.extend "application/ino", extensions: %w(ino), parents: "text/p
91
91
  ```
92
92
 
93
93
  **Content type spoofing protection**
94
+
94
95
  File content type spoofing happens when an ill-intentioned user uploads a file which hides its true content type by faking its extension and its declared content type value. For example, a user may try to upload a `.exe` file (application/x-msdownload content type) dissimulated as a `.jpg` file (image/jpeg content type).
95
96
 
96
- By default, the gem does not prevent content type spoofing (prevent it by default is a breaking change that will be implemented in v2). The spoofing protection relies on both the linux `file` command and `Marcel` gem.
97
+ By default, the gem does not prevent content type spoofing (prevent it by default is a breaking change that will be implemented in v2). The spoofing protection relies on both the linux `file` command and `Marcel` gem. Be careful, since it needs to load the whole file io to perform the analysis, it will use a lot of RAM for very large files. Therefore it could be a wise decision not to enable it in this case.
97
98
 
98
99
  Take note that the `file` analyzer will not find the exactly same content type as the ActiveStorage blob (its content type detection relies on a different logic using content+filename+extension). To handle this issue, we consider a close parent content type to be a match. For example, for an ActiveStorage blob which content type is `video/x-ms-wmv`, the `file` analyzer will probably detect a `video/x-ms-asf` content type, this will be considered as a valid match because these 2 content types are closely related. The correlation mapping is based on `Marcel::TYPE_PARENTS`.
99
100
 
@@ -202,7 +203,6 @@ en:
202
203
  aspect_ratio_not_portrait: "must be a portrait image"
203
204
  aspect_ratio_not_landscape: "must be a landscape image"
204
205
  aspect_ratio_is_not: "must have an aspect ratio of %{aspect_ratio}"
205
- aspect_ratio_unknown: "has an unknown aspect ratio"
206
206
  image_not_processable: "is not a valid image"
207
207
  ```
208
208
 
@@ -221,7 +221,8 @@ aspect_ratio_is_not: "must be a %{aspect_ratio} image"
221
221
 
222
222
  ### Content type
223
223
  The `content_type_invalid` key has three variables that you can use:
224
- - `content_type` containing the content type of the sent file
224
+ - `content_type` containing the exact content type of the sent file
225
+ - `human_content_type` containing a more user-friendly version of the sent file content type (e.g. 'TXT' for 'text/plain')
225
226
  - `authorized_types` containing the list of authorized content types
226
227
  - `filename` containing the current file name
227
228
 
@@ -436,10 +437,11 @@ Then you can use the matchers with the syntax specified in the RSpec section, ju
436
437
 
437
438
  To run tests in root folder of gem:
438
439
 
439
- * `BUNDLE_GEMFILE=gemfiles/rails_6_1_4.gemfile bundle exec rake test` to run for Rails 6.1.4
440
+ * `BUNDLE_GEMFILE=gemfiles/rails_6_1_4.gemfile bundle exec rake test` to run for Rails 7.0
440
441
  * `BUNDLE_GEMFILE=gemfiles/rails_7_0.gemfile bundle exec rake test` to run for Rails 7.0
441
- * `BUNDLE_GEMFILE=gemfiles/rails_7_1.gemfile bundle exec rake test` to run for Rails 7.0
442
- * `BUNDLE_GEMFILE=gemfiles/rails_next.gemfile bundle exec rake test` to run for Rails main branch
442
+ * `BUNDLE_GEMFILE=gemfiles/rails_7_1.gemfile bundle exec rake test` to run for Rails 7.1
443
+ * `BUNDLE_GEMFILE=gemfiles/rails_7_2.gemfile bundle exec rake test` to run for Rails 7.2
444
+ * `BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle exec rake test` to run for Rails 8.0
443
445
 
444
446
  Snippet to run in console:
445
447
 
@@ -447,11 +449,13 @@ Snippet to run in console:
447
449
  BUNDLE_GEMFILE=gemfiles/rails_6_1_4.gemfile bundle
448
450
  BUNDLE_GEMFILE=gemfiles/rails_7_0.gemfile bundle
449
451
  BUNDLE_GEMFILE=gemfiles/rails_7_1.gemfile bundle
450
- BUNDLE_GEMFILE=gemfiles/rails_next.gemfile bundle
452
+ BUNDLE_GEMFILE=gemfiles/rails_7_2.gemfile bundle
453
+ BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle
451
454
  BUNDLE_GEMFILE=gemfiles/rails_6_1_4.gemfile bundle exec rake test
452
455
  BUNDLE_GEMFILE=gemfiles/rails_7_0.gemfile bundle exec rake test
453
456
  BUNDLE_GEMFILE=gemfiles/rails_7_1.gemfile bundle exec rake test
454
- BUNDLE_GEMFILE=gemfiles/rails_next.gemfile bundle exec rake test
457
+ BUNDLE_GEMFILE=gemfiles/rails_7_2.gemfile bundle exec rake test
458
+ BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle exec rake test
455
459
  ```
456
460
 
457
461
  Tips:
@@ -29,5 +29,4 @@ da:
29
29
  aspect_ratio_not_portrait: "skal være et portrætbillede"
30
30
  aspect_ratio_not_landscape: "skal være et landskabsbillede"
31
31
  aspect_ratio_is_not: "skal have et størrelsesforhold på %{aspect_ratio}"
32
- aspect_ratio_unknown: "har et ukendt størrelsesforhold"
33
32
  image_not_processable: "er ikke et gyldigt billede"
@@ -29,5 +29,4 @@ de:
29
29
  aspect_ratio_not_portrait: "muss Hochformat sein"
30
30
  aspect_ratio_not_landscape: "muss Querformat sein"
31
31
  aspect_ratio_is_not: "muss ein Bildseitenverhältnis von %{aspect_ratio} haben"
32
- aspect_ratio_unknown: "hat ein unbekanntes Bildseitenverhältnis"
33
32
  image_not_processable: "ist kein gültiges Bild"
@@ -29,5 +29,4 @@ en:
29
29
  aspect_ratio_not_portrait: "must be a portrait image"
30
30
  aspect_ratio_not_landscape: "must be a landscape image"
31
31
  aspect_ratio_is_not: "must have an aspect ratio of %{aspect_ratio}"
32
- aspect_ratio_unknown: "has an unknown aspect ratio"
33
32
  image_not_processable: "is not a valid image"
@@ -29,5 +29,4 @@ es:
29
29
  aspect_ratio_not_portrait: "debe ser una imagen vertical"
30
30
  aspect_ratio_not_landscape: "debe ser una imagen apaisada"
31
31
  aspect_ratio_is_not: "debe tener una relación de aspecto de %{aspect_ratio}"
32
- aspect_ratio_unknown: "tiene una relación de aspecto desconocida"
33
32
  image_not_processable: "no es una imagen válida"
@@ -29,5 +29,4 @@ fr:
29
29
  aspect_ratio_not_portrait: "doit être une image en format portrait"
30
30
  aspect_ratio_not_landscape: "doit être une image en format paysage"
31
31
  aspect_ratio_is_not: "doit avoir un rapport hauteur / largeur de %{aspect_ratio}"
32
- aspect_ratio_unknown: "a un rapport d'aspect inconnu"
33
32
  image_not_processable: "n'est pas une image valide"
@@ -29,5 +29,4 @@ it:
29
29
  aspect_ratio_not_portrait: "l’orientamento dell’immagine deve essere verticale"
30
30
  aspect_ratio_not_landscape: "l’orientamento dell’immagine deve essere orizzontale"
31
31
  aspect_ratio_is_not: "deve avere un rapporto altezza / larghezza di %{aspect_ratio}"
32
- aspect_ratio_unknown: "ha un rapporto altezza / larghezza sconosciuto"
33
32
  image_not_processable: "non è un'immagine valida"
@@ -29,5 +29,4 @@ ja:
29
29
  aspect_ratio_not_portrait: "は縦長にしてください"
30
30
  aspect_ratio_not_landscape: "は横長にしてください"
31
31
  aspect_ratio_is_not: "のアスペクト比は %{aspect_ratio} にしてください"
32
- aspect_ratio_unknown: "のアスペクト比を取得できませんでした"
33
32
  image_not_processable: "は不正な画像です"
@@ -29,5 +29,4 @@ nl:
29
29
  aspect_ratio_not_portrait: "moet een staande afbeelding zijn"
30
30
  aspect_ratio_not_landscape: "moet een liggende afbeelding zijn"
31
31
  aspect_ratio_is_not: "moet een beeldverhouding hebben van %{aspect_ratio}"
32
- aspect_ratio_unknown: "heeft een onbekende beeldverhouding"
33
32
  image_not_processable: "is geen geldige afbeelding"
@@ -29,5 +29,4 @@ pl:
29
29
  aspect_ratio_not_portrait: "musi mieć proporcje portretu"
30
30
  aspect_ratio_not_landscape: "musi mieć proporcje pejzażu"
31
31
  aspect_ratio_is_not: "musi mieć proporcje %{aspect_ratio}"
32
- aspect_ratio_unknown: "ma nieokreślone proporcje"
33
32
  image_not_processable: "nie jest prawidłowym obrazem"
@@ -29,5 +29,4 @@ pt-BR:
29
29
  aspect_ratio_not_portrait: "não está no formato retrato"
30
30
  aspect_ratio_not_landscape: "não está no formato paisagem"
31
31
  aspect_ratio_is_not: "não contém uma proporção de %{aspect_ratio}"
32
- aspect_ratio_unknown: "não tem uma proporção definida"
33
32
  image_not_processable: "não é uma imagem válida"
@@ -29,5 +29,4 @@ ru:
29
29
  aspect_ratio_not_portrait: "должно быть портретное изображение"
30
30
  aspect_ratio_not_landscape: "должно быть пейзажное изображение"
31
31
  aspect_ratio_is_not: "должен иметь соотношение сторон %{aspect_ratio}"
32
- aspect_ratio_unknown: "имеет неизвестное соотношение сторон"
33
32
  image_not_processable: "не является допустимым изображением"
@@ -29,5 +29,4 @@ sv:
29
29
  aspect_ratio_not_portrait: "måste vara en porträttorienterad bild"
30
30
  aspect_ratio_not_landscape: "måste vara en landskapsorienterad bild"
31
31
  aspect_ratio_is_not: "måste ha en följande aspect ratio %{aspect_ratio}"
32
- aspect_ratio_unknown: "har en okänd aspect ratio"
33
32
  image_not_processable: "är inte en giltig bild"
@@ -29,5 +29,4 @@ tr:
29
29
  aspect_ratio_not_portrait: "dikey bir imaj olmalı"
30
30
  aspect_ratio_not_landscape: "yatay bir imaj olmalı"
31
31
  aspect_ratio_is_not: "%{aspect_ratio} en boy oranına sahip olmalı"
32
- aspect_ratio_unknown: "bilinmeyen en boy oranı"
33
32
  image_not_processable: "geçerli bir imaj değil"
@@ -29,5 +29,4 @@ uk:
29
29
  aspect_ratio_not_portrait: "мусить бути портретне зображення"
30
30
  aspect_ratio_not_landscape: "мусить бути пейзажне зображення"
31
31
  aspect_ratio_is_not: "мусить мати співвідношення сторін %{aspect_ratio}"
32
- aspect_ratio_unknown: "має невідоме співвідношення сторін"
33
32
  image_not_processable: "не є допустимим зображенням"
@@ -29,5 +29,4 @@ vi:
29
29
  aspect_ratio_not_portrait: "phải là ảnh đứng"
30
30
  aspect_ratio_not_landscape: "phải là ảnh ngang"
31
31
  aspect_ratio_is_not: "phải có tỉ lệ ảnh %{aspect_ratio}"
32
- aspect_ratio_unknown: "tỉ lệ ảnh không xác định"
33
32
  image_not_processable: "không phải là ảnh"
@@ -29,5 +29,4 @@ zh-CN:
29
29
  aspect_ratio_not_portrait: "必须是竖屏图片"
30
30
  aspect_ratio_not_landscape: "必须是横屏图片"
31
31
  aspect_ratio_is_not: "纵横比必须是 %{aspect_ratio}"
32
- aspect_ratio_unknown: "未知的纵横比"
33
32
  image_not_processable: "不是有效的图像"
@@ -1,16 +1,17 @@
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/active_storageable'
4
+ require_relative 'shared/attachable'
5
+ require_relative 'shared/errorable'
6
+ require_relative 'shared/optionable'
7
+ require_relative 'shared/symbolizable'
7
8
 
8
9
  module ActiveStorageValidations
9
10
  class AspectRatioValidator < ActiveModel::EachValidator # :nodoc
10
11
  include ActiveStorageable
12
+ include Attachable
11
13
  include Errorable
12
- include Metadatable
13
- include OptionProcUnfolding
14
+ include Optionable
14
15
  include Symbolizable
15
16
 
16
17
  AVAILABLE_CHECKS = %i[with].freeze
@@ -22,7 +23,6 @@ module ActiveStorageValidations
22
23
  aspect_ratio_not_portrait
23
24
  aspect_ratio_not_landscape
24
25
  aspect_ratio_is_not
25
- aspect_ratio_unknown
26
26
  ].freeze
27
27
  PRECISION = 3.freeze
28
28
 
@@ -40,48 +40,63 @@ module ActiveStorageValidations
40
40
  private
41
41
 
42
42
  def is_valid?(record, attribute, attachable, metadata)
43
- flat_options = unfold_procs(record, self.options, AVAILABLE_CHECKS)
44
- errors_options = initialize_error_options(options, attachable)
45
-
46
- if metadata[:width].to_i <= 0 || metadata[:height].to_i <= 0
47
- errors_options[:aspect_ratio] = flat_options[:with]
43
+ flat_options = set_flat_options(record)
48
44
 
49
- add_error(record, attribute, :image_metadata_missing, **errors_options)
50
- return false
51
- end
45
+ return if image_metadata_missing?(record, attribute, attachable, flat_options, metadata)
52
46
 
53
47
  case flat_options[:with]
54
- when :square
55
- return true if metadata[:width] == metadata[:height]
56
- errors_options[:aspect_ratio] = flat_options[:with]
57
- add_error(record, attribute, :aspect_ratio_not_square, **errors_options)
58
-
59
- when :portrait
60
- return true if metadata[:height] > metadata[:width]
61
- errors_options[:aspect_ratio] = flat_options[:with]
62
- add_error(record, attribute, :aspect_ratio_not_portrait, **errors_options)
63
-
64
- when :landscape
65
- return true if metadata[:width] > metadata[:height]
66
- errors_options[:aspect_ratio] = flat_options[:with]
67
- add_error(record, attribute, :aspect_ratio_not_landscape, **errors_options)
68
-
69
- when ASPECT_RATIO_REGEX
70
- flat_options[:with] =~ ASPECT_RATIO_REGEX
71
- x = $1.to_i
72
- y = $2.to_i
73
-
74
- return true if x > 0 && y > 0 && (x.to_f / y).round(PRECISION) == (metadata[:width].to_f / metadata[:height]).round(PRECISION)
75
-
76
- errors_options[:aspect_ratio] = "#{x}:#{y}"
77
- add_error(record, attribute, :aspect_ratio_is_not, **errors_options)
78
- else
79
- errors_options[:aspect_ratio] = flat_options[:with]
80
- add_error(record, attribute, :aspect_ratio_unknown, **errors_options)
81
- return false
48
+ when :square then validate_square_aspect_ratio(record, attribute, attachable, flat_options, metadata)
49
+ when :portrait then validate_portrait_aspect_ratio(record, attribute, attachable, flat_options, metadata)
50
+ when :landscape then validate_landscape_aspect_ratio(record, attribute, attachable, flat_options, metadata)
51
+ when ASPECT_RATIO_REGEX then validate_regex_aspect_ratio(record, attribute, attachable, flat_options, metadata)
82
52
  end
83
53
  end
84
54
 
55
+ def image_metadata_missing?(record, attribute, attachable, flat_options, metadata)
56
+ return false if metadata[:width].to_i > 0 && metadata[:height].to_i > 0
57
+
58
+ errors_options = initialize_error_options(options, attachable)
59
+ errors_options[:aspect_ratio] = flat_options[:with]
60
+ add_error(record, attribute, :image_metadata_missing, **errors_options)
61
+ true
62
+ end
63
+
64
+ def validate_square_aspect_ratio(record, attribute, attachable, flat_options, metadata)
65
+ return if metadata[:width] == metadata[:height]
66
+
67
+ errors_options = initialize_error_options(options, attachable)
68
+ errors_options[:aspect_ratio] = flat_options[:with]
69
+ add_error(record, attribute, :aspect_ratio_not_square, **errors_options)
70
+ end
71
+
72
+ def validate_portrait_aspect_ratio(record, attribute, attachable, flat_options, metadata)
73
+ return if metadata[:width] < metadata[:height]
74
+
75
+ errors_options = initialize_error_options(options, attachable)
76
+ errors_options[:aspect_ratio] = flat_options[:with]
77
+ add_error(record, attribute, :aspect_ratio_not_portrait, **errors_options)
78
+ end
79
+
80
+ def validate_landscape_aspect_ratio(record, attribute, attachable, flat_options, metadata)
81
+ return if metadata[:width] > metadata[:height]
82
+
83
+ errors_options = initialize_error_options(options, attachable)
84
+ errors_options[:aspect_ratio] = flat_options[:with]
85
+ add_error(record, attribute, :aspect_ratio_not_landscape, **errors_options)
86
+ end
87
+
88
+ def validate_regex_aspect_ratio(record, attribute, attachable, flat_options, metadata)
89
+ flat_options[:with] =~ ASPECT_RATIO_REGEX
90
+ x = $1.to_i
91
+ y = $2.to_i
92
+
93
+ return if x > 0 && y > 0 && (x.to_f / y).round(PRECISION) == (metadata[:width].to_f / metadata[:height]).round(PRECISION)
94
+
95
+ errors_options = initialize_error_options(options, attachable)
96
+ errors_options[:aspect_ratio] = "#{x}:#{y}"
97
+ add_error(record, attribute, :aspect_ratio_is_not, **errors_options)
98
+ end
99
+
85
100
  def ensure_at_least_one_validator_option
86
101
  unless AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
87
102
  raise ArgumentError, 'You must pass :with to the validator'
@@ -1,8 +1,8 @@
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/active_storageable'
4
+ require_relative 'shared/errorable'
5
+ require_relative 'shared/symbolizable'
6
6
 
7
7
  module ActiveStorageValidations
8
8
  class AttachedValidator < ActiveModel::EachValidator # :nodoc:
@@ -1,14 +1,15 @@
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/active_storageable'
4
+ require_relative 'shared/errorable'
5
+ require_relative 'shared/optionable'
6
+ require_relative 'shared/symbolizable'
6
7
 
7
8
  module ActiveStorageValidations
8
9
  class BaseSizeValidator < ActiveModel::EachValidator # :nodoc:
9
10
  include ActiveStorageable
10
11
  include Errorable
11
- include OptionProcUnfolding
12
+ include Optionable
12
13
  include Symbolizable
13
14
 
14
15
  delegate :number_to_human_size, to: ActiveSupport::NumberHelper
@@ -1,18 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'concerns/loggable'
3
+ require_relative 'shared/attachable'
4
+ require_relative 'shared/loggable'
4
5
  require 'open3'
5
6
 
6
7
  module ActiveStorageValidations
7
8
  class ContentTypeSpoofDetector
8
9
  class FileCommandLineToolNotInstalledError < StandardError; end
9
10
 
11
+ include Attachable
10
12
  include Loggable
11
13
 
12
- def initialize(record, attribute, file)
14
+ def initialize(record, attribute, attachable)
13
15
  @record = record
14
16
  @attribute = attribute
15
- @file = file
17
+ @attachable = attachable
16
18
  end
17
19
 
18
20
  def spoofed?
@@ -27,60 +29,22 @@ module ActiveStorageValidations
27
29
  private
28
30
 
29
31
  def filename
30
- @filename ||= @file.blob.present? && @file.blob.filename.to_s
32
+ @filename ||= attachable_filename(@attachable).to_s
31
33
  end
32
34
 
33
35
  def supplied_content_type
34
- # We remove potential mime type parameters
35
- @supplied_content_type ||= @file.blob.present? && @file.blob.content_type.downcase.split(/[;,\s]/, 2).first
36
+ @supplied_content_type ||= attachable_content_type(@attachable)
36
37
  end
37
38
 
38
39
  def io
39
- @io ||= case @record.public_send(@attribute)
40
- when ActiveStorage::Attached::One then get_io_from_one
41
- when ActiveStorage::Attached::Many then get_io_from_many
42
- end
43
- end
44
-
45
- def get_io_from_one
46
- attachable = @record.attachment_changes[@attribute.to_s].attachable
47
-
48
- case attachable
49
- when ActionDispatch::Http::UploadedFile
50
- attachable.read
51
- when String
52
- blob = ActiveStorage::Blob.find_signed!(attachable)
53
- blob.download
54
- when ActiveStorage::Blob
55
- attachable.download
56
- when Hash
57
- attachable[:io].read
58
- end
59
- end
60
-
61
- def get_io_from_many
62
- attachables = @record.attachment_changes[@attribute.to_s].attachables
63
-
64
- if attachables.all? { |attachable| attachable.is_a?(ActionDispatch::Http::UploadedFile) }
65
- attachables.find do |uploaded_file|
66
- checksum = ActiveStorage::Blob.new.send(:compute_checksum_in_chunks, uploaded_file)
67
- checksum == @file.checksum
68
- end.read
69
- elsif attachables.all? { |attachable| attachable.is_a?(String) }
70
- # It's only possible to pass one String as attachable (not an array of String)
71
- blob = ActiveStorage::Blob.find_signed!(attachables.first)
72
- blob.download
73
- elsif attachables.all? { |attachable| attachable.is_a?(ActiveStorage::Blob) }
74
- attachables.find { |blob| blob == @file.blob }.download
75
- elsif attachables.all? { |attachable| attachable.is_a?(Hash) }
76
- # It's only possible to pass one Hash as attachable (not an array of Hash)
77
- attachables.first[:io].read
78
- end
40
+ @io ||= attachable_io(@attachable)
79
41
  end
80
42
 
43
+ # Return the content_type found by Open3 analysis.
44
+ #
45
+ # Using Open3 is a better alternative than Marcel (Marcel::MimeType.for(io))
46
+ # for analyzing content type solely based on the file io.
81
47
  def content_type_from_analyzer
82
- # Using Open3 is a better alternative than Marcel (Marcel::MimeType.for(io))
83
- # for analyzing content type solely based on the file io
84
48
  @content_type_from_analyzer ||= open3_mime_type_for_io
85
49
  end
86
50