active_storage_validations 1.0.0 → 1.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3fa76d4dc0cf3d5bffc62dcc604d6f38edf165931c94d9314288606b6205a56
4
- data.tar.gz: 9adb8defb7d44c65a4ea67cf632087d423a1c2c44b918822653638132f8beef4
3
+ metadata.gz: a7b0998dd3938eee42be49dc3d831eaea8482001a0e03227f61cc0330050a3a1
4
+ data.tar.gz: 72a82d3965c026f118bd1d4bc7e0338a2313c43394f780577cbbe944356caf39
5
5
  SHA512:
6
- metadata.gz: 9a2d4ce5113ed8a018d71b4d40b01c11946892eea438d2b7481b7007511022695998271d06215e6ddf07929d4e057fc8411a2b0babcb508f70af6140fb4d65c8
7
- data.tar.gz: 3d31b7848f3174a8df330e5410b5c1aec018a243bfdefa2abdfc8eee9d9f652e34645853da38dee2b1627ef42efa679fabdac11e896a6a37ce9cd0ed1c5569f7
6
+ metadata.gz: a0117ebfeed615802b45e4f341eea6e5676d8189e4a4bf81faa565e8e49f418142a7aa3466a6cd3f92761e029ff54f58298259ae390682649d5ad5950e52d787
7
+ data.tar.gz: e6ab73fe085110abe6b346e3eb837a59ca81b499707cdbf8df9773647feefd7b11ca4e5be03329226954a43756a5ceda9550169ff5402a6f275cfc7b4865f6b9
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:
@@ -311,9 +314,9 @@ end
311
314
 
312
315
  To run tests in root folder of gem:
313
316
 
314
- * `BUNDLE_GEMFILE=gemfiles/rails_5_2.gemfile bundle exec rake test` to run for Rails 5.2
315
317
  * `BUNDLE_GEMFILE=gemfiles/rails_6_0.gemfile bundle exec rake test` to run for Rails 6.0
316
318
  * `BUNDLE_GEMFILE=gemfiles/rails_6_1.gemfile bundle exec rake test` to run for Rails 6.1
319
+ * `BUNDLE_GEMFILE=gemfiles/rails_7_0.gemfile bundle exec rake test` to run for Rails 7.0
317
320
  * `BUNDLE_GEMFILE=gemfiles/rails_next.gemfile bundle exec rake test` to run for Rails main branch
318
321
 
319
322
  Snippet to run in console:
@@ -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"
@@ -33,15 +33,19 @@ module ActiveStorageValidations
33
33
  end
34
34
 
35
35
  def failure_message
36
- <<~MESSAGE
37
- Expected #{@attribute_name}
36
+ message = ["Expected #{@attribute_name}"]
38
37
 
39
- Accept content types: #{allowed_types.join(", ")}
40
- #{accepted_types_and_failures}
38
+ if @allowed_types
39
+ message << "Accept content types: #{allowed_types.join(", ")}"
40
+ message << "#{@missing_allowed_types.join(", ")} were rejected"
41
+ end
42
+
43
+ if @rejected_types
44
+ message << "Reject content types: #{rejected_types.join(", ")}"
45
+ message << "#{@missing_rejected_types.join(", ")} were accepted"
46
+ end
41
47
 
42
- Reject content types: #{rejected_types.join(", ")}
43
- #{rejected_types_and_failures}
44
- MESSAGE
48
+ message.join("\n")
45
49
  end
46
50
 
47
51
  protected
@@ -57,7 +61,7 @@ module ActiveStorageValidations
57
61
  end
58
62
 
59
63
  def rejected_types
60
- @rejected_types || (content_type_keys - allowed_types)
64
+ @rejected_types || []
61
65
  end
62
66
 
63
67
  def allowed_types_allowed?
@@ -70,22 +74,6 @@ module ActiveStorageValidations
70
74
  @missing_rejected_types.none?
71
75
  end
72
76
 
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
77
  def type_allowed?(type)
90
78
  @subject.public_send(@attribute_name).attach(attachment_for(type))
91
79
  @subject.validate
@@ -96,16 +84,6 @@ module ActiveStorageValidations
96
84
  suffix = type.to_s.split('/').last
97
85
  { io: Tempfile.new('.'), filename: "test.#{suffix}", content_type: type }
98
86
  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
87
  end
110
88
  end
111
89
  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.2'
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.2
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-22 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