active_storage_validations 1.3.4 → 1.4.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -67
  3. data/config/locales/da.yml +1 -0
  4. data/config/locales/de.yml +1 -0
  5. data/config/locales/en.yml +1 -0
  6. data/config/locales/es.yml +1 -0
  7. data/config/locales/fr.yml +1 -0
  8. data/config/locales/it.yml +1 -0
  9. data/config/locales/ja.yml +1 -0
  10. data/config/locales/nl.yml +1 -0
  11. data/config/locales/pl.yml +1 -0
  12. data/config/locales/pt-BR.yml +1 -0
  13. data/config/locales/ru.yml +1 -0
  14. data/config/locales/sv.yml +1 -0
  15. data/config/locales/tr.yml +1 -0
  16. data/config/locales/uk.yml +1 -0
  17. data/config/locales/vi.yml +1 -0
  18. data/config/locales/zh-CN.yml +1 -0
  19. data/lib/active_storage_validations/analyzer/image_analyzer/image_magick.rb +47 -0
  20. data/lib/active_storage_validations/analyzer/image_analyzer/vips.rb +58 -0
  21. data/lib/active_storage_validations/analyzer/image_analyzer.rb +93 -0
  22. data/lib/active_storage_validations/analyzer/null_analyzer.rb +18 -0
  23. data/lib/active_storage_validations/analyzer.rb +34 -0
  24. data/lib/active_storage_validations/aspect_ratio_validator.rb +150 -118
  25. data/lib/active_storage_validations/content_type_spoof_detector.rb +3 -1
  26. data/lib/active_storage_validations/content_type_validator.rb +13 -5
  27. data/lib/active_storage_validations/dimension_validator.rb +2 -0
  28. data/lib/active_storage_validations/marcel_extensor.rb +2 -0
  29. data/lib/active_storage_validations/matchers/processable_image_validator_matcher.rb +4 -4
  30. data/lib/active_storage_validations/matchers.rb +2 -1
  31. data/lib/active_storage_validations/processable_image_validator.rb +2 -0
  32. data/lib/active_storage_validations/shared/asv_active_storageable.rb +2 -2
  33. data/lib/active_storage_validations/shared/asv_analyzable.rb +45 -0
  34. data/lib/active_storage_validations/shared/asv_attachable.rb +63 -16
  35. data/lib/active_storage_validations/version.rb +1 -1
  36. data/lib/active_storage_validations.rb +6 -0
  37. metadata +8 -3
  38. data/lib/active_storage_validations/metadata.rb +0 -179
