active_storage_validations 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -0
- data/config/locales/en.yml +1 -0
- data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +20 -34
- data/lib/active_storage_validations/metadata.rb +18 -7
- data/lib/active_storage_validations/option_proc_unfolding.rb +2 -2
- data/lib/active_storage_validations/processable_image_validator.rb +43 -0
- data/lib/active_storage_validations/version.rb +1 -1
- data/lib/active_storage_validations.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3a3eccb847537354b9018f239171e4b3bdf363991d68c57844d87ecd0c14fcd
|
4
|
+
data.tar.gz: 432cfb9b3162db1d57446536de58d3b511c5a641d3da8c22123da56c877fd804
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0697168d7d38d2974772a89e203861099bf5ac476e147dbc14826d1cee98271e3ded9789fd0ada073853d835658165141a596eeeb92af1cf52850d8c8f76c110'
|
7
|
+
data.tar.gz: d9ad210e053d90e69df85a6ce21644ace0eb096376db6ff5c5b1e4fb31a6eff2d23db6000f1ef566fba33337d242b23b52dbdc9af481b10e9870248a88c15fac
|
data/README.md
CHANGED
@@ -19,6 +19,7 @@ This gems doing it for you. Just use `attached: true` or `content_type: 'image/p
|
|
19
19
|
* validates dimension of images/videos
|
20
20
|
* validates number of uploaded files (min/max required)
|
21
21
|
* validates aspect ratio (if square, portrait, landscape, is_16_9, ...)
|
22
|
+
* validates if file can be processed by MiniMagick or Vips
|
22
23
|
* custom error messages
|
23
24
|
* allow procs for dynamic determination of values
|
24
25
|
|
@@ -40,6 +41,7 @@ class User < ApplicationRecord
|
|
40
41
|
dimension: { width: { min: 800, max: 2400 },
|
41
42
|
height: { min: 600, max: 1800 }, message: 'is not given between dimension' }
|
42
43
|
validates :image, attached: true,
|
44
|
+
processable_image: true,
|
43
45
|
content_type: ['image/png', 'image/jpeg'],
|
44
46
|
aspect_ratio: :landscape
|
45
47
|
end
|
@@ -164,6 +166,7 @@ en:
|
|
164
166
|
aspect_ratio_not_landscape: "must be a landscape image"
|
165
167
|
aspect_ratio_is_not: "must have an aspect ratio of %{aspect_ratio}"
|
166
168
|
aspect_ratio_unknown: "has an unknown aspect ratio"
|
169
|
+
image_not_processable: "is not a valid image"
|
167
170
|
```
|
168
171
|
|
169
172
|
In some cases, Active Storage Validations provides variables to help you customize messages:
|
@@ -395,6 +398,8 @@ You are welcome to contribute.
|
|
395
398
|
= https://github.com/gr8bit
|
396
399
|
= https://github.com/codegeek319
|
397
400
|
= https://github.com/clwy-cn
|
401
|
+
= https://github.com/kukicola
|
402
|
+
= https://github.com/sobrinho
|
398
403
|
|
399
404
|
## License
|
400
405
|
|
data/config/locales/en.yml
CHANGED
@@ -19,11 +19,13 @@ module ActiveStorageValidations
|
|
19
19
|
|
20
20
|
def allowing(*types)
|
21
21
|
@allowed_types = types.flatten
|
22
|
+
either_allowing_or_rejecting
|
22
23
|
self
|
23
24
|
end
|
24
25
|
|
25
26
|
def rejecting(*types)
|
26
27
|
@rejected_types = types.flatten
|
28
|
+
either_allowing_or_rejecting
|
27
29
|
self
|
28
30
|
end
|
29
31
|
|
@@ -33,19 +35,29 @@ module ActiveStorageValidations
|
|
33
35
|
end
|
34
36
|
|
35
37
|
def failure_message
|
36
|
-
|
37
|
-
Expected #{@attribute_name}
|
38
|
+
message = ["Expected #{@attribute_name}"]
|
38
39
|
|
39
|
-
|
40
|
-
|
40
|
+
if @allowed_types
|
41
|
+
message << "Accept content types: #{allowed_types.join(", ")}"
|
42
|
+
message << "#{@missing_allowed_types.join(", ")} were rejected"
|
43
|
+
end
|
44
|
+
|
45
|
+
if @rejected_types
|
46
|
+
message << "Reject content types: #{rejected_types.join(", ")}"
|
47
|
+
message << "#{@missing_rejected_types.join(", ")} were accepted"
|
48
|
+
end
|
41
49
|
|
42
|
-
|
43
|
-
#{rejected_types_and_failures}
|
44
|
-
MESSAGE
|
50
|
+
message.join("\n")
|
45
51
|
end
|
46
52
|
|
47
53
|
protected
|
48
54
|
|
55
|
+
def either_allowing_or_rejecting
|
56
|
+
if @allowed_types && @rejected_types
|
57
|
+
raise ArgumentError, "You must specify either allowing or rejecting"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
49
61
|
def responds_to_methods
|
50
62
|
@subject.respond_to?(@attribute_name) &&
|
51
63
|
@subject.public_send(@attribute_name).respond_to?(:attach) &&
|
@@ -57,7 +69,7 @@ module ActiveStorageValidations
|
|
57
69
|
end
|
58
70
|
|
59
71
|
def rejected_types
|
60
|
-
@rejected_types ||
|
72
|
+
@rejected_types || []
|
61
73
|
end
|
62
74
|
|
63
75
|
def allowed_types_allowed?
|
@@ -70,22 +82,6 @@ module ActiveStorageValidations
|
|
70
82
|
@missing_rejected_types.none?
|
71
83
|
end
|
72
84
|
|
73
|
-
def accepted_types_and_failures
|
74
|
-
if @missing_allowed_types.present?
|
75
|
-
"#{@missing_allowed_types.join(", ")} were rejected."
|
76
|
-
else
|
77
|
-
"All were accepted successfully."
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def rejected_types_and_failures
|
82
|
-
if @missing_rejected_types.present?
|
83
|
-
"#{@missing_rejected_types.join(", ")} were accepted."
|
84
|
-
else
|
85
|
-
"All were rejected successfully."
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
85
|
def type_allowed?(type)
|
90
86
|
@subject.public_send(@attribute_name).attach(attachment_for(type))
|
91
87
|
@subject.validate
|
@@ -96,16 +92,6 @@ module ActiveStorageValidations
|
|
96
92
|
suffix = type.to_s.split('/').last
|
97
93
|
{ io: Tempfile.new('.'), filename: "test.#{suffix}", content_type: type }
|
98
94
|
end
|
99
|
-
|
100
|
-
private
|
101
|
-
|
102
|
-
def content_type_keys
|
103
|
-
if Rails.gem_version < Gem::Version.new('6.1.0')
|
104
|
-
Mime::LOOKUP.keys
|
105
|
-
else
|
106
|
-
Marcel::TYPES.keys
|
107
|
-
end
|
108
|
-
end
|
109
95
|
end
|
110
96
|
end
|
111
97
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module ActiveStorageValidations
|
2
2
|
class Metadata
|
3
|
+
class InvalidImageError < StandardError; end
|
4
|
+
|
3
5
|
attr_reader :file
|
4
6
|
|
5
7
|
def initialize(file)
|
@@ -10,7 +12,7 @@ module ActiveStorageValidations
|
|
10
12
|
def image_processor
|
11
13
|
Rails.application.config.active_storage.variant_processor
|
12
14
|
end
|
13
|
-
|
15
|
+
|
14
16
|
def exception_class
|
15
17
|
if image_processor == :vips && defined?(Vips)
|
16
18
|
Vips::Error
|
@@ -35,6 +37,16 @@ module ActiveStorageValidations
|
|
35
37
|
{ width: image.width, height: image.height }
|
36
38
|
end
|
37
39
|
end
|
40
|
+
rescue InvalidImageError
|
41
|
+
logger.info "Skipping image analysis because ImageMagick or Vips doesn't support the file"
|
42
|
+
{}
|
43
|
+
end
|
44
|
+
|
45
|
+
def valid?
|
46
|
+
read_image
|
47
|
+
true
|
48
|
+
rescue InvalidImageError
|
49
|
+
false
|
38
50
|
end
|
39
51
|
|
40
52
|
private
|
@@ -76,12 +88,9 @@ module ActiveStorageValidations
|
|
76
88
|
end
|
77
89
|
end
|
78
90
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
logger.info "Skipping image analysis because ImageMagick or Vips doesn't support the file"
|
83
|
-
{}
|
84
|
-
end
|
91
|
+
|
92
|
+
raise InvalidImageError unless valid_image?(image)
|
93
|
+
yield image
|
85
94
|
rescue LoadError, NameError
|
86
95
|
logger.info "Skipping image analysis because the mini_magick or ruby-vips gem isn't installed"
|
87
96
|
{}
|
@@ -93,6 +102,8 @@ module ActiveStorageValidations
|
|
93
102
|
end
|
94
103
|
|
95
104
|
def valid_image?(image)
|
105
|
+
return false unless image
|
106
|
+
|
96
107
|
image_processor == :vips && image.is_a?(Vips::Image) ? image.avg : image.valid?
|
97
108
|
rescue exception_class
|
98
109
|
false
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module ActiveStorageValidations
|
2
2
|
module OptionProcUnfolding
|
3
3
|
|
4
|
-
def unfold_procs(record, object, only_keys
|
4
|
+
def unfold_procs(record, object, only_keys)
|
5
5
|
case object
|
6
6
|
when Hash
|
7
|
-
object.merge(object) { |key, value| only_keys&.exclude?(key) ?
|
7
|
+
object.merge(object) { |key, value| only_keys&.exclude?(key) ? {} : unfold_procs(record, value, nil) }
|
8
8
|
when Array
|
9
9
|
object.map { |o| unfold_procs(record, o, only_keys) }
|
10
10
|
else
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'metadata.rb'
|
4
|
+
|
5
|
+
module ActiveStorageValidations
|
6
|
+
class ProcessableImageValidator < ActiveModel::EachValidator # :nodoc
|
7
|
+
include OptionProcUnfolding
|
8
|
+
|
9
|
+
if Rails.gem_version >= Gem::Version.new('6.0.0')
|
10
|
+
def validate_each(record, attribute, _value)
|
11
|
+
return true unless record.send(attribute).attached?
|
12
|
+
|
13
|
+
changes = record.attachment_changes[attribute.to_s]
|
14
|
+
return true if changes.blank?
|
15
|
+
|
16
|
+
files = Array.wrap(changes.is_a?(ActiveStorage::Attached::Changes::CreateMany) ? changes.attachables : changes.attachable)
|
17
|
+
|
18
|
+
files.each do |file|
|
19
|
+
add_error(record, attribute, :image_not_processable) unless Metadata.new(file).valid?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
# Rails 5
|
24
|
+
def validate_each(record, attribute, _value)
|
25
|
+
return true unless record.send(attribute).attached?
|
26
|
+
|
27
|
+
files = Array.wrap(record.send(attribute))
|
28
|
+
|
29
|
+
files.each do |file|
|
30
|
+
add_error(record, attribute, :image_not_processable) unless Metadata.new(file).valid?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def add_error(record, attribute, default_message)
|
38
|
+
message = options[:message].presence || default_message
|
39
|
+
return if record.errors.added?(attribute, message)
|
40
|
+
record.errors.add(attribute, message)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -9,6 +9,7 @@ require 'active_storage_validations/size_validator'
|
|
9
9
|
require 'active_storage_validations/limit_validator'
|
10
10
|
require 'active_storage_validations/dimension_validator'
|
11
11
|
require 'active_storage_validations/aspect_ratio_validator'
|
12
|
+
require 'active_storage_validations/processable_image_validator'
|
12
13
|
|
13
14
|
ActiveSupport.on_load(:active_record) do
|
14
15
|
send :include, ActiveStorageValidations
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_storage_validations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Kasyanchuk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-10-
|
11
|
+
date: 2022-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -216,6 +216,7 @@ files:
|
|
216
216
|
- lib/active_storage_validations/matchers/size_validator_matcher.rb
|
217
217
|
- lib/active_storage_validations/metadata.rb
|
218
218
|
- lib/active_storage_validations/option_proc_unfolding.rb
|
219
|
+
- lib/active_storage_validations/processable_image_validator.rb
|
219
220
|
- lib/active_storage_validations/railtie.rb
|
220
221
|
- lib/active_storage_validations/size_validator.rb
|
221
222
|
- lib/active_storage_validations/version.rb
|
@@ -239,7 +240,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
239
240
|
- !ruby/object:Gem::Version
|
240
241
|
version: '0'
|
241
242
|
requirements: []
|
242
|
-
rubygems_version: 3.3
|
243
|
+
rubygems_version: 3.2.3
|
243
244
|
signing_key:
|
244
245
|
specification_version: 4
|
245
246
|
summary: Validations for Active Storage
|