active_storage_validations 0.6.1 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5c26b96ccb72cbb6ca57cf20df590f334703e2e30e51e4f2ccc1ff5891a74f5
4
- data.tar.gz: 401043685c41419a8ab9018de42f844a15d2d1219b74f0f1c37473840129bdea
3
+ metadata.gz: 455224d1bb911fd68b4d89e76bcbac8d262db1119bfe30d20a46e5483c48f428
4
+ data.tar.gz: 3c3189898c56806b8efe8601f4f7b530af365cc0ae34b88f5d738ebd5a27a43b
5
5
  SHA512:
6
- metadata.gz: c0cda1fa4d225a0bf381fb08a2db575d1f3a2baef5b85bd954473455a26c454af2efc0de4b40ecc9156a9b553f38cd8653cb78f7c70a05c87291d0a0a01df94e
7
- data.tar.gz: c98ff98594d610a57cc649f66bc7a5bd3ac135723dae52f68d807e6ccf0131e76fc5e9e577708a36badf20583ce08f61b7a0c9f3286e53f119e4e39f04714407
6
+ metadata.gz: f13f49e78f5b04d96e36c46de7deb7c4d18abbb95f6a0f1fcb60a4e68e6c58b0280a9038fac22c085b55c778854775bfc3f9d690beb3cd11bf4a5ae2b9a81d4d
7
+ data.tar.gz: 2a6062552f5e05701f8efde71dbfccbebd64a43a9adc637d1b74c70676b9e289e7089cd2d79eb25042c22cdebf3723991c73dc86a34d1852cf985186cc3eeae3
data/README.md CHANGED
@@ -11,6 +11,7 @@ This gems doing it for you. Just use `attached: true` or `content_type: 'image/p
11
11
  * validates if file(s) attached
12
12
  * validates content type
13
13
  * validates size of files
14
+ * validates dimension of images/videos
14
15
  * validates number of uploaded files (min/max required)
15
16
  * custom error messages
16
17
 
@@ -25,8 +26,11 @@ class User < ApplicationRecord
25
26
 
26
27
  validates :name, presence: true
27
28
 
28
- validates :avatar, attached: true, content_type: 'image/png'
29
- validates :photos, attached: true, content_type: ['image/png', 'image/jpg', 'image/jpeg']
29
+ validates :avatar, attached: true, content_type: 'image/png',
30
+ dimension: { width: 200, height: 200 }
31
+ validates :photos, attached: true, content_type: ['image/png', 'image/jpg', 'image/jpeg'],
32
+ dimension: { width: { min: 800, max: 2400 },
33
+ height: { min: 600, max: 1800 }, message: 'is not given between dimension' }
30
34
  end
31
35
  ```
32
36
 
@@ -46,19 +50,60 @@ class Project < ApplicationRecord
46
50
  end
47
51
  ```
48
52
 
53
+ ### More examples
54
+
55
+ - Dimension validation with `width`, `height` and `in`.
56
+
57
+ ```ruby
58
+ class User < ApplicationRecord
59
+ has_one_attached :avatar
60
+ has_many_attached :photos
61
+
62
+ validates :avatar, dimension: { width: { in: 80..100 }, message: 'is not given between dimension' }
63
+ validates :photos, dimension: { height: { in: 600..1800 } }
64
+ end
65
+ ```
66
+
67
+ - Dimension validation with `min` and `max` range for width and height.
68
+
69
+ ```ruby
70
+ class User < ApplicationRecord
71
+ has_one_attached :avatar
72
+ has_many_attached :photos
73
+
74
+ validates :avatar, dimension: { min: 200..100 }
75
+ # Equivalent to:
76
+ # validates :avatar, dimension: { width: { min: 200 }, height: { min: 100 } }
77
+ validates :photos, dimension: { min: 200..100, max: 400..200 }
78
+ # Equivalent to:
79
+ # validates :avatar, dimension: { width: { min: 200, max: 400 }, height: { min: 100, max: 200 } }
80
+ end
81
+ ```
82
+
49
83
  ## Internationalization (I18n)
50
84
 
51
- Active Storage Validations use I18n for errors messages. For this add there keys in your translation file :
85
+ Active Storage Validations use I18n for errors messages. For this add there keys in your translation file:
52
86
 
53
87
  ```yml
54
88
  en:
55
89
  errors:
56
90
  messages:
57
91
  content_type_invalid: "has an invalid content type"
92
+ file_size_out_of_range: "size %{file_size} is not between required range"
58
93
  limit_out_of_range: "total number is out of range"