@@ -1,179 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'shared/asv_loggable'
4
-
5
- module ActiveStorageValidations
6
- class Metadata
7
- include ASVLoggable
8
-
9
- class InvalidImageError < StandardError; end
10
-
11
- attr_reader :attachable
12
-
13
- DEFAULT_IMAGE_PROCESSOR = :mini_magick.freeze
14
-
15
- def initialize(attachable)
16
- require_image_processor
17
- @attachable = attachable
18
- end
19
-
20
- def valid?
21
- read_image
22
- true
23
- rescue InvalidImageError
24
- false
25
- end
26
-
27
- def metadata
28
- read_image do |image|
29
- if rotated_image?(image)
30
- { width: image.height, height: image.width }
31
- else
32
- { width: image.width, height: image.height }
33
- end
34
- end
35
- rescue InvalidImageError
36
- logger.info "Skipping image analysis because ImageMagick or Vips doesn't support the file"
37
- {}
38
- end
39
-
40
- private
41
-
42
- def image_processor
43
- # Rails returns nil for default image processor, because it is set in an after initialize callback
44
- # https://github.com/rails/rails/blob/89d8569abe2564c8187debf32dd3b4e33d6ad983/activestorage/lib/active_storage/engine.rb
45
- Rails.application.config.active_storage.variant_processor || DEFAULT_IMAGE_PROCESSOR
46
- end
47
-
48
- def require_image_processor
49
- case image_processor
50
- when :vips then require 'vips' unless defined?(Vips)
51
- when :mini_magick then require 'mini_magick' unless defined?(MiniMagick)
52
- end
53
- end
54
-
55
- def exception_class
56
- case image_processor
57
- when :vips then Vips::Error
58
- when :mini_magick then MiniMagick::Error
59
- end
60
- end
61
-
62
- def vips_image_processor?
63
- image_processor == :vips
64
- end
65
-
66
- def read_image
67
- is_string = attachable.is_a?(String)
68
- if is_string || attachable.is_a?(ActiveStorage::Blob)
69
- blob = is_string ? ActiveStorage::Blob.find_signed!(attachable) : attachable
70
-
71
- tempfile = Tempfile.new(["ActiveStorage-#{blob.id}-", blob.filename.extension_with_delimiter])
72
- tempfile.binmode
73
-
74
- blob.download do |chunk|
75
- tempfile.write(chunk)
76
- end
77
-
78
- tempfile.flush
79
- tempfile.rewind
80
-
81
- image = new_image_from_path(tempfile.path)
82
- else
83
- file_path = read_file_path
84
- image = new_image_from_path(file_path)
85
- end
86
-
87
-
88
- raise InvalidImageError unless valid_image?(image)
89
- yield image if block_given?
90
- rescue LoadError, NameError
91
- logger.info "Skipping image analysis because the mini_magick or ruby-vips gem isn't installed"
92
- {}
93
- rescue exception_class => error
94
- logger.error "Skipping image analysis due to an #{exception_class.name.split('::').map(&:downcase).join(' ').capitalize} error: #{error.message}"
95
- {}
96
- ensure
97
- image = nil
98
- end
99
-
100
- def new_image_from_path(path)
101
- if vips_image_processor? && (supported_vips_suffix?(path) || vips_version_below_8_8? || open_uri_tempfile?(path))
102
- begin
103
- Vips::Image.new_from_file(path)
104
- rescue exception_class
105
- # We handle cases where an error is raised when reading the attachable
106
- # because Vips can throw errors rather than returning false
107
- # We stumble upon this issue while reading 0 byte size attachable
108
- # https://github.com/janko/image_processing/issues/97
109
- false
110
- end
111
- elsif defined?(MiniMagick)
112
- MiniMagick::Image.new(path)
113
- end
114
- end
115
-
116
- def supported_vips_suffix?(path)
117
- Vips::get_suffixes.include?(File.extname(path).downcase)
118
- end
119
-
120
- def vips_version_below_8_8?
121
- # FYI, Vips 8.8 was released in 2019
122
- # https://github.com/libvips/libvips/releases/tag/v8.8.0
123
- !Vips::respond_to?(:vips_foreign_get_suffixes)
124
- end
125
-
126
- def open_uri_tempfile?(path)
127
- # When trying to open urls for 'large' images, OpenURI will return a
128
- # tempfile. That tempfile does not have an extension indicating the type
129
- # of file. However, Vips will be able to process it anyway.
130
- # The 'large' file value is derived from OpenUri::Buffer class (> 10ko)
131
- path.split('/').last.starts_with?("open-uri")
132
- end
133
-
134
- def valid_image?(image)
135
- return false unless image
136
-
137
- vips_image_processor? && image.is_a?(Vips::Image) ? image.avg : image.valid?
138
- rescue exception_class
139
- false
140
- end
141
-
142
- def rotated_image?(image)
143
- if vips_image_processor? && image.is_a?(Vips::Image)
144
- image.get('exif-ifd0-Orientation').include?('Right-top') ||
145
- image.get('exif-ifd0-Orientation').include?('Left-bottom')
146
- else
147
- %w[ RightTop LeftBottom ].include?(image["%[orientation]"])
148
- end
149
- rescue exception_class # field "exif-ifd0-Orientation" not found
150
- false
151
- end
152
-
153
- def read_file_path
154
- case attachable
155
- when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
156
- attachable.path
157
- when Hash
158
- io = attachable.fetch(:io)
159
- if io.is_a?(StringIO)
160
- tempfile = Tempfile.new([File.basename(attachable[:filename], '.*'), File.extname(attachable[:filename])])
161
- tempfile.binmode
162
- IO.copy_stream(io, tempfile)
163
- io.rewind
164
- tempfile.flush
165
- tempfile.rewind
166
- tempfile.path
167
- else
168
- File.open(io).path
169
- end
170
- when File
171
- attachable.path
172
- when Pathname
173
- attachable.to_s
174
- else
175
- raise "Something wrong with params."
176
- end
177
- end
178
- end
179
- end