active_storage_validations 1.3.4 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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