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.
- checksums.yaml +4 -4
- data/README.md +5 -67
- data/config/locales/da.yml +1 -0
- data/config/locales/de.yml +1 -0
- data/config/locales/en.yml +1 -0
- data/config/locales/es.yml +1 -0
- data/config/locales/fr.yml +1 -0
- data/config/locales/it.yml +1 -0
- data/config/locales/ja.yml +1 -0
- data/config/locales/nl.yml +1 -0
- data/config/locales/pl.yml +1 -0
- data/config/locales/pt-BR.yml +1 -0
- data/config/locales/ru.yml +1 -0
- data/config/locales/sv.yml +1 -0
- data/config/locales/tr.yml +1 -0
- data/config/locales/uk.yml +1 -0
- data/config/locales/vi.yml +1 -0
- data/config/locales/zh-CN.yml +1 -0
- data/lib/active_storage_validations/analyzer/image_analyzer/image_magick.rb +47 -0
- data/lib/active_storage_validations/analyzer/image_analyzer/vips.rb +58 -0
- data/lib/active_storage_validations/analyzer/image_analyzer.rb +93 -0
- data/lib/active_storage_validations/analyzer/null_analyzer.rb +18 -0
- data/lib/active_storage_validations/analyzer.rb +34 -0
- data/lib/active_storage_validations/aspect_ratio_validator.rb +150 -118
- data/lib/active_storage_validations/content_type_spoof_detector.rb +3 -1
- data/lib/active_storage_validations/content_type_validator.rb +13 -5
- data/lib/active_storage_validations/dimension_validator.rb +2 -0
- data/lib/active_storage_validations/marcel_extensor.rb +2 -0
- data/lib/active_storage_validations/matchers/processable_image_validator_matcher.rb +4 -4
- data/lib/active_storage_validations/matchers.rb +2 -1
- data/lib/active_storage_validations/processable_image_validator.rb +2 -0
- data/lib/active_storage_validations/shared/asv_active_storageable.rb +2 -2
- data/lib/active_storage_validations/shared/asv_analyzable.rb +45 -0
- data/lib/active_storage_validations/shared/asv_attachable.rb +63 -16
- data/lib/active_storage_validations/version.rb +1 -1
- data/lib/active_storage_validations.rb +6 -0
- metadata +8 -3
- data/lib/active_storage_validations/metadata.rb +0 -179
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12a58907c84f972cb00aab13b6a60d26afda0383cbe88e13ec12b1c9d178ba36
|
4
|
+
data.tar.gz: 4df97d0b9e88054403ff7a39c89b00c4b3f9a0c3de7a38997f4f3a6193d803af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80210f9dd1636fac2f2dd9ab15c0090990312fdcd1c34f2c0aa6157c9c4e27b1aed9a03c064e91b148014388eaec3ed4171dd57d4c059e4f2e9ee919610d098a
|
7
|
+
data.tar.gz: f23b85dbb613af03faa0637fd5e9dd626e8e975b87590a0b87aedeba296bcd24c0f79c16dc20059ddb8f41b892887098d0f7dc58d1b951eaae0094b5dd15ea2a
|
data/README.md
CHANGED
@@ -204,6 +204,7 @@ en:
|
|
204
204
|
aspect_ratio_not_landscape: "must be a landscape image"
|
205
205
|
aspect_ratio_is_not: "must have an aspect ratio of %{aspect_ratio}"
|
206
206
|
image_not_processable: "is not a valid image"
|
207
|
+
aspect_ratio_invalid: "has invalid aspect ratio"
|
207
208
|
```
|
208
209
|
|
209
210
|
In several cases, Active Storage Validations provides variables to help you customize messages:
|
@@ -354,8 +355,8 @@ describe User do
|
|
354
355
|
|
355
356
|
# limit
|
356
357
|
# #min, #max
|
357
|
-
it { is_expected.to
|
358
|
-
it { is_expected.to
|
358
|
+
it { is_expected.to validate_limits_of(:avatar).min(1) }
|
359
|
+
it { is_expected.to validate_limits_of(:avatar).max(5) }
|
359
360
|
|
360
361
|
# content_type:
|
361
362
|
# #allowing, #rejecting
|
@@ -437,7 +438,7 @@ Then you can use the matchers with the syntax specified in the RSpec section, ju
|
|
437
438
|
|
438
439
|
To run tests in root folder of gem:
|
439
440
|
|
440
|
-
* `BUNDLE_GEMFILE=gemfiles/rails_6_1_4.gemfile bundle exec rake test` to run for Rails
|
441
|
+
* `BUNDLE_GEMFILE=gemfiles/rails_6_1_4.gemfile bundle exec rake test` to run for Rails 6.1
|
441
442
|
* `BUNDLE_GEMFILE=gemfiles/rails_7_0.gemfile bundle exec rake test` to run for Rails 7.0
|
442
443
|
* `BUNDLE_GEMFILE=gemfiles/rails_7_1.gemfile bundle exec rake test` to run for Rails 7.1
|
443
444
|
* `BUNDLE_GEMFILE=gemfiles/rails_7_2.gemfile bundle exec rake test` to run for Rails 7.2
|
@@ -471,71 +472,8 @@ You are welcome to contribute.
|
|
471
472
|
/>](https://opensource-heroes.com/r/igorkasyanchuk/active_storage_validations)
|
472
473
|
|
473
474
|
## Contributors (BIG THANK YOU)
|
474
|
-
- https://github.com/schweigert
|
475
|
-
- https://github.com/tleneveu
|
476
|
-
- https://github.com/reckerswartz
|
477
|
-
- https://github.com/Uysim
|
478
|
-
- https://github.com/D-system
|
479
|
-
- https://github.com/ivanelrey
|
480
|
-
- https://github.com/phlegx
|
481
|
-
- https://github.com/rr-dev
|
482
|
-
- https://github.com/dsmalko
|
483
|
-
- https://github.com/danderozier
|
484
|
-
- https://github.com/cseelus
|
485
|
-
- https://github.com/vkinelev
|
486
|
-
- https://github.com/reed
|
487
|
-
- https://github.com/connorshea
|
488
|
-
- https://github.com/Atul9
|
489
|
-
- https://github.com/victorbueno
|
490
|
-
- https://github.com/UICJohn
|
491
|
-
- https://github.com/giovannibonetti
|
492
|
-
- https://github.com/dlepage
|
493
|
-
- https://github.com/StefSchenkelaars
|
494
|
-
- https://github.com/willnet
|
495
|
-
- https://github.com/mohanklein
|
496
|
-
- https://github.com/High5Apps
|
497
|
-
- https://github.com/mschnitzer
|
498
|
-
- https://github.com/sinankeskin
|
499
|
-
- https://github.com/alejandrodevs
|
500
|
-
- https://github.com/molfar
|
501
|
-
- https://github.com/connorshea
|
502
|
-
- https://github.com/yshmarov
|
503
|
-
- https://github.com/fongfan999
|
504
|
-
- https://github.com/cooperka
|
505
|
-
- https://github.com/dolarsrg
|
506
|
-
- https://github.com/jayshepherd
|
507
|
-
- https://github.com/ohbarye
|
508
|
-
- https://github.com/randsina
|
509
|
-
- https://github.com/vietqhoang
|
510
|
-
- https://github.com/kemenaran
|
511
|
-
- https://github.com/jrmhaig
|
512
|
-
- https://github.com/evedovelli
|
513
|
-
- https://github.com/JuanVqz
|
514
|
-
- https://github.com/luiseugenio
|
515
|
-
- https://github.com/equivalent
|
516
|
-
- https://github.com/NARKOZ
|
517
|
-
- https://github.com/stephensolis
|
518
|
-
- https://github.com/kwent
|
519
|
-
- https://github.com/Animesh-Ghosh
|
520
|
-
- https://github.com/gr8bit
|
521
|
-
- https://github.com/codegeek319
|
522
|
-
- https://github.com/clwy-cn
|
523
|
-
- https://github.com/kukicola
|
524
|
-
- https://github.com/sobrinho
|
525
|
-
- https://github.com/iainbeeston
|
526
|
-
- https://github.com/marckohlbrugge
|
527
|
-
- https://github.com/Mth0158
|
528
|
-
- https://github.com/technicalpickles
|
529
|
-
- https://github.com/ricsdeol
|
530
|
-
- https://github.com/Fonsan
|
531
|
-
- https://github.com/tagliala
|
532
|
-
- https://github.com/ocarreterom
|
533
|
-
- https://github.com/aditya-cherukuri
|
534
|
-
- https://github.com/searls
|
535
|
-
- https://github.com/yenshirak
|
536
|
-
- https://github.com/wataori
|
537
|
-
- https://github.com/Scorpahr
|
538
475
|
|
476
|
+
https://github.com/igorkasyanchuk/active_storage_validations/graphs/contributors
|
539
477
|
|
540
478
|
## License
|
541
479
|
|
data/config/locales/da.yml
CHANGED
data/config/locales/de.yml
CHANGED
data/config/locales/en.yml
CHANGED
data/config/locales/es.yml
CHANGED
data/config/locales/fr.yml
CHANGED
@@ -30,3 +30,4 @@ fr:
|
|
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
32
|
image_not_processable: "n'est pas une image valide"
|
33
|
+
aspect_ratio_invalid: "a un rapport hauteur / largeur invalide"
|
data/config/locales/it.yml
CHANGED
@@ -30,3 +30,4 @@ it:
|
|
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
32
|
image_not_processable: "non è un'immagine valida"
|
33
|
+
aspect_ratio_invalid: "ha proporzioni non valide"
|
data/config/locales/ja.yml
CHANGED
data/config/locales/nl.yml
CHANGED
data/config/locales/pl.yml
CHANGED
data/config/locales/pt-BR.yml
CHANGED
data/config/locales/ru.yml
CHANGED
@@ -30,3 +30,4 @@ ru:
|
|
30
30
|
aspect_ratio_not_landscape: "должно быть пейзажное изображение"
|
31
31
|
aspect_ratio_is_not: "должен иметь соотношение сторон %{aspect_ratio}"
|
32
32
|
image_not_processable: "не является допустимым изображением"
|
33
|
+
aspect_ratio_invalid: "имеет недопустимое соотношение сторон"
|
data/config/locales/sv.yml
CHANGED
data/config/locales/tr.yml
CHANGED
data/config/locales/uk.yml
CHANGED
data/config/locales/vi.yml
CHANGED
data/config/locales/zh-CN.yml
CHANGED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveStorageValidations
|
4
|
+
# This analyzer relies on the third-party {MiniMagick}[https://github.com/minimagick/minimagick] gem.
|
5
|
+
# MiniMagick requires the {ImageMagick}[http://www.imagemagick.org] system library.
|
6
|
+
# This is the default Rails image analyzer.
|
7
|
+
class Analyzer::ImageAnalyzer::ImageMagick < Analyzer::ImageAnalyzer
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def read_image
|
12
|
+
Tempfile.create(binmode: true) do |tempfile|
|
13
|
+
begin
|
14
|
+
if image(tempfile).valid?
|
15
|
+
yield image(tempfile)
|
16
|
+
else
|
17
|
+
logger.info "Skipping image analysis because ImageMagick doesn't support the file"
|
18
|
+
{}
|
19
|
+
end
|
20
|
+
ensure
|
21
|
+
tempfile.close
|
22
|
+
end
|
23
|
+
end
|
24
|
+
rescue MiniMagick::Error => error
|
25
|
+
logger.error "Skipping image analysis due to an ImageMagick error: #{error.message}"
|
26
|
+
{}
|
27
|
+
end
|
28
|
+
|
29
|
+
def image_from_path(path)
|
30
|
+
instrument("mini_magick") do
|
31
|
+
MiniMagick::Image.new(path)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def rotated_image?(image)
|
36
|
+
%w[ RightTop LeftBottom TopRight BottomLeft ].include?(image["%[orientation]"])
|
37
|
+
end
|
38
|
+
|
39
|
+
def supported?
|
40
|
+
require "mini_magick"
|
41
|
+
true
|
42
|
+
rescue LoadError
|
43
|
+
logger.info "Skipping image analysis because the mini_magick gem isn't installed"
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveStorageValidations
|
4
|
+
# This analyzer relies on the third-party {ruby-vips}[https://github.com/libvips/ruby-vips] gem.
|
5
|
+
# Ruby-vips requires the {libvips}[https://libvips.github.io/libvips/] system library.
|
6
|
+
class Analyzer::ImageAnalyzer::Vips < Analyzer::ImageAnalyzer
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def read_image
|
11
|
+
Tempfile.create(binmode: true) do |tempfile|
|
12
|
+
begin
|
13
|
+
if image(tempfile)
|
14
|
+
yield image(tempfile)
|
15
|
+
else
|
16
|
+
logger.info "Skipping image analysis because Vips doesn't support the file"
|
17
|
+
{}
|
18
|
+
end
|
19
|
+
ensure
|
20
|
+
tempfile.close
|
21
|
+
end
|
22
|
+
end
|
23
|
+
rescue ::Vips::Error => error
|
24
|
+
logger.error "Skipping image analysis due to a Vips error: #{error.message}"
|
25
|
+
{}
|
26
|
+
end
|
27
|
+
|
28
|
+
def image_from_path(path)
|
29
|
+
instrument("vips") do
|
30
|
+
begin
|
31
|
+
::Vips::Image.new_from_file(path, access: :sequential)
|
32
|
+
rescue ::Vips::Error
|
33
|
+
# Vips throw errors rather than returning false when reading a not
|
34
|
+
# supported attachable.
|
35
|
+
# We stumbled upon this issue while reading 0 byte size attachable
|
36
|
+
# https://github.com/janko/image_processing/issues/97
|
37
|
+
logger.info "Skipping image analysis because Vips doesn't support the file"
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def supported?
|
44
|
+
require "vips"
|
45
|
+
true
|
46
|
+
rescue LoadError
|
47
|
+
logger.info "Skipping image analysis because the ruby-vips gem isn't installed"
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
ROTATIONS = /Right-top|Left-bottom|Top-right|Bottom-left/
|
52
|
+
def rotated_image?(image)
|
53
|
+
ROTATIONS === image.get("exif-ifd0-Orientation")
|
54
|
+
rescue ::Vips::Error
|
55
|
+
false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveStorageValidations
|
4
|
+
# = Active Storage Image \Analyzer
|
5
|
+
#
|
6
|
+
# This is an abstract base class for image analyzers, which extract width and height from an image attachable.
|
7
|
+
#
|
8
|
+
# If the image contains EXIF data indicating its angle is 90 or 270 degrees, its width and height are swapped for convenience.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# ActiveStorage::Analyzer::ImageAnalyzer::ImageMagick.new(attachable).metadata
|
13
|
+
# # => { width: 4104, height: 2736 }
|
14
|
+
class Analyzer::ImageAnalyzer < Analyzer
|
15
|
+
@@supported_analyzers = {}
|
16
|
+
|
17
|
+
def metadata
|
18
|
+
return {} unless analyzer_supported?
|
19
|
+
|
20
|
+
read_image do |image|
|
21
|
+
if rotated_image?(image)
|
22
|
+
{ width: image.height, height: image.width }
|
23
|
+
else
|
24
|
+
{ width: image.width, height: image.height }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def image(tempfile)
|
32
|
+
case @attachable
|
33
|
+
when ActiveStorage::Blob, String
|
34
|
+
blob = @attachable.is_a?(String) ? ActiveStorage::Blob.find_signed!(@attachable) : @attachable
|
35
|
+
image_from_tempfile_path(tempfile, blob)
|
36
|
+
when Hash
|
37
|
+
io = @attachable[:io]
|
38
|
+
if io.is_a?(StringIO)
|
39
|
+
image_from_tempfile_path(tempfile, io)
|
40
|
+
else
|
41
|
+
File.open(io) do |file|
|
42
|
+
image_from_path(file.path)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
|
46
|
+
image_from_path(@attachable.path)
|
47
|
+
when File
|
48
|
+
supports_file_attachment? ? image_from_path(@attachable.path) : raise_rails_like_error(@attachable)
|
49
|
+
when Pathname
|
50
|
+
supports_pathname_attachment? ? image_from_path(@attachable.to_s) : raise_rails_like_error(@attachable)
|
51
|
+
else
|
52
|
+
raise_rails_like_error(@attachable)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def image_from_tempfile_path(tempfile, file_representation)
|
57
|
+
if file_representation.is_a?(ActiveStorage::Blob)
|
58
|
+
file_representation.download { |chunk| tempfile.write(chunk) }
|
59
|
+
else
|
60
|
+
IO.copy_stream(file_representation, tempfile)
|
61
|
+
file_representation.rewind
|
62
|
+
end
|
63
|
+
|
64
|
+
tempfile.flush
|
65
|
+
tempfile.rewind
|
66
|
+
image_from_path(tempfile.path)
|
67
|
+
end
|
68
|
+
|
69
|
+
def analyzer_supported?
|
70
|
+
if @@supported_analyzers.key?(self)
|
71
|
+
@@supported_analyzers.fetch(self)
|
72
|
+
else
|
73
|
+
@@supported_analyzers[self] = supported?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def read_image
|
78
|
+
raise NotImplementedError
|
79
|
+
end
|
80
|
+
|
81
|
+
def image_from_path(path)
|
82
|
+
raise NotImplementedError
|
83
|
+
end
|
84
|
+
|
85
|
+
def rotated_image?(image)
|
86
|
+
raise NotImplementedError
|
87
|
+
end
|
88
|
+
|
89
|
+
def supported?
|
90
|
+
raise NotImplementedError
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveStorageValidations
|
4
|
+
# = Active Storage Null Analyzer
|
5
|
+
#
|
6
|
+
# This is a fallback analyzer when the attachable media type is not supported
|
7
|
+
# by our gem.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# ActiveStorage::Analyzer::NullAnalyzer.new(attachable).metadata
|
12
|
+
# # => {}
|
13
|
+
class Analyzer::NullAnalyzer < Analyzer
|
14
|
+
def metadata
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'shared/asv_attachable'
|
4
|
+
require_relative 'shared/asv_loggable'
|
5
|
+
|
6
|
+
module ActiveStorageValidations
|
7
|
+
# = Active Storage Validations \Analyzer
|
8
|
+
#
|
9
|
+
# This is an abstract base class for analyzers, which extract metadata from attachables.
|
10
|
+
# See ActiveStorageValidations::Analyzer::ImageAnalyzer for an example of a concrete subclass.
|
11
|
+
#
|
12
|
+
# Heavily (not to say 100%) inspired by Rails own ActiveStorage::Analyzer
|
13
|
+
class Analyzer
|
14
|
+
include ASVAttachable
|
15
|
+
include ASVLoggable
|
16
|
+
|
17
|
+
attr_reader :attachable
|
18
|
+
|
19
|
+
def initialize(attachable)
|
20
|
+
@attachable = attachable
|
21
|
+
end
|
22
|
+
|
23
|
+
# Override this method in a concrete subclass. Have it return a Hash of metadata.
|
24
|
+
def metadata
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def instrument(analyzer, &block)
|
31
|
+
ActiveSupport::Notifications.instrument("analyze.active_storage_validations", analyzer: analyzer, &block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|