active_storage_validations 1.0.3 → 1.1.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 +60 -13
- data/config/locales/de.yml +5 -1
- data/config/locales/en.yml +5 -1
- data/config/locales/es.yml +5 -1
- data/config/locales/fr.yml +5 -1
- data/config/locales/it.yml +5 -1
- data/config/locales/ja.yml +5 -1
- data/config/locales/nl.yml +5 -1
- data/config/locales/pl.yml +5 -1
- data/config/locales/pt-BR.yml +6 -1
- data/config/locales/ru.yml +5 -1
- data/config/locales/sv.yml +23 -0
- data/config/locales/tr.yml +5 -1
- data/config/locales/uk.yml +5 -1
- data/config/locales/vi.yml +5 -1
- data/config/locales/zh-CN.yml +5 -1
- data/lib/active_storage_validations/aspect_ratio_validator.rb +19 -16
- data/lib/active_storage_validations/attached_validator.rb +4 -3
- data/lib/active_storage_validations/content_type_validator.rb +4 -3
- data/lib/active_storage_validations/dimension_validator.rb +34 -18
- data/lib/active_storage_validations/error_handler.rb +18 -0
- data/lib/active_storage_validations/limit_validator.rb +8 -6
- data/lib/active_storage_validations/matchers/attached_validator_matcher.rb +12 -2
- data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +22 -2
- data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +11 -7
- data/lib/active_storage_validations/matchers/size_validator_matcher.rb +27 -18
- data/lib/active_storage_validations/metadata.rb +23 -11
- data/lib/active_storage_validations/processable_image_validator.rb +5 -10
- data/lib/active_storage_validations/size_validator.rb +8 -5
- data/lib/active_storage_validations/version.rb +1 -1
- data/lib/active_storage_validations.rb +3 -0
- metadata +19 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f41ec7bce11523af0deebbac7aac427c19a0c5f9788c3719ff94122468781a79
|
4
|
+
data.tar.gz: aef955507fb8c8b3a42a27820ea213347f03c49373eef27b44a99e764456b77d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19b37c9826cda8b8864631c77e5a3eabf1f8164279ad2c64beebd5f60b82e03dc39bdb466d516fa161b81b9ca3b0d98913bd04b32950e3b98b3ab28632941a91
|
7
|
+
data.tar.gz: d12ffa26482ae3e3feead8c3b658f0aa4331dd08a16b87ba9d7e639bc78a25499bda8ba27ae81c0db39d7d0c9bd59bc9f136b56500d97ae5a0f8b2bd85a5f112
|
data/README.md
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
[](https://github.com/igorkasyanchuk/active_storage_validations/actions)
|
7
7
|
[](https://www.railsjazz.com)
|
8
8
|
[](https://www.patreon.com/igorkasyanchuk)
|
9
|
+
[](https://opensource-heroes.com/r/igorkasyanchuk/active_storage_validations)
|
9
10
|
|
10
11
|
If you are using `active_storage` gem and you want to add simple validations for it, like presence or content_type you need to write a custom validation method.
|
11
12
|
|
@@ -148,7 +149,11 @@ en:
|
|
148
149
|
errors:
|
149
150
|
messages:
|
150
151
|
content_type_invalid: "has an invalid content type"
|
151
|
-
|
152
|
+
file_size_not_less_than: "file size must be less than %{max_size} (current size is %{file_size})"
|
153
|
+
file_size_not_less_than_or_equal_to: "file size must be less than or equal to %{max_size} (current size is %{file_size})"
|
154
|
+
file_size_not_greater_than: "file size must be greater than %{min_size} (current size is %{file_size})"
|
155
|
+
file_size_not_greater_than_or_equal_to: "file size must be greater than or equal to %{min_size} (current size is %{file_size})"
|
156
|
+
file_size_not_between: "file size must be between %{min_size} and %{max_size} (current size is %{file_size})"
|
152
157
|
limit_out_of_range: "total number is out of range"
|
153
158
|
image_metadata_missing: "is not a valid image"
|
154
159
|
dimension_min_inclusion: "must be greater than or equal to %{width} x %{height} pixel."
|
@@ -171,17 +176,21 @@ en:
|
|
171
176
|
|
172
177
|
In some cases, Active Storage Validations provides variables to help you customize messages:
|
173
178
|
|
174
|
-
|
175
|
-
|
176
|
-
|
179
|
+
### Content type
|
180
|
+
The `content_type_invalid` key has two variables that you can use:
|
181
|
+
- `content_type` containing the content type of the sent file
|
182
|
+
- `authorized_types` containing the list of authorized content types
|
177
183
|
|
178
184
|
For example :
|
179
185
|
|
180
186
|
```yml
|
181
|
-
content_type_invalid: "has an invalid content type : %{content_type}"
|
187
|
+
content_type_invalid: "has an invalid content type : %{content_type}, authorized types are %{authorized_types}"
|
182
188
|
```
|
183
189
|
|
184
|
-
|
190
|
+
### Number of files
|
191
|
+
The `limit_out_of_range` key supports two variables that you can use:
|
192
|
+
- `min` containing the minimum number of files
|
193
|
+
- `max` containing the maximum number of files
|
185
194
|
|
186
195
|
For example :
|
187
196
|
|
@@ -189,6 +198,28 @@ For example :
|
|
189
198
|
limit_out_of_range: "total number is out of range. range: [%{min}, %{max}]"
|
190
199
|
```
|
191
200
|
|
201
|
+
### File size
|
202
|
+
The keys starting with `file_size_not_` support three variables that you can use:
|
203
|
+
- `file_size` containing the current file size
|
204
|
+
- `min` containing the minimum file size
|
205
|
+
- `max` containing the maxmimum file size
|
206
|
+
|
207
|
+
For example :
|
208
|
+
|
209
|
+
```yml
|
210
|
+
file_size_not_between: "file size must be between %{min_size} and %{max_size} (current size is %{file_size})"
|
211
|
+
```
|
212
|
+
|
213
|
+
### Aspect ratio
|
214
|
+
The keys starting with `aspect_ratio_` support one variable that you can use:
|
215
|
+
- `aspect_ratio` containing the expected aspect ratio, especially usefull for custom aspect ratio
|
216
|
+
|
217
|
+
For example :
|
218
|
+
|
219
|
+
```yml
|
220
|
+
aspect_ratio_is_not: "must be a %{aspect_ratio} image"
|
221
|
+
```
|
222
|
+
|
192
223
|
## Installation
|
193
224
|
|
194
225
|
Add this line to your application's Gemfile:
|
@@ -239,9 +270,11 @@ Example (Note that the options are chainable):
|
|
239
270
|
```ruby
|
240
271
|
describe User do
|
241
272
|
it { is_expected.to validate_attached_of(:avatar) }
|
273
|
+
it { is_expected.to validate_attached_of(:avatar).with_message('must not be blank') }
|
242
274
|
|
243
275
|
it { is_expected.to validate_content_type_of(:avatar).allowing('image/png', 'image/gif') }
|
244
276
|
it { is_expected.to validate_content_type_of(:avatar).rejecting('text/plain', 'text/xml') }
|
277
|
+
it { is_expected.to validate_content_type_of(:avatar).rejecting('text/plain', 'text/xml').with_message("must be an authorized type") }
|
245
278
|
|
246
279
|
it { is_expected.to validate_dimensions_of(:avatar).width(250) }
|
247
280
|
it { is_expected.to validate_dimensions_of(:avatar).height(200) }
|
@@ -256,6 +289,7 @@ describe User do
|
|
256
289
|
it { is_expected.to validate_size_of(:avatar).less_than(50.kilobytes) }
|
257
290
|
it { is_expected.to validate_size_of(:avatar).less_than_or_equal_to(50.kilobytes) }
|
258
291
|
it { is_expected.to validate_size_of(:avatar).greater_than(1.kilobyte) }
|
292
|
+
it { is_expected.to validate_size_of(:avatar).greater_than(1.kilobyte).with_message('is not in required file size range') }
|
259
293
|
it { is_expected.to validate_size_of(:avatar).greater_than_or_equal_to(1.kilobyte) }
|
260
294
|
it { is_expected.to validate_size_of(:avatar).between(100..500.kilobytes) }
|
261
295
|
end
|
@@ -281,9 +315,11 @@ Example (Note that the options are chainable):
|
|
281
315
|
```ruby
|
282
316
|
class UserTest < ActiveSupport::TestCase
|
283
317
|
should validate_attached_of(:avatar)
|
318
|
+
should validate_attached_of(:avatar).with_message('must not be blank')
|
284
319
|
|
285
320
|
should validate_content_type_of(:avatar).allowing('image/png', 'image/gif')
|
286
321
|
should validate_content_type_of(:avatar).rejecting('text/plain', 'text/xml')
|
322
|
+
should validate_content_type_of(:avatar).rejecting('text/plain', 'text/xml').with_message("must be an authorized type")
|
287
323
|
|
288
324
|
should validate_dimensions_of(:avatar).width(250)
|
289
325
|
should validate_dimensions_of(:avatar).height(200)
|
@@ -298,6 +334,7 @@ class UserTest < ActiveSupport::TestCase
|
|
298
334
|
should validate_size_of(:avatar).less_than(50.kilobytes)
|
299
335
|
should validate_size_of(:avatar).less_than_or_equal_to(50.kilobytes)
|
300
336
|
should validate_size_of(:avatar).greater_than(1.kilobyte)
|
337
|
+
should validate_size_of(:avatar).greater_than(1.kilobyte).with_message('is not in required file size range')
|
301
338
|
should validate_size_of(:avatar).greater_than_or_equal_to(1.kilobyte)
|
302
339
|
should validate_size_of(:avatar).between(100..500.kilobytes)
|
303
340
|
end
|
@@ -307,7 +344,6 @@ end
|
|
307
344
|
|
308
345
|
* verify with remote storages (s3, etc)
|
309
346
|
* verify how it works with direct upload
|
310
|
-
* better error message when content_size is invalid
|
311
347
|
* add more translations
|
312
348
|
|
313
349
|
## Tests & Contributing
|
@@ -345,8 +381,12 @@ BUNDLE_GEMFILE=gemfiles/rails_next.gemfile bundle exec rake test
|
|
345
381
|
This is a Rails issue, and is fixed in Rails 6.
|
346
382
|
|
347
383
|
## Contributing
|
384
|
+
|
348
385
|
You are welcome to contribute.
|
349
386
|
|
387
|
+
[<img src="https://opensource-heroes.com/svg/embed/igorkasyanchuk/active_storage_validations"
|
388
|
+
/>](https://opensource-heroes.com/r/igorkasyanchuk/active_storage_validations)
|
389
|
+
|
350
390
|
## Contributors (BIG THANK YOU)
|
351
391
|
- https://github.com/schweigert
|
352
392
|
- https://github.com/tleneveu
|
@@ -394,12 +434,19 @@ You are welcome to contribute.
|
|
394
434
|
- https://github.com/NARKOZ
|
395
435
|
- https://github.com/stephensolis
|
396
436
|
- https://github.com/kwent
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
437
|
+
- https://github.com/Animesh-Ghosh
|
438
|
+
- https://github.com/gr8bit
|
439
|
+
- https://github.com/codegeek319
|
440
|
+
- https://github.com/clwy-cn
|
441
|
+
- https://github.com/kukicola
|
442
|
+
- https://github.com/sobrinho
|
443
|
+
- https://github.com/iainbeeston
|
444
|
+
- https://github.com/marckohlbrugge
|
445
|
+
- https://github.com/Mth0158
|
446
|
+
- https://github.com/technicalpickles
|
447
|
+
- https://github.com/ricsdeol
|
448
|
+
- https://github.com/Fonsan
|
449
|
+
|
403
450
|
|
404
451
|
## License
|
405
452
|
|
data/config/locales/de.yml
CHANGED
@@ -2,7 +2,11 @@ de:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "hat einen ungültigen Dateityp"
|
5
|
-
|
5
|
+
file_size_not_less_than: "Dateigröße muss kleiner als %{max_size} sein (aktuelle Dateigröße ist %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "Dateigröße muss kleiner oder gleich %{max_size} sein (aktuelle Dateigröße ist %{file_size})"
|
7
|
+
file_size_not_greater_than: "Dateigröße muss größer als %{min_size} sein (aktuelle Dateigröße ist %{file_size})"
|
8
|
+
file_size_not_greater_than_or_equal_to: "Dateigröße muss größer oder gleich %{min_size} sein (aktuelle Dateigröße ist %{file_size})"
|
9
|
+
file_size_not_between: "Dateigröße muss zwischen %{min_size} und %{max_size} liegen (aktuelle Dateigröße ist %{file_size})"
|
6
10
|
limit_out_of_range: "Anzahl ist außerhalb des gültigen Bereichs"
|
7
11
|
image_metadata_missing: "ist kein gültiges Bild"
|
8
12
|
dimension_min_inclusion: "muss größer oder gleich %{width} x %{height} Pixel sein"
|
data/config/locales/en.yml
CHANGED
@@ -2,7 +2,11 @@ en:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "has an invalid content type"
|
5
|
-
|
5
|
+
file_size_not_less_than: "file size must be less than %{max_size} (current size is %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "file size must be less than or equal to %{max_size} (current size is %{file_size})"
|
7
|
+
file_size_not_greater_than: "file size must be greater than %{min_size} (current size is %{file_size})"
|
8
|
+
file_size_not_greater_than_or_equal_to: "file size must be greater than or equal to %{min_size} (current size is %{file_size})"
|
9
|
+
file_size_not_between: "file size must be between %{min_size} and %{max_size} (current size is %{file_size})"
|
6
10
|
limit_out_of_range: "total number is out of range"
|
7
11
|
image_metadata_missing: "is not a valid image"
|
8
12
|
dimension_min_inclusion: "must be greater than or equal to %{width} x %{height} pixel"
|
data/config/locales/es.yml
CHANGED
@@ -2,7 +2,11 @@ es:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "tiene un tipo de contenido inválido"
|
5
|
-
|
5
|
+
file_size_not_less_than: "el tamaño del archivo debe ser inferior a %{max_size} (el tamaño actual es %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "el tamaño del archivo debe ser menor o igual a %{max_size} (el tamaño actual es %{file_size})"
|
7
|
+
file_size_not_greater_than: "el tamaño del archivo debe ser mayor que %{min_size} (el tamaño actual es %{file_size})"
|
8
|
+
file_size_not_greater_than_or_equal_to: "el tamaño del archivo debe ser mayor o igual a %{min_size} (el tamaño actual es %{file_size})"
|
9
|
+
file_size_not_between: "el tamaño del archivo debe estar entre %{min_size} y %{max_size} (el tamaño actual es %{file_size})"
|
6
10
|
limit_out_of_range: "el número total está fuera de rango"
|
7
11
|
image_metadata_missing: "no es una imagen válida"
|
8
12
|
dimension_min_inclusion: "debe ser mayor o igual a %{width} x %{height} pixel"
|
data/config/locales/fr.yml
CHANGED
@@ -2,7 +2,11 @@ fr:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "a un type de contenu non valide"
|
5
|
-
|
5
|
+
file_size_not_less_than: "la taille du fichier doit être inférieure à %{max_size} (la taille actuelle est %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "la taille du fichier doit être inférieure ou égale à %{max_size} (la taille actuelle est %{file_size})"
|
7
|
+
file_size_not_greater_than: "la taille du fichier doit être supérieure à %{min_size} (la taille actuelle est %{file_size})"
|
8
|
+
file_size_not_greater_than_or_equal_to: "la taille du fichier doit être supérieure ou égale à %{min_size} (la taille actuelle est %{file_size})"
|
9
|
+
file_size_not_between: "la taille du fichier doit être comprise entre %{min_size} et %{max_size} (la taille actuelle est %{file_size})"
|
6
10
|
limit_out_of_range: "le nombre total est hors limites"
|
7
11
|
image_metadata_missing: "n'est pas une image valide"
|
8
12
|
dimension_min_inclusion: "doit être supérieur ou égal à %{width} x %{height} pixels"
|
data/config/locales/it.yml
CHANGED
@@ -2,7 +2,11 @@ it:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "ha un tipo di contenuto non valido"
|
5
|
-
|
5
|
+
file_size_not_less_than: "la dimensione del file deve essere inferiore a %{max_size} (la dimensione attuale è %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "la dimensione del file deve essere minore o uguale a %{max_size} (la dimensione attuale è %{file_size})"
|
7
|
+
file_size_not_greater_than: "la dimensione del file deve essere maggiore di %{min_size} (la dimensione attuale è %{file_size})"
|
8
|
+
file_size_not_greater_than_or_equal_to: "la dimensione del file deve essere maggiore o uguale a %{min_size} (la dimensione attuale è %{file_size})"
|
9
|
+
file_size_not_between: "la dimensione del file deve essere compresa tra %{min_size} e %{max_size} (la dimensione attuale è %{file_size})"
|
6
10
|
limit_out_of_range: "il valore è al di fuori dell’intervallo consentito"
|
7
11
|
image_metadata_missing: "non è un'immagine valida"
|
8
12
|
dimension_min_inclusion: "deve essere maggiore o uguale a %{width} x %{height} pixel"
|
data/config/locales/ja.yml
CHANGED
@@ -2,7 +2,11 @@ ja:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "のContent Typeが不正です"
|
5
|
-
|
5
|
+
file_size_not_less_than: "ファイル サイズは %{max_size} 未満にする必要があります (現在のサイズは %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "ファイル サイズは %{max_size} 以下である必要があります (現在のサイズは %{file_size})"
|
7
|
+
file_size_not_greater_than: "ファイル サイズは %{min_size} より大きい必要があります (現在のサイズは %{file_size} です)"
|
8
|
+
file_size_not_greater_than_or_equal_to: "ファイル サイズは %{min_size} 以上である必要があります (現在のサイズは %{file_size})"
|
9
|
+
file_size_not_between: "ファイル サイズは %{min_size} から %{max_size} の間でなければなりません (現在のサイズは %{file_size} です)"
|
6
10
|
limit_out_of_range: "の数が許容範囲外です"
|
7
11
|
image_metadata_missing: "は不正な画像です"
|
8
12
|
dimension_min_inclusion: "は %{width} x %{height} ピクセル以上の大きさにしてください"
|
data/config/locales/nl.yml
CHANGED
@@ -2,7 +2,11 @@ nl:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "heeft een ongeldig inhoudstype"
|
5
|
-
|
5
|
+
file_size_not_less_than: "bestandsgrootte moet kleiner zijn dan %{max_size} (huidige grootte is %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "bestandsgrootte moet kleiner zijn dan of gelijk zijn aan %{max_size} (huidige grootte is %{file_size})"
|
7
|
+
file_size_not_greater_than: "bestandsgrootte moet groter zijn dan %{min_size} (huidige grootte is %{file_size})"
|
8
|
+
file_size_not_greater_than_or_equal_to: "bestandsgrootte moet groter zijn dan of gelijk zijn aan %{min_size} (huidige grootte is %{file_size})"
|
9
|
+
file_size_not_between: "bestandsgrootte moet tussen %{min_size} en %{max_size} liggen (huidige grootte is %{file_size})"
|
6
10
|
limit_out_of_range: "totaal aantal valt buiten het vereiste bereik"
|
7
11
|
image_metadata_missing: "is geen geldige afbeelding"
|
8
12
|
dimension_min_inclusion: "moet groter of gelijk zijn aan %{width} x %{height} pixels"
|
data/config/locales/pl.yml
CHANGED
@@ -2,7 +2,11 @@ pl:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "jest nieprawidłowego typu"
|
5
|
-
|
5
|
+
file_size_not_less_than: "rozmiar pliku musi być mniejszy niż %{max_size} (obecny rozmiar to %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "rozmiar pliku musi być mniejszy lub równy %{max_size} (obecny rozmiar to %{file_size})"
|
7
|
+
file_size_not_greater_than: "rozmiar pliku musi być większy niż %{min_size} (obecny rozmiar to %{file_size})"
|
8
|
+
file_size_not_greater_than_or_equal_to: "rozmiar pliku musi być większy lub równy %{min_size} (obecny rozmiar to %{file_size})"
|
9
|
+
file_size_not_between: "rozmiar pliku musi mieścić się w przedziale od %{min_size} do %{max_size} (obecny rozmiar to %{file_size})"
|
6
10
|
limit_out_of_range: "ilość przekracza dopuszczalny zakres"
|
7
11
|
image_metadata_missing: "nie jest prawidłowym obrazem"
|
8
12
|
dimension_min_inclusion: "musi być równe lub większe od %{width} x %{height} pixeli"
|
data/config/locales/pt-BR.yml
CHANGED
@@ -2,7 +2,11 @@ pt-BR:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "tem um tipo de arquivo inválido"
|
5
|
-
|
5
|
+
file_size_not_less_than: "o tamanho do arquivo deve ser menor que %{max_size} (o tamanho atual é %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "o tamanho do arquivo deve ser menor ou igual a %{max_size} (o tamanho atual é %{file_size})"
|
7
|
+
file_size_not_greater_than: "o tamanho do arquivo deve ser maior que %{min_size} (o tamanho atual é %{file_size})"
|
8
|
+
file_size_not_greater_than_or_equal_to: "o tamanho do arquivo deve ser maior ou igual a %{min_size} (o tamanho atual é %{file_size})"
|
9
|
+
file_size_not_between: "o tamanho do arquivo deve estar entre %{min_size} e %{max_size} (o tamanho atual é %{file_size})"
|
6
10
|
limit_out_of_range: "o número total está fora do limite"
|
7
11
|
image_metadata_missing: "não é uma imagem válida"
|
8
12
|
dimension_min_inclusion: "deve ser maior ou igual a %{width} x %{height} pixels"
|
@@ -20,3 +24,4 @@ pt-BR:
|
|
20
24
|
aspect_ratio_not_landscape: "não está no formato paisagem"
|
21
25
|
aspect_ratio_is_not: "não contém uma proporção de %{aspect_ratio}"
|
22
26
|
aspect_ratio_unknown: "não tem uma proporção definida"
|
27
|
+
image_not_processable: "não é uma imagem válida"
|
data/config/locales/ru.yml
CHANGED
@@ -2,7 +2,11 @@ ru:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "имеет недопустимый тип содержимого"
|
5
|
-
|
5
|
+
file_size_not_less_than: "размер файла должен быть меньше %{max_size} (текущий размер %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "размер файла должен быть меньше или равен %{max_size} (текущий размер %{file_size})"
|
7
|
+
file_size_not_greater_than: "размер файла должен быть больше %{min_size} (текущий размер %{file_size})"
|
8
|
+
file_size_not_greater_than_or_equal_to: "размер файла должен быть больше или равен %{min_size} (текущий размер %{file_size})"
|
9
|
+
file_size_not_between: "размер файла должен быть между %{min_size} и %{max_size} (текущий размер %{file_size})"
|
6
10
|
limit_out_of_range: "количество файлов больше требуемого"
|
7
11
|
image_metadata_missing: "не является допустимым изображением"
|
8
12
|
dimension_min_inclusion: "должен быть больше или равно %{width} x %{height} пикселям"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
sv:
|
2
|
+
errors:
|
3
|
+
messages:
|
4
|
+
content_type_invalid: "Har en ogiltig filtyp"
|
5
|
+
file_size_out_of_range: "storleken %{file_size} är utanför det krävda intervallet"
|
6
|
+
limit_out_of_range: "antalet filer är utanför det godkända intervallet"
|
7
|
+
image_metadata_missing: "bilden sankar metadata"
|
8
|
+
dimension_min_inclusion: "måste minst vara %{width} x %{height} pixlar"
|
9
|
+
dimension_max_inclusion: "måste vara mindre eller lika med %{width} x %{height} pixlar"
|
10
|
+
dimension_width_inclusion: "bredden är utanför %{min} till %{max} pixlar"
|
11
|
+
dimension_height_inclusion: "höjden är utanför %{min} till %{max} pixlar"
|
12
|
+
dimension_width_greater_than_or_equal_to: "bredden måste minst vara %{length} pixlar"
|
13
|
+
dimension_height_greater_than_or_equal_to: "höjden måste minst vara %{length} pixlar"
|
14
|
+
dimension_width_less_than_or_equal_to: "bredden får max vara %{length} pixlar"
|
15
|
+
dimension_height_less_than_or_equal_to: "höjden får max vara %{length} pixlar"
|
16
|
+
dimension_width_equal_to: "bredden måste vara %{length} pixlar"
|
17
|
+
dimension_height_equal_to: "höjden måste vara %{length} pixlar"
|
18
|
+
aspect_ratio_not_square: "måste vara en kvadratisk bild"
|
19
|
+
aspect_ratio_not_portrait: "måste vara en porträttorienterad bild"
|
20
|
+
aspect_ratio_not_landscape: "måste vara en landskapsorienterad bild"
|
21
|
+
aspect_ratio_is_not: "måste ha en följande aspect ratio %{aspect_ratio}"
|
22
|
+
aspect_ratio_unknown: "har en okänd aspect ratio"
|
23
|
+
image_not_processable: "är inte en giltig bild"
|
data/config/locales/tr.yml
CHANGED
@@ -2,7 +2,11 @@ tr:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "geçersiz dosya tipine sahip"
|
5
|
-
|
5
|
+
file_size_not_less_than: "dosya boyutu %{max_size} boyutundan küçük olmalıdır (geçerli boyut %{file_size}'dir)"
|
6
|
+
file_size_not_less_than_or_equal_to: "dosya boyutu %{max_size} değerinden küçük veya ona eşit olmalıdır (geçerli boyut %{file_size}'dir)"
|
7
|
+
file_size_not_greater_than: "dosya boyutu %{min_size} boyutundan büyük olmalıdır (geçerli boyut %{file_size}'dir)"
|
8
|
+
file_size_not_greater_than_or_equal_to: "dosya boyutu %{min_size} değerinden büyük veya eşit olmalıdır (geçerli boyut %{file_size}'dir)"
|
9
|
+
file_size_not_between: "dosya boyutu %{min_size} ile %{max_size} arasında olmalıdır (geçerli boyut %{file_size}'dir)"
|
6
10
|
limit_out_of_range: "toplam miktar aralık dışında"
|
7
11
|
image_metadata_missing: "geçerli bir imaj değil"
|
8
12
|
dimension_min_inclusion: "%{width} x %{height} piksele eşit ya da büyük olmalı"
|
data/config/locales/uk.yml
CHANGED
@@ -2,7 +2,11 @@ uk:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "має неприпустимий тип вмісту"
|
5
|
-
|
5
|
+
file_size_not_less_than: "розмір файлу має бути менше %{max_size} (поточний розмір %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "розмір файлу має бути меншим або дорівнювати %{max_size} (поточний розмір %{file_size})"
|
7
|
+
file_size_not_greater_than: "розмір файлу має бути більшим ніж %{min_size} (поточний розмір %{file_size})"
|
8
|
+
file_size_not_greater_than_or_equal_to: "розмір файлу має бути більшим або дорівнювати %{min_size} (поточний розмір %{file_size})"
|
9
|
+
file_size_not_between: "розмір файлу має бути від %{min_size} до %{max_size} (поточний розмір %{file_size})"
|
6
10
|
limit_out_of_range: "кількість файлів більше необхідного"
|
7
11
|
image_metadata_missing: "не є допустимим зображенням"
|
8
12
|
dimension_min_inclusion: "мусить бути більше або дорівнювати %{width} x %{height} пікселям"
|
data/config/locales/vi.yml
CHANGED
@@ -2,7 +2,11 @@ vi:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "tệp không hợp lệ"
|
5
|
-
|
5
|
+
file_size_not_less_than: "kích thước tệp phải nhỏ hơn %{max_size} (kích thước hiện tại là %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "kích thước tệp phải nhỏ hơn hoặc bằng %{max_size} (kích thước hiện tại là %{file_size})"
|
7
|
+
file_size_not_greater_than: "kích thước tệp phải lớn hơn %{min_size} (kích thước hiện tại là %{file_size})"
|
8
|
+
file_size_not_greater_than_or_equal_to: "kích thước tệp phải lớn hơn hoặc bằng %{min_size} (kích thước hiện tại là %{file_size})"
|
9
|
+
file_size_not_between: "kích thước tệp phải nằm trong khoảng từ %{min_size} đến %{max_size} (kích thước hiện tại là %{file_size})"
|
6
10
|
limit_out_of_range: "tổng số tệp vượt giới hạn"
|
7
11
|
image_metadata_missing: "không phải là ảnh"
|
8
12
|
dimension_min_inclusion: "phải lớn hơn hoặc bằng %{width} x %{height} pixel"
|
data/config/locales/zh-CN.yml
CHANGED
@@ -2,7 +2,11 @@ zh-CN:
|
|
2
2
|
errors:
|
3
3
|
messages:
|
4
4
|
content_type_invalid: "文件类型错误"
|
5
|
-
|
5
|
+
file_size_not_less_than: "文件大小必须小于 %{max_size}(当前大小为 %{file_size})"
|
6
|
+
file_size_not_less_than_or_equal_to: "文件大小必须小于或等于 %{max_size}(当前大小为 %{file_size})"
|
7
|
+
file_size_not_greater_than: "文件大小必须大于 %{min_size}(当前大小为 %{file_size})"
|
8
|
+
file_size_not_greater_than_or_equal_to: "文件大小必须大于或等于 %{min_size}(当前大小为 %{file_size})"
|
9
|
+
file_size_not_between: "文件大小必须介于 %{min_size} 和 %{max_size} 之间(当前大小为 %{file_size})"
|
6
10
|
limit_out_of_range: "文件数超出限定范围"
|
7
11
|
image_metadata_missing: "不是有效的图像"
|
8
12
|
dimension_min_inclusion: "必须大于或等于 %{width} x %{height} 像素"
|
@@ -5,13 +5,15 @@ require_relative 'metadata.rb'
|
|
5
5
|
module ActiveStorageValidations
|
6
6
|
class AspectRatioValidator < ActiveModel::EachValidator # :nodoc
|
7
7
|
include OptionProcUnfolding
|
8
|
+
include ErrorHandler
|
8
9
|
|
9
10
|
AVAILABLE_CHECKS = %i[with].freeze
|
10
11
|
PRECISION = 3
|
11
12
|
|
12
13
|
def check_validity!
|
13
|
-
|
14
|
-
|
14
|
+
unless AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
|
15
|
+
raise ArgumentError, 'You must pass :with to the validator'
|
16
|
+
end
|
15
17
|
end
|
16
18
|
|
17
19
|
if Rails.gem_version >= Gem::Version.new('6.0.0')
|
@@ -53,23 +55,30 @@ module ActiveStorageValidations
|
|
53
55
|
|
54
56
|
def is_valid?(record, attribute, metadata)
|
55
57
|
flat_options = unfold_procs(record, self.options, AVAILABLE_CHECKS)
|
58
|
+
errors_options = initialize_error_options(options)
|
59
|
+
|
56
60
|
if metadata[:width].to_i <= 0 || metadata[:height].to_i <= 0
|
57
|
-
|
61
|
+
errors_options[:aspect_ratio] = flat_options[:with]
|
62
|
+
|
63
|
+
add_error(record, attribute, :image_metadata_missing, **errors_options)
|
58
64
|
return false
|
59
65
|
end
|
60
66
|
|
61
67
|
case flat_options[:with]
|
62
68
|
when :square
|
63
69
|
return true if metadata[:width] == metadata[:height]
|
64
|
-
|
70
|
+
errors_options[:aspect_ratio] = flat_options[:with]
|
71
|
+
add_error(record, attribute, :aspect_ratio_not_square, **errors_options)
|
65
72
|
|
66
73
|
when :portrait
|
67
74
|
return true if metadata[:height] > metadata[:width]
|
68
|
-
|
75
|
+
errors_options[:aspect_ratio] = flat_options[:with]
|
76
|
+
add_error(record, attribute, :aspect_ratio_not_portrait, **errors_options)
|
69
77
|
|
70
78
|
when :landscape
|
71
79
|
return true if metadata[:width] > metadata[:height]
|
72
|
-
|
80
|
+
errors_options[:aspect_ratio] = flat_options[:with]
|
81
|
+
add_error(record, attribute, :aspect_ratio_not_landscape, **errors_options)
|
73
82
|
|
74
83
|
else
|
75
84
|
if flat_options[:with] =~ /is_(\d*)_(\d*)/
|
@@ -78,20 +87,14 @@ module ActiveStorageValidations
|
|
78
87
|
|
79
88
|
return true if (x.to_f / y).round(PRECISION) == (metadata[:width].to_f / metadata[:height]).round(PRECISION)
|
80
89
|
|
81
|
-
|
90
|
+
errors_options[:aspect_ratio] = "#{x}x#{y}"
|
91
|
+
add_error(record, attribute, :aspect_ratio_is_not, **errors_options)
|
82
92
|
else
|
83
|
-
|
93
|
+
errors_options[:aspect_ratio] = flat_options[:with]
|
94
|
+
add_error(record, attribute, :aspect_ratio_unknown, **errors_options)
|
84
95
|
end
|
85
96
|
end
|
86
97
|
false
|
87
98
|
end
|
88
|
-
|
89
|
-
|
90
|
-
def add_error(record, attribute, default_message, interpolate)
|
91
|
-
message = options[:message].presence || default_message
|
92
|
-
return if record.errors.added?(attribute, message)
|
93
|
-
record.errors.add(attribute, message, aspect_ratio: interpolate)
|
94
|
-
end
|
95
|
-
|
96
99
|
end
|
97
100
|
end
|
@@ -2,13 +2,14 @@
|
|
2
2
|
|
3
3
|
module ActiveStorageValidations
|
4
4
|
class AttachedValidator < ActiveModel::EachValidator # :nodoc:
|
5
|
+
include ErrorHandler
|
6
|
+
|
5
7
|
def validate_each(record, attribute, _value)
|
6
8
|
return if record.send(attribute).attached?
|
7
9
|
|
8
|
-
errors_options =
|
9
|
-
errors_options[:message] = options[:message] if options[:message].present?
|
10
|
+
errors_options = initialize_error_options(options)
|
10
11
|
|
11
|
-
record
|
12
|
+
add_error(record, attribute, :blank, **errors_options)
|
12
13
|
end
|
13
14
|
end
|
14
15
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module ActiveStorageValidations
|
4
4
|
class ContentTypeValidator < ActiveModel::EachValidator # :nodoc:
|
5
5
|
include OptionProcUnfolding
|
6
|
+
include ErrorHandler
|
6
7
|
|
7
8
|
AVAILABLE_CHECKS = %i[with in].freeze
|
8
9
|
|
@@ -14,14 +15,14 @@ module ActiveStorageValidations
|
|
14
15
|
|
15
16
|
files = Array.wrap(record.send(attribute))
|
16
17
|
|
17
|
-
errors_options =
|
18
|
-
errors_options[:
|
18
|
+
errors_options = initialize_error_options(options)
|
19
|
+
errors_options[:authorized_types] = types_to_human_format(types)
|
19
20
|
|
20
21
|
files.each do |file|
|
21
22
|
next if is_valid?(file, types)
|
22
23
|
|
23
24
|
errors_options[:content_type] = content_type(file)
|
24
|
-
record
|
25
|
+
add_error(record, attribute, :content_type_invalid, **errors_options)
|
25
26
|
break
|
26
27
|
end
|
27
28
|
end
|
@@ -5,6 +5,7 @@ require_relative 'metadata.rb'
|
|
5
5
|
module ActiveStorageValidations
|
6
6
|
class DimensionValidator < ActiveModel::EachValidator # :nodoc
|
7
7
|
include OptionProcUnfolding
|
8
|
+
include ErrorHandler
|
8
9
|
|
9
10
|
AVAILABLE_CHECKS = %i[width height min max].freeze
|
10
11
|
|
@@ -32,8 +33,9 @@ module ActiveStorageValidations
|
|
32
33
|
|
33
34
|
|
34
35
|
def check_validity!
|
35
|
-
|
36
|
-
|
36
|
+
unless AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
|
37
|
+
raise ArgumentError, 'You must pass either :width, :height, :min or :max to the validator'
|
38
|
+
end
|
37
39
|
end
|
38
40
|
|
39
41
|
|
@@ -70,9 +72,11 @@ module ActiveStorageValidations
|
|
70
72
|
|
71
73
|
def is_valid?(record, attribute, file_metadata)
|
72
74
|
flat_options = process_options(record)
|
75
|
+
errors_options = initialize_error_options(options)
|
76
|
+
|
73
77
|
# Validation fails unless file metadata contains valid width and height.
|
74
78
|
if file_metadata[:width].to_i <= 0 || file_metadata[:height].to_i <= 0
|
75
|
-
add_error(record, attribute, :image_metadata_missing)
|
79
|
+
add_error(record, attribute, :image_metadata_missing, **errors_options)
|
76
80
|
return false
|
77
81
|
end
|
78
82
|
|
@@ -82,39 +86,58 @@ module ActiveStorageValidations
|
|
82
86
|
(flat_options[:width][:min] && file_metadata[:width] < flat_options[:width][:min]) ||
|
83
87
|
(flat_options[:height][:min] && file_metadata[:height] < flat_options[:height][:min])
|
84
88
|
)
|
85
|
-
|
89
|
+
errors_options[:width] = flat_options[:width][:min]
|
90
|
+
errors_options[:height] = flat_options[:height][:min]
|
91
|
+
|
92
|
+
add_error(record, attribute, :dimension_min_inclusion, **errors_options)
|
86
93
|
return false
|
87
94
|
end
|
88
95
|
if flat_options[:max] && (
|
89
96
|
(flat_options[:width][:max] && file_metadata[:width] > flat_options[:width][:max]) ||
|
90
97
|
(flat_options[:height][:max] && file_metadata[:height] > flat_options[:height][:max])
|
91
98
|
)
|
92
|
-
|
99
|
+
errors_options[:width] = flat_options[:width][:max]
|
100
|
+
errors_options[:height] = flat_options[:height][:max]
|
101
|
+
|
102
|
+
add_error(record, attribute, :dimension_max_inclusion, **errors_options)
|
93
103
|
return false
|
94
104
|
end
|
95
105
|
|
96
106
|
# Validation based on checks :width and :height.
|
97
107
|
else
|
98
108
|
width_or_height_invalid = false
|
109
|
+
|
99
110
|
[:width, :height].each do |length|
|
100
111
|
next unless flat_options[length]
|
101
112
|
if flat_options[length].is_a?(Hash)
|
102
113
|
if flat_options[length][:in] && (file_metadata[length] < flat_options[length][:min] || file_metadata[length] > flat_options[length][:max])
|
103
|
-
|
114
|
+
error_type = :"dimension_#{length}_inclusion"
|
115
|
+
errors_options[:min] = flat_options[length][:min]
|
116
|
+
errors_options[:max] = flat_options[length][:max]
|
117
|
+
|
118
|
+
add_error(record, attribute, error_type, **errors_options)
|
104
119
|
width_or_height_invalid = true
|
105
120
|
else
|
106
121
|
if flat_options[length][:min] && file_metadata[length] < flat_options[length][:min]
|
107
|
-
|
122
|
+
error_type = :"dimension_#{length}_greater_than_or_equal_to"
|
123
|
+
errors_options[:length] = flat_options[length][:min]
|
124
|
+
|
125
|
+
add_error(record, attribute, error_type, **errors_options)
|
108
126
|
width_or_height_invalid = true
|
109
|
-
|
110
|
-
|
111
|
-
|
127
|
+
elsif flat_options[length][:max] && file_metadata[length] > flat_options[length][:max]
|
128
|
+
error_type = :"dimension_#{length}_less_than_or_equal_to"
|
129
|
+
errors_options[:length] = flat_options[length][:max]
|
130
|
+
|
131
|
+
add_error(record, attribute, error_type, **errors_options)
|
112
132
|
width_or_height_invalid = true
|
113
133
|
end
|
114
134
|
end
|
115
135
|
else
|
116
136
|
if file_metadata[length] != flat_options[length]
|
117
|
-
|
137
|
+
error_type = :"dimension_#{length}_equal_to"
|
138
|
+
errors_options[:length] = flat_options[length]
|
139
|
+
|
140
|
+
add_error(record, attribute, error_type, **errors_options)
|
118
141
|
width_or_height_invalid = true
|
119
142
|
end
|
120
143
|
end
|
@@ -125,12 +148,5 @@ module ActiveStorageValidations
|
|
125
148
|
|
126
149
|
true # valid file
|
127
150
|
end
|
128
|
-
|
129
|
-
def add_error(record, attribute, default_message, **attrs)
|
130
|
-
message = options[:message].presence || default_message
|
131
|
-
return if record.errors.added?(attribute, message)
|
132
|
-
record.errors.add(attribute, message, **attrs)
|
133
|
-
end
|
134
|
-
|
135
151
|
end
|
136
152
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActiveStorageValidations
|
2
|
+
module ErrorHandler
|
3
|
+
|
4
|
+
def initialize_error_options(options)
|
5
|
+
{
|
6
|
+
message: (options[:message] if options[:message].present?)
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_error(record, attribute, default_message, **errors_options)
|
11
|
+
message = errors_options[:message].presence || default_message
|
12
|
+
return if record.errors.added?(attribute, message)
|
13
|
+
|
14
|
+
record.errors.add(attribute, message, **errors_options)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -3,23 +3,25 @@
|
|
3
3
|
module ActiveStorageValidations
|
4
4
|
class LimitValidator < ActiveModel::EachValidator # :nodoc:
|
5
5
|
include OptionProcUnfolding
|
6
|
+
include ErrorHandler
|
6
7
|
|
7
8
|
AVAILABLE_CHECKS = %i[max min].freeze
|
8
9
|
|
9
10
|
def check_validity!
|
10
|
-
|
11
|
-
|
11
|
+
unless AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
|
12
|
+
raise ArgumentError, 'You must pass either :max or :min to the validator'
|
13
|
+
end
|
12
14
|
end
|
13
15
|
|
14
16
|
def validate_each(record, attribute, _)
|
15
|
-
return true unless record.send(attribute).attached?
|
16
|
-
|
17
17
|
files = Array.wrap(record.send(attribute)).compact.uniq
|
18
18
|
flat_options = unfold_procs(record, self.options, AVAILABLE_CHECKS)
|
19
|
-
errors_options =
|
19
|
+
errors_options = initialize_error_options(options)
|
20
|
+
errors_options[:min] = flat_options[:min]
|
21
|
+
errors_options[:max] = flat_options[:max]
|
20
22
|
|
21
23
|
return true if files_count_valid?(files.count, flat_options)
|
22
|
-
record
|
24
|
+
add_error(record, attribute, :limit_out_of_range, **errors_options)
|
23
25
|
end
|
24
26
|
|
25
27
|
def files_count_valid?(count, flat_options)
|
@@ -9,12 +9,18 @@ module ActiveStorageValidations
|
|
9
9
|
class AttachedValidatorMatcher
|
10
10
|
def initialize(attribute_name)
|
11
11
|
@attribute_name = attribute_name
|
12
|
+
@custom_message = nil
|
12
13
|
end
|
13
14
|
|
14
15
|
def description
|
15
16
|
"validate #{@attribute_name} must be attached"
|
16
17
|
end
|
17
18
|
|
19
|
+
def with_message(message)
|
20
|
+
@custom_message = message
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
18
24
|
def matches?(subject)
|
19
25
|
@subject = subject.is_a?(Class) ? subject.new : subject
|
20
26
|
responds_to_methods && valid_when_attached && invalid_when_not_attached
|
@@ -39,7 +45,7 @@ module ActiveStorageValidations
|
|
39
45
|
def valid_when_attached
|
40
46
|
@subject.public_send(@attribute_name).attach(attachable) unless @subject.public_send(@attribute_name).attached?
|
41
47
|
@subject.validate
|
42
|
-
@subject.errors.details[@attribute_name].exclude?(error:
|
48
|
+
@subject.errors.details[@attribute_name].exclude?(error: error_message)
|
43
49
|
end
|
44
50
|
|
45
51
|
def invalid_when_not_attached
|
@@ -48,7 +54,11 @@ module ActiveStorageValidations
|
|
48
54
|
@subject.public_send("#{@attribute_name}=", nil)
|
49
55
|
|
50
56
|
@subject.validate
|
51
|
-
@subject.errors.details[@attribute_name].include?(error:
|
57
|
+
@subject.errors.details[@attribute_name].include?(error: error_message)
|
58
|
+
end
|
59
|
+
|
60
|
+
def error_message
|
61
|
+
@custom_message || :blank
|
52
62
|
end
|
53
63
|
|
54
64
|
def attachable
|
@@ -11,6 +11,7 @@ module ActiveStorageValidations
|
|
11
11
|
class ContentTypeValidatorMatcher
|
12
12
|
def initialize(attribute_name)
|
13
13
|
@attribute_name = attribute_name
|
14
|
+
@custom_message = nil
|
14
15
|
end
|
15
16
|
|
16
17
|
def description
|
@@ -27,9 +28,14 @@ module ActiveStorageValidations
|
|
27
28
|
self
|
28
29
|
end
|
29
30
|
|
31
|
+
def with_message(message)
|
32
|
+
@custom_message = message
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
30
36
|
def matches?(subject)
|
31
37
|
@subject = subject.is_a?(Class) ? subject.new : subject
|
32
|
-
responds_to_methods && allowed_types_allowed? && rejected_types_rejected?
|
38
|
+
responds_to_methods && allowed_types_allowed? && rejected_types_rejected? && validate_error_message?
|
33
39
|
end
|
34
40
|
|
35
41
|
def failure_message
|
@@ -77,7 +83,21 @@ module ActiveStorageValidations
|
|
77
83
|
def type_allowed?(type)
|
78
84
|
@subject.public_send(@attribute_name).attach(attachment_for(type))
|
79
85
|
@subject.validate
|
80
|
-
@subject.errors.details[@attribute_name].
|
86
|
+
@subject.errors.details[@attribute_name].none? do |error|
|
87
|
+
error[:error].to_s.include?(error_message)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def validate_error_message?
|
92
|
+
@subject.public_send(@attribute_name).attach(attachment_for('fake/fake'))
|
93
|
+
@subject.validate
|
94
|
+
@subject.errors.details[@attribute_name].all? do |error|
|
95
|
+
error[:error].to_s.include?(error_message)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def error_message
|
100
|
+
@custom_message || :content_type_invalid.to_s
|
81
101
|
end
|
82
102
|
|
83
103
|
def attachment_for(type)
|
@@ -65,8 +65,8 @@ module ActiveStorageValidations
|
|
65
65
|
def matches?(subject)
|
66
66
|
@subject = subject.is_a?(Class) ? subject.new : subject
|
67
67
|
responds_to_methods &&
|
68
|
-
|
69
|
-
|
68
|
+
width_not_smaller_than_min? && width_larger_than_min? && width_smaller_than_max? && width_not_larger_than_max? && width_equals? &&
|
69
|
+
height_not_smaller_than_min? && height_larger_than_min? && height_smaller_than_max? && height_not_larger_than_max? && height_equals?
|
70
70
|
end
|
71
71
|
|
72
72
|
def failure_message
|
@@ -93,7 +93,7 @@ module ActiveStorageValidations
|
|
93
93
|
((@height_min || 0) + (@height_max || 2000)) / 2
|
94
94
|
end
|
95
95
|
|
96
|
-
def
|
96
|
+
def width_not_smaller_than_min?
|
97
97
|
@width_min.nil? || !passes_validation_with_dimensions(@width_min - 1, valid_height, 'width')
|
98
98
|
end
|
99
99
|
|
@@ -105,7 +105,7 @@ module ActiveStorageValidations
|
|
105
105
|
@width_max.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_max - 1, valid_height, 'width')
|
106
106
|
end
|
107
107
|
|
108
|
-
def
|
108
|
+
def width_not_larger_than_max?
|
109
109
|
@width_max.nil? || !passes_validation_with_dimensions(@width_max + 1, valid_height, 'width')
|
110
110
|
end
|
111
111
|
|
@@ -113,7 +113,7 @@ module ActiveStorageValidations
|
|
113
113
|
@width_min.nil? || @width_min != @width_max || passes_validation_with_dimensions(@width_min, valid_height, 'width')
|
114
114
|
end
|
115
115
|
|
116
|
-
def
|
116
|
+
def height_not_smaller_than_min?
|
117
117
|
@height_min.nil? || !passes_validation_with_dimensions(valid_width, @height_min - 1, 'height')
|
118
118
|
end
|
119
119
|
|
@@ -125,7 +125,7 @@ module ActiveStorageValidations
|
|
125
125
|
@height_max.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_max - 1, 'height')
|
126
126
|
end
|
127
127
|
|
128
|
-
def
|
128
|
+
def height_not_larger_than_max?
|
129
129
|
@height_max.nil? || !passes_validation_with_dimensions(valid_width, @height_max + 1, 'height')
|
130
130
|
end
|
131
131
|
|
@@ -140,7 +140,11 @@ module ActiveStorageValidations
|
|
140
140
|
Matchers.mock_metadata(attachment, width, height) do
|
141
141
|
@subject.validate
|
142
142
|
exclude_error_message = @custom_message || "dimension_#{check}"
|
143
|
-
@subject.errors.details[@attribute_name].
|
143
|
+
@subject.errors.details[@attribute_name].none? do |error|
|
144
|
+
error[:error].to_s.include?(exclude_error_message) ||
|
145
|
+
error[:error].to_s.include?("dimension_min") ||
|
146
|
+
error[:error].to_s.include?("dimension_max")
|
147
|
+
end
|
144
148
|
end
|
145
149
|
end
|
146
150
|
|
@@ -11,7 +11,8 @@ module ActiveStorageValidations
|
|
11
11
|
class SizeValidatorMatcher
|
12
12
|
def initialize(attribute_name)
|
13
13
|
@attribute_name = attribute_name
|
14
|
-
@
|
14
|
+
@min = @max = nil
|
15
|
+
@custom_message = nil
|
15
16
|
end
|
16
17
|
|
17
18
|
def description
|
@@ -19,41 +20,46 @@ module ActiveStorageValidations
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def less_than(size)
|
22
|
-
@
|
23
|
+
@max = size - 1.byte
|
23
24
|
self
|
24
25
|
end
|
25
26
|
|
26
27
|
def less_than_or_equal_to(size)
|
27
|
-
@
|
28
|
+
@max = size
|
28
29
|
self
|
29
30
|
end
|
30
31
|
|
31
32
|
def greater_than(size)
|
32
|
-
@
|
33
|
+
@min = size + 1.byte
|
33
34
|
self
|
34
35
|
end
|
35
36
|
|
36
37
|
def greater_than_or_equal_to(size)
|
37
|
-
@
|
38
|
+
@min = size
|
38
39
|
self
|
39
40
|
end
|
40
41
|
|
41
42
|
def between(range)
|
42
|
-
@
|
43
|
+
@min, @max = range.first, range.last
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def with_message(message)
|
48
|
+
@custom_message = message
|
43
49
|
self
|
44
50
|
end
|
45
51
|
|
46
52
|
def matches?(subject)
|
47
53
|
@subject = subject.is_a?(Class) ? subject.new : subject
|
48
|
-
responds_to_methods &&
|
54
|
+
responds_to_methods && not_lower_than_min? && higher_than_min? && lower_than_max? && not_higher_than_max?
|
49
55
|
end
|
50
56
|
|
51
57
|
def failure_message
|
52
|
-
"is expected to validate file size of #{@attribute_name} to be between #{@
|
58
|
+
"is expected to validate file size of #{@attribute_name} to be between #{@min} and #{@max} bytes"
|
53
59
|
end
|
54
60
|
|
55
61
|
def failure_message_when_negated
|
56
|
-
"is expected to not validate file size of #{@attribute_name} to be between #{@
|
62
|
+
"is expected to not validate file size of #{@attribute_name} to be between #{@min} and #{@max} bytes"
|
57
63
|
end
|
58
64
|
|
59
65
|
protected
|
@@ -64,20 +70,20 @@ module ActiveStorageValidations
|
|
64
70
|
@subject.public_send(@attribute_name).respond_to?(:detach)
|
65
71
|
end
|
66
72
|
|
67
|
-
def
|
68
|
-
@
|
73
|
+
def not_lower_than_min?
|
74
|
+
@min.nil? || !passes_validation_with_size(@min - 1)
|
69
75
|
end
|
70
76
|
|
71
|
-
def
|
72
|
-
@
|
77
|
+
def higher_than_min?
|
78
|
+
@min.nil? || passes_validation_with_size(@min + 1)
|
73
79
|
end
|
74
80
|
|
75
|
-
def
|
76
|
-
@
|
81
|
+
def lower_than_max?
|
82
|
+
@max.nil? || @max == Float::INFINITY || passes_validation_with_size(@max - 1)
|
77
83
|
end
|
78
84
|
|
79
|
-
def
|
80
|
-
@
|
85
|
+
def not_higher_than_max?
|
86
|
+
@max.nil? || @max == Float::INFINITY || !passes_validation_with_size(@max + 1)
|
81
87
|
end
|
82
88
|
|
83
89
|
def passes_validation_with_size(new_size)
|
@@ -85,7 +91,10 @@ module ActiveStorageValidations
|
|
85
91
|
Matchers.stub_method(io, :size, new_size) do
|
86
92
|
@subject.public_send(@attribute_name).attach(io: io, filename: 'test.png', content_type: 'image/pg')
|
87
93
|
@subject.validate
|
88
|
-
|
94
|
+
exclude_error_message = @custom_message || "file_size_not_"
|
95
|
+
@subject.errors.details[@attribute_name].none? do |error|
|
96
|
+
error[:error].to_s.include?(exclude_error_message)
|
97
|
+
end
|
89
98
|
end
|
90
99
|
end
|
91
100
|
end
|
@@ -75,17 +75,10 @@ module ActiveStorageValidations
|
|
75
75
|
tempfile.flush
|
76
76
|
tempfile.rewind
|
77
77
|
|
78
|
-
image =
|
79
|
-
Vips::Image.new_from_file(tempfile.path)
|
80
|
-
elsif defined?(MiniMagick)
|
81
|
-
MiniMagick::Image.new(tempfile.path)
|
82
|
-
end
|
78
|
+
image = new_image_from_path(tempfile.path)
|
83
79
|
else
|
84
|
-
|
85
|
-
|
86
|
-
elsif defined?(MiniMagick)
|
87
|
-
MiniMagick::Image.new(read_file_path)
|
88
|
-
end
|
80
|
+
file_path = read_file_path
|
81
|
+
image = new_image_from_path(file_path)
|
89
82
|
end
|
90
83
|
|
91
84
|
|
@@ -101,6 +94,14 @@ module ActiveStorageValidations
|
|
101
94
|
image = nil
|
102
95
|
end
|
103
96
|
|
97
|
+
def new_image_from_path(path)
|
98
|
+
if image_processor == :vips && defined?(Vips) && (Vips::get_suffixes.include?(File.extname(path).downcase) || !Vips::respond_to?(:vips_foreign_get_suffixes))
|
99
|
+
Vips::Image.new_from_file(path)
|
100
|
+
elsif defined?(MiniMagick)
|
101
|
+
MiniMagick::Image.new(path)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
104
105
|
def valid_image?(image)
|
105
106
|
return false unless image
|
106
107
|
|
@@ -125,7 +126,18 @@ module ActiveStorageValidations
|
|
125
126
|
when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
|
126
127
|
file.path
|
127
128
|
when Hash
|
128
|
-
|
129
|
+
io = file.fetch(:io)
|
130
|
+
if io.is_a?(StringIO)
|
131
|
+
tempfile = Tempfile.new([File.basename(file[:filename], '.*'), File.extname(file[:filename])])
|
132
|
+
tempfile.binmode
|
133
|
+
IO.copy_stream(io, tempfile)
|
134
|
+
io.rewind
|
135
|
+
tempfile.flush
|
136
|
+
tempfile.rewind
|
137
|
+
tempfile.path
|
138
|
+
else
|
139
|
+
File.open(io).path
|
140
|
+
end
|
129
141
|
else
|
130
142
|
raise "Something wrong with params."
|
131
143
|
end
|
@@ -5,18 +5,21 @@ require_relative 'metadata.rb'
|
|
5
5
|
module ActiveStorageValidations
|
6
6
|
class ProcessableImageValidator < ActiveModel::EachValidator # :nodoc
|
7
7
|
include OptionProcUnfolding
|
8
|
+
include ErrorHandler
|
8
9
|
|
9
10
|
if Rails.gem_version >= Gem::Version.new('6.0.0')
|
10
11
|
def validate_each(record, attribute, _value)
|
11
12
|
return true unless record.send(attribute).attached?
|
12
13
|
|
14
|
+
errors_options = initialize_error_options(options)
|
15
|
+
|
13
16
|
changes = record.attachment_changes[attribute.to_s]
|
14
17
|
return true if changes.blank?
|
15
18
|
|
16
19
|
files = Array.wrap(changes.is_a?(ActiveStorage::Attached::Changes::CreateMany) ? changes.attachables : changes.attachable)
|
17
20
|
|
18
21
|
files.each do |file|
|
19
|
-
add_error(record, attribute, :image_not_processable) unless Metadata.new(file).valid?
|
22
|
+
add_error(record, attribute, :image_not_processable, **errors_options) unless Metadata.new(file).valid?
|
20
23
|
end
|
21
24
|
end
|
22
25
|
else
|
@@ -27,17 +30,9 @@ module ActiveStorageValidations
|
|
27
30
|
files = Array.wrap(record.send(attribute))
|
28
31
|
|
29
32
|
files.each do |file|
|
30
|
-
add_error(record, attribute, :image_not_processable) unless Metadata.new(file).valid?
|
33
|
+
add_error(record, attribute, :image_not_processable, **errors_options) unless Metadata.new(file).valid?
|
31
34
|
end
|
32
35
|
end
|
33
36
|
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
37
|
end
|
43
38
|
end
|
@@ -3,14 +3,16 @@
|
|
3
3
|
module ActiveStorageValidations
|
4
4
|
class SizeValidator < ActiveModel::EachValidator # :nodoc:
|
5
5
|
include OptionProcUnfolding
|
6
|
+
include ErrorHandler
|
6
7
|
|
7
8
|
delegate :number_to_human_size, to: ActiveSupport::NumberHelper
|
8
9
|
|
9
10
|
AVAILABLE_CHECKS = %i[less_than less_than_or_equal_to greater_than greater_than_or_equal_to between].freeze
|
10
11
|
|
11
12
|
def check_validity!
|
12
|
-
|
13
|
-
|
13
|
+
unless AVAILABLE_CHECKS.one? { |argument| options.key?(argument) }
|
14
|
+
raise ArgumentError, 'You must pass either :less_than(_or_equal_to), :greater_than(_or_equal_to), or :between to the validator'
|
15
|
+
end
|
14
16
|
end
|
15
17
|
|
16
18
|
def validate_each(record, attribute, _value)
|
@@ -19,8 +21,8 @@ module ActiveStorageValidations
|
|
19
21
|
|
20
22
|
files = Array.wrap(record.send(attribute))
|
21
23
|
|
22
|
-
errors_options =
|
23
|
-
|
24
|
+
errors_options = initialize_error_options(options)
|
25
|
+
|
24
26
|
flat_options = unfold_procs(record, self.options, AVAILABLE_CHECKS)
|
25
27
|
|
26
28
|
files.each do |file|
|
@@ -29,8 +31,9 @@ module ActiveStorageValidations
|
|
29
31
|
errors_options[:file_size] = number_to_human_size(file.blob.byte_size)
|
30
32
|
errors_options[:min_size] = number_to_human_size(min_size(flat_options))
|
31
33
|
errors_options[:max_size] = number_to_human_size(max_size(flat_options))
|
34
|
+
error_type = "file_size_not_#{flat_options.keys.first}".to_sym
|
32
35
|
|
33
|
-
record
|
36
|
+
add_error(record, attribute, error_type, **errors_options)
|
34
37
|
break
|
35
38
|
end
|
36
39
|
end
|
@@ -1,8 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_model'
|
4
|
+
|
3
5
|
require 'active_storage_validations/railtie'
|
4
6
|
require 'active_storage_validations/engine'
|
5
7
|
require 'active_storage_validations/option_proc_unfolding'
|
8
|
+
require 'active_storage_validations/error_handler'
|
6
9
|
require 'active_storage_validations/attached_validator'
|
7
10
|
require 'active_storage_validations/content_type_validator'
|
8
11
|
require 'active_storage_validations/size_validator'
|
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
|
4
|
+
version: 1.1.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:
|
11
|
+
date: 2023-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: simplecov
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
167
181
|
- !ruby/object:Gem::Dependency
|
168
182
|
name: globalid
|
169
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -198,6 +212,7 @@ files:
|
|
198
212
|
- config/locales/pl.yml
|
199
213
|
- config/locales/pt-BR.yml
|
200
214
|
- config/locales/ru.yml
|
215
|
+
- config/locales/sv.yml
|
201
216
|
- config/locales/tr.yml
|
202
217
|
- config/locales/uk.yml
|
203
218
|
- config/locales/vi.yml
|
@@ -208,6 +223,7 @@ files:
|
|
208
223
|
- lib/active_storage_validations/content_type_validator.rb
|
209
224
|
- lib/active_storage_validations/dimension_validator.rb
|
210
225
|
- lib/active_storage_validations/engine.rb
|
226
|
+
- lib/active_storage_validations/error_handler.rb
|
211
227
|
- lib/active_storage_validations/limit_validator.rb
|
212
228
|
- lib/active_storage_validations/matchers.rb
|
213
229
|
- lib/active_storage_validations/matchers/attached_validator_matcher.rb
|
@@ -240,7 +256,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
240
256
|
- !ruby/object:Gem::Version
|
241
257
|
version: '0'
|
242
258
|
requirements: []
|
243
|
-
rubygems_version: 3.
|
259
|
+
rubygems_version: 3.4.10
|
244
260
|
signing_key:
|
245
261
|
specification_version: 4
|
246
262
|
summary: Validations for Active Storage
|