94
+ dimension_min_inclusion: "must be greater than or equal to %{width} x %{height} pixel."
95
+ dimension_max_inclusion: "must be less than or equal to %{width} x %{height} pixel."
96
+ dimension_width_inclusion: "width is not included between %{min} and %{max} pixel."
97
+ dimension_height_inclusion: "height is not included between %{min} and %{max} pixel."
98
+ dimension_width_greater_than_or_equal_to: "width must be greater than or equal to %{length} pixel."
99
+ dimension_height_greater_than_or_equal_to: "height must be greater than or equal to %{length} pixel."
100
+ dimension_width_less_than_or_equal_to: "width must be less than or equal to %{length} pixel."
101
+ dimension_height_less_than_or_equal_to: "height must be less than or equal to %{length} pixel."
102
+ dimension_width_equal_to: "width must be equal to %{length} pixel."
103
+ dimension_height_equal_to: "height must be equal to %{length} pixel."
59
104
  ```
60
105
 
61
- In some cases Active Storage Validations provides variables to help you customize messages :
106
+ In some cases Active Storage Validations provides variables to help you customize messages:
62
107
 
63
108
  The "content_type_invalid" key has two variables that you can use, a variable named "content_type" containing the content type of the send file and a variable named "authorized_type" containing the list of authorized content types.
64
109
 
@@ -87,6 +132,7 @@ gem 'active_storage_validations'
87
132
  ```
88
133
 
89
134
  And then execute:
135
+
90
136
  ```bash
91
137
  $ bundle
92
138
  ```
