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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3fa76d4dc0cf3d5bffc62dcc604d6f38edf165931c94d9314288606b6205a56
4
- data.tar.gz: 9adb8defb7d44c65a4ea67cf632087d423a1c2c44b918822653638132f8beef4
3
+ metadata.gz: b3a3eccb847537354b9018f239171e4b3bdf363991d68c57844d87ecd0c14fcd
4
+ data.tar.gz: 432cfb9b3162db1d57446536de58d3b511c5a641d3da8c22123da56c877fd804
5
5
  SHA512:
6
- metadata.gz: 9a2d4ce5113ed8a018d71b4d40b01c11946892eea438d2b7481b7007511022695998271d06215e6ddf07929d4e057fc8411a2b0babcb508f70af6140fb4d65c8
7
- data.tar.gz: 3d31b7848f3174a8df330e5410b5c1aec018a243bfdefa2abdfc8eee9d9f652e34645853da38dee2b1627ef42efa679fabdac11e896a6a37ce9cd0ed1c5569f7
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
 
@@ -20,3 +20,4 @@ en:
20
20
  aspect_ratio_not_landscape: "must be a landscape image"
21
21
  aspect_ratio_is_not: "must have an aspect ratio of %{aspect_ratio}"
22
22
  aspect_ratio_unknown: "has an unknown aspect ratio"
23
+ image_not_processable: "is not a valid image"
@@ -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
- <<~MESSAGE
37
- Expected #{@attribute_name}
38
+ message = ["Expected #{@attribute_name}"]
38
39
 
39
- Accept content types: #{allowed_types.join(", ")}
40
- #{accepted_types_and_failures}
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
- Reject content types: #{rejected_types.join(", ")}
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 || (content_type_keys - allowed_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
- if image && valid_image?(image)
80
- yield image
81
- else
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 = nil)
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) ? unfold_procs(record, value, []) : unfold_procs(record, value) }
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveStorageValidations
4
- VERSION = '1.0.0'
4
+ VERSION = '1.0.1'
5
5
  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.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-05 00:00:00.000000000 Z
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.7
243
+ rubygems_version: 3.2.3
243
244
  signing_key:
244
245
  specification_version: 4
245
246
  summary: Validations for Active Storage