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 +4 -4
- data/README.md +58 -7
- data/config/locales/de.yml +16 -0
- data/config/locales/en.yml +11 -0
- data/lib/active_storage_validations.rb +1 -0
- data/lib/active_storage_validations/content_type_validator.rb +2 -4
- data/lib/active_storage_validations/dimension_validator.rb +96 -0
- data/lib/active_storage_validations/limit_validator.rb +2 -4
- data/lib/active_storage_validations/size_validator.rb +5 -1
- data/lib/active_storage_validations/version.rb +1 -1
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 455224d1bb911fd68b4d89e76bcbac8d262db1119bfe30d20a46e5483c48f428
|
4
|
+
data.tar.gz: 3c3189898c56806b8efe8601f4f7b530af365cc0ae34b88f5d738ebd5a27a43b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
[](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
|
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
|
-
-
|
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"
|
data/config/locales/en.yml
CHANGED
@@ -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
|
-
|
6
|
+
return true if !record.send(attribute).attached? || types.empty?
|
7
7
|
|
8
|
-
|
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
|
-
|
14
|
+
return true unless record.send(attribute).attached?
|
15
15
|
|
16
|
-
|
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
|
-
|
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
|
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.
|
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-
|
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
|
-
|
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
|