@@ -98,9 +144,12 @@ Very simple example of validation with file attached, content type check and cus
98
144
  [![Sample](https://raw.githubusercontent.com/igorkasyanchuk/active_storage_validations/master/docs/preview.png)](https://raw.githubusercontent.com/igorkasyanchuk/active_storage_validations/master/docs/preview.png)
99
145
 
100
146
  ## Todo
147
+
101
148
  * verify with remote storages (s3, etc)
102
- * verify how it works with direct upload
103
- * better error message when content_size is invalid
149
+ * verify how it works with direct upload
150
+ * better error message when content_size is invalid
151
+ * add more translations
152
+ * add aspect ratio validation
104
153
 
105
154
  ## Tests & Contributing
106
155
 
@@ -110,7 +159,7 @@ To play with app `cd test/dummy` and `rails s -b 0.0.0.0` (before `rails db:migr
110
159
 
111
160
  ## Known issues
112
161
 
113
- - there is an issue in Rails which it possible to get if you have aadded a validation and generating for example an image preview of atachments. It can be fixed with this:
162
+ - There is an issue in Rails which it possible to get if you have added a validation and generating for example an image preview of attachments. It can be fixed with this:
114
163
 
115
164
  ```
116
165
  <% if @user.avatar.attached? && @user.avatar.attachment.blob.present? && @user.avatar.attachment.blob.persisted? %>
@@ -129,6 +178,8 @@ You are welcome to contribute.
129
178
  - https://github.com/Uysim
130
179
  - https://github.com/D-system
131
180
  - https://github.com/ivanelrey
181
+ - https://github.com/phlegx
132
182
 
133
183
  ## License
184
+
134
185
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,16 @@
1
+ de:
2
+ errors:
3
+ messages:
4
+ content_type_invalid: "hat einen ungültigen Dateityp"
5
+ file_size_out_of_range: "Dateigröße von %{file_size} ist außerhalb des erlaubten Bereichs"
6
+ limit_out_of_range: "Anzahl ist außerhalb des gültigen Bereichs"
7
+ dimension_min_inclusion: "muss größer oder gleich %{width} x %{height} Pixel sein"
8
+ dimension_max_inclusion: "muss kleiner oder gleich %{width} x %{height} Pixel sein"
9
+ dimension_width_inclusion: "Breite ist nicht zwischen %{min} und %{max} Pixel enthalten"
10
+ dimension_height_inclusion: "Höhe ist nicht zwischen %{min} und %{max} Pixel enthalten"
11
+ dimension_width_greater_than_or_equal_to: "Breite muss größer oder gleich %{length} Pixel sein"
12
+ dimension_height_greater_than_or_equal_to: "Höhe muss größer oder gleich %{length} Pixel sein"
13
+ dimension_width_less_than_or_equal_to: "Breite muss kleiner oder gleich %{length} Pixel sein"
14
+ dimension_height_less_than_or_equal_to: "Höhe muss kleiner oder gleich %{length} Pixel sein"
15
+ dimension_width_equal_to: "Breite muss genau %{length} Pixel sein"
16
+ dimension_height_equal_to: "Höhe muss genau %{length} Pixel sein"
@@ -2,4 +2,15 @@ en:
2
2
  errors:
3
3
  messages:
4
4
  content_type_invalid: "has an invalid content type"
5
+ file_size_out_of_range: "size %{file_size} is not between required range"
5
6
  limit_out_of_range: "total number is out of range"
7
+ dimension_min_inclusion: "must be greater than or equal to %{width} x %{height} pixel"
8
+ dimension_max_inclusion: "must be less than or equal to %{width} x %{height} pixel"
9
+ dimension_width_inclusion: "width is not included between %{min} and %{max} pixel"
10
+ dimension_height_inclusion: "height is not included between %{min} and %{max} pixel"
11
+ dimension_width_greater_than_or_equal_to: "width must be greater than or equal to %{length} pixel"
12
+ dimension_height_greater_than_or_equal_to: "height must be greater than or equal to %{length} pixel"
13
+ dimension_width_less_than_or_equal_to: "width must be less than or equal to %{length} pixel"
14
+ dimension_height_less_than_or_equal_to: "height must be less than or equal to %{length} pixel"
15
+ dimension_width_equal_to: "width must be equal to %{length} pixel"
16
+ dimension_height_equal_to: "height must be equal to %{length} pixel"
@@ -6,6 +6,7 @@ require 'active_storage_validations/attached_validator'
6
6
  require 'active_storage_validations/content_type_validator'
7
7
  require 'active_storage_validations/size_validator'
8
8
  require 'active_storage_validations/limit_validator'
9
+ require 'active_storage_validations/dimension_validator'
9
10
 
10
11
  ActiveSupport.on_load(:active_record) do
11
12
  send :include, ActiveStorageValidations
@@ -3,11 +3,9 @@
3
3
  module ActiveStorageValidations
4
4
  class ContentTypeValidator < ActiveModel::EachValidator # :nodoc:
5
5
  def validate_each(record, attribute, _value)
6
- files = record.send(attribute)
6
+ return true if !record.send(attribute).attached? || types.empty?
7
7
 
8
- return true if !files.attached? || types.empty?
9
-
10
- files = Array.wrap(files)
8
+ files = Array.wrap(record.send(attribute))
11
9
 
12
10
  errors_options = { authorized_types: types_to_human_format }
13
11
  errors_options[:message] = options[:message] if options[:message].present?
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveStorageValidations
4
+ class DimensionValidator < ActiveModel::EachValidator # :nodoc
5
+ AVAILABLE_CHECKS = %i[width height min max].freeze
6
+
7
+ def initialize(options)
8
+ [:width, :height].each do |length|
9
+ if options[length] and options[length].is_a?(Hash)
10
+ if range = options[length][:in]
11
+ raise ArgumentError, ":in must be a Range" unless range.is_a?(Range)
12
+ options[length][:min], options[length][:max] = range.min, range.max
13
+ end
14
+ end
15
+ end
16
+ [:min, :max].each do |dim|
17
+ if range = options[dim]
18
+ raise ArgumentError, ":#{dim} must be a Range (width..height)" unless range.is_a?(Range)
19
+ options[:width] = { dim => range.first }
20
+ options[:height] = { dim => range.last }
21
+ end
22
+ end
23
+ super
24
+ end
25
+
26
+
27
+ def check_validity!
28
+ return true if AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
29
+ raise ArgumentError, 'You must pass either :width, :height, :min or :max to the validator'
30
+ end
31
+
32
+
33
+ def validate_each(record, attribute, _value)
34
+ return true unless record.send(attribute).attached?
35
+
36
+ files = Array.wrap(record.send(attribute))
37
+
38
+ files.each do |file|
39
+ # Analyze file first if not analyzed to get all required metadata.
40
+ file.analyze; file.reload unless file.analyzed?
41
+
42
+ # File has no dimension and no width and height in metadata.
43
+ raise StandardError, 'File has no dimension and no width and height in metadata' unless (['width', 'height'] - file.metadata.keys).empty?
44
+
45
+ next if dimension_valid?(record, attribute, file.metadata)
46
+ break
47
+ end
48
+ end
49
+
50
+
51
+ def dimension_valid?(record, attribute, file_metadata)
52
+ valid = true
53
+
54
+ # Validation based on checks :min and :max (:min, :max has higher priority to :width, :height).
55
+ if options[:min] || options[:max]
56
+ if options[:min] && (
57
+ (options[:width][:min] && file_metadata[:width] < options[:width][:min]) ||
58
+ (options[:height][:min] && file_metadata[:height] < options[:height][:min])
59
+ )
60
+ valid = record.errors.add(attribute, options[:message].presence || :"dimension_min_inclusion", width: options[:width][:min], height: options[:height][:min]).empty?
61
+ end
62
+ if options[:max] && (
63
+ (options[:width][:max] && file_metadata[:width] > options[:width][:max]) ||
64
+ (options[:height][:max] && file_metadata[:height] > options[:height][:max])
65
+ )
66
+ valid = record.errors.add(attribute, options[:message].presence || :"dimension_max_inclusion", width: options[:width][:max], height: options[:height][:max]).empty?
67
+ end
68
+
69
+ # Validation based on checks :width and :height.
70
+ else
71
+ [:width, :height].each do |length|
72
+ next unless options[length]
73
+ if options[length].is_a?(Hash)
74
+ if options[length][:in] && (file_metadata[length] < options[length][:min] || file_metadata[length] > options[length][:max])
75
+ valid = record.errors.add(attribute, options[:message].presence || :"dimension_#{length}_inclusion", min: options[length][:min], max: options[length][:max]).empty?
76
+ else
77
+ if options[length][:min] && file_metadata[length] < options[length][:min]
78
+ valid = record.errors.add(attribute, options[:message].presence || :"dimension_#{length}_greater_than_or_equal_to", length: options[length][:min]).empty?
79
+ end
80
+ if options[length][:max] && file_metadata[length] > options[length][:max]
81
+ valid = record.errors.add(attribute, options[:message].presence || :"dimension_#{length}_less_than_or_equal_to", length: options[length][:max]).empty?
82
+ end
83
+ end
84
+ else
85
+ if file_metadata[length] != options[length]
86
+ valid = record.errors.add(attribute, options[:message].presence || :"dimension_#{length}_equal_to", length: options[length]).empty?
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ valid
93
+ end
94
+
95
+ end
96
+ end
@@ -11,11 +11,9 @@ module ActiveStorageValidations
11
11
  end
12
12
 
13
13
  def validate_each(record, attribute, _value)
14
- files = record.send(attribute)
14
+ return true unless record.send(attribute).attached?
15
15
 
16
- return true unless files.attached?
17
-
18
- files = Array.wrap(files)
16
+ files = Array.wrap(record.send(attribute))
19
17
 
20
18
  errors_options = {}
21
19
  errors_options[:min] = options[:min]
@@ -18,10 +18,14 @@ module ActiveStorageValidations
18
18
 
19
19
  files = Array.wrap(record.send(attribute))
20
20
 
21
+ errors_options = {}
22
+ errors_options[:message] = options[:message] if options[:message].present?
23
+
21
24
  files.each do |file|
22
25
  next if content_size_valid?(file.blob.byte_size)
23
26
 
24
- record.errors.add(attribute, options[:message].presence || "size #{number_to_human_size(file.blob.byte_size)} is not between required range")
27
+ errors_options[:file_size] = number_to_human_size(file.blob.byte_size)
28
+ record.errors.add(attribute, :file_size_out_of_range, errors_options)
25
29
  break
26
30
  end
27
31
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveStorageValidations
4
- VERSION = '0.6.1'
4
+ VERSION = '0.7.0'
5
5
  end
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: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Kasyanchuk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-05 00:00:00.000000000 Z
11
+ date: 2019-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 5.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: image_processing
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.8'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.8'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: pry
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -76,10 +90,12 @@ files:
76
90
  - MIT-LICENSE
77
91
  - README.md
78
92
  - Rakefile
93
+ - config/locales/de.yml
79
94
  - config/locales/en.yml
80
95
  - lib/active_storage_validations.rb
81
96
  - lib/active_storage_validations/attached_validator.rb
82
97
  - lib/active_storage_validations/content_type_validator.rb
98
+ - lib/active_storage_validations/dimension_validator.rb
83
99
  - lib/active_storage_validations/engine.rb
84
100
  - lib/active_storage_validations/limit_validator.rb
85
101
  - lib/active_storage_validations/railtie.rb
@@ -105,8 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
121
  - !ruby/object:Gem::Version
106
122
  version: '0'
107
123
  requirements: []
108
- rubyforge_project:
109
- rubygems_version: 2.7.7
124
+ rubygems_version: 3.0.2
110
125
  signing_key:
111
126
  specification_version: 4
112
127
  summary: Validations for Active Storage