active_storage_validations 0.6.1 → 0.7.0

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: 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