active_storage_validations 2.0.3 → 3.0.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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +122 -16
  3. data/config/locales/da.yml +9 -0
  4. data/config/locales/de.yml +9 -0
  5. data/config/locales/en-GB.yml +9 -0
  6. data/config/locales/en.yml +9 -0
  7. data/config/locales/es.yml +9 -0
  8. data/config/locales/fr.yml +9 -0
  9. data/config/locales/it.yml +9 -0
  10. data/config/locales/ja.yml +9 -0
  11. data/config/locales/nl.yml +9 -0
  12. data/config/locales/pl.yml +9 -0
  13. data/config/locales/pt-BR.yml +9 -0
  14. data/config/locales/ru.yml +9 -0
  15. data/config/locales/sv.yml +10 -1
  16. data/config/locales/tr.yml +9 -0
  17. data/config/locales/uk.yml +9 -0
  18. data/config/locales/vi.yml +9 -0
  19. data/config/locales/zh-CN.yml +9 -0
  20. data/lib/active_storage_validations/analyzer/pdf_analyzer.rb +89 -0
  21. data/lib/active_storage_validations/base_comparison_validator.rb +13 -1
  22. data/lib/active_storage_validations/duration_validator.rb +1 -0
  23. data/lib/active_storage_validations/matchers/base_comparison_validator_matcher.rb +11 -1
  24. data/lib/active_storage_validations/matchers/pages_validator_matcher.rb +39 -0
  25. data/lib/active_storage_validations/matchers.rb +1 -0
  26. data/lib/active_storage_validations/pages_validator.rb +61 -0
  27. data/lib/active_storage_validations/railtie.rb +6 -3
  28. data/lib/active_storage_validations/shared/asv_analyzable.rb +6 -0
  29. data/lib/active_storage_validations/shared/asv_attachable.rb +1 -1
  30. data/lib/active_storage_validations/shared/asv_errorable.rb +25 -1
  31. data/lib/active_storage_validations/size_validator.rb +1 -0
  32. data/lib/active_storage_validations/total_size_validator.rb +1 -0
  33. data/lib/active_storage_validations/version.rb +1 -1
  34. data/lib/active_storage_validations.rb +2 -0
  35. metadata +8 -2
@@ -12,16 +12,19 @@ nl:
12
12
  file_size_not_greater_than: "bestandsgrootte moet groter zijn dan %{min} (huidige grootte is %{file_size})"
13
13
  file_size_not_greater_than_or_equal_to: "bestandsgrootte moet groter zijn dan of gelijk zijn aan %{min} (huidige grootte is %{file_size})"
14
14
  file_size_not_between: "bestandsgrootte moet tussen %{min} en %{max} liggen (huidige grootte is %{file_size})"
15
+ file_size_not_equal_to: "bestandsgrootte moet gelijk zijn aan %{exact} (huidige grootte is %{file_size})"
15
16
  total_file_size_not_less_than: "totale bestandsgrootte moet kleiner zijn dan %{max} (huidige grootte is %{total_file_size})"
16
17
  total_file_size_not_less_than_or_equal_to: "totale bestandsgrootte moet kleiner zijn dan of gelijk zijn aan %{max} (huidige grootte is %{total_file_size})"
17
18
  total_file_size_not_greater_than: "totale bestandsgrootte moet groter zijn dan %{min} (huidige grootte is %{total_file_size})"
18
19
  total_file_size_not_greater_than_or_equal_to: "totale bestandsgrootte moet groter zijn dan of gelijk zijn aan %{min} (huidige grootte is %{total_file_size})"
19
20
  total_file_size_not_between: "totale bestandsgrootte moet tussen %{min} en %{max} liggen (huidige grootte is %{total_file_size})"
21
+ total_file_size_not_equal_to: "totale bestandsgrootte moet gelijk zijn aan %{exact} (huidige grootte is %{total_file_size})"
20
22
  duration_not_less_than: "duur moet kleiner zijn dan %{max} (huidige duur is %{duration})"
21
23
  duration_not_less_than_or_equal_to: "duur moet kleiner zijn dan of gelijk zijn aan %{max} (huidige duur is %{duration})"
22
24
  duration_not_greater_than: "duur moet groter zijn dan %{min} (huidige duur is %{duration})"
23
25
  duration_not_greater_than_or_equal_to: "duur moet groter zijn dan of gelijk zijn aan %{min} (huidige duur is %{duration})"
24
26
  duration_not_between: "duur moet tussen %{min} en %{max} zijn (huidige duur is %{duration})"
27
+ duration_not_equal_to: "duur moet gelijk zijn aan %{exact} (huidige duur is %{duration})"
25
28
  limit_out_of_range:
26
29
  zero: "geen bestanden bijgevoegd (moet tussen %{min} en %{max} bestanden hebben)"
27
30
  one: "slechts 1 bijgevoegd bestand (moet tussen %{min} en %{max} bestanden hebben)"
@@ -52,3 +55,9 @@ nl:
52
55
  aspect_ratio_not_x_y: "moet %{authorized_aspect_ratios} zijn (huidig bestand is %{width}x%{height}px)"
53
56
  aspect_ratio_invalid: "heeft een ongeldige beeldverhouding (geldige beeldverhoudingen zijn %{authorized_aspect_ratios})"
54
57
  file_not_processable: "wordt niet geïdentificeerd als een geldig mediabestand"
58
+ pages_not_less_than: "het aantal pagina's moet kleiner zijn dan %{max} (het huidige aantal pagina's is %{pages})"
59
+ pages_not_less_than_or_equal_to: "het aantal pagina's moet kleiner zijn dan of gelijk zijn aan %{max} (het huidige aantal pagina's is %{pages})"
60
+ pages_not_greater_than: "het aantal pagina's moet groter zijn dan %{min} (het huidige aantal pagina's is %{pages})"
61
+ pages_not_greater_than_or_equal_to: "het aantal pagina's moet groter zijn dan of gelijk zijn aan %{min} (het huidige aantal pagina's is %{pages})"
62
+ pages_not_between: "het aantal pagina's moet tussen %{min} en %{max} liggen (het huidige aantal pagina's is %{pages})"
63
+ pages_not_equal_to: "het aantal pagina's moet gelijk zijn aan %{exact} (het huidige aantal pagina's is %{pages})"
@@ -12,16 +12,19 @@ pl:
12
12
  file_size_not_greater_than: "rozmiar pliku musi być większy niż %{min} (obecny rozmiar to %{file_size})"
13
13
  file_size_not_greater_than_or_equal_to: "rozmiar pliku musi być większy lub równy %{min} (obecny rozmiar to %{file_size})"
14
14
  file_size_not_between: "rozmiar pliku musi mieścić się w przedziale od %{min} do %{max} (obecny rozmiar to %{file_size})"
15
+ file_size_not_equal_to: "rozmiar pliku musi być równy %{exact} (obecny rozmiar to %{file_size})"
15
16
  total_file_size_not_less_than: "całkowity rozmiar pliku musi być mniejszy niż %{max} (obecny rozmiar to %{total_file_size})"
16
17
  total_file_size_not_less_than_or_equal_to: "całkowity rozmiar pliku musi być mniejszy lub równy %{max} (obecny rozmiar to %{total_file_size})"
17
18
  total_file_size_not_greater_than: "całkowity rozmiar pliku musi być większy niż %{min} (obecny rozmiar to %{total_file_size})"
18
19
  total_file_size_not_greater_than_or_equal_to: "całkowity rozmiar pliku musi być większy lub równy %{min} (obecny rozmiar to %{total_file_size})"
19
20
  total_file_size_not_between: "całkowity rozmiar pliku musi mieścić się w przedziale od %{min} do %{max} (obecny rozmiar to %{total_file_size})"
21
+ total_file_size_not_equal_to: "całkowity rozmiar pliku musi być równy %{exact} (obecny rozmiar to %{total_file_size})"
20
22
  duration_not_less_than: "czas trwania musi być mniejszy niż %{max} (obecny czas trwania wynosi %{duration})"
21
23
  duration_not_less_than_or_equal_to: "czas trwania musi być mniejszy lub równy %{max} (obecny czas trwania wynosi %{duration})"
22
24
  duration_not_greater_than: "czas trwania musi być większy niż %{min} (obecny czas trwania wynosi %{duration})"
23
25
  duration_not_greater_than_or_equal_to: "czas trwania musi być większy lub równy %{min} (obecny czas trwania wynosi %{duration})"
24
26
  duration_not_between: "czas trwania musi wynosić między %{min} a %{max} (obecny czas trwania wynosi %{duration})"
27
+ duration_not_equal_to: "czas trwania musi być równy %{exact} (obecny czas trwania wynosi %{duration})"
25
28
  limit_out_of_range:
26
29
  zero: "brak dołączonych plików (musi mieć między %{min} a %{max} pliki)"
27
30
  one: "załączony tylko 1 plik (musi mieć między %{min} a %{max} pliki)"
@@ -52,3 +55,9 @@ pl:
52
55
  aspect_ratio_not_x_y: "musi być %{authorized_aspect_ratios} (bieżący plik to %{width}x%{height}px)"
53
56
  aspect_ratio_invalid: "ma nieprawidłowy współczynnik kształtu (ważne współczynniki kształtu wynoszą %{authorized_aspect_ratios})"
54
57
  file_not_processable: "nie jest identyfikowany jako prawidłowy plik multimedialny"
58
+ pages_not_less_than: "liczba stron musi być mniejsza niż %{max} (bieżąca liczba stron to %{pages})"
59
+ pages_not_less_than_or_equal_to: "liczba stron musi być mniejsza lub równa %{max} (bieżąca liczba stron to %{pages})"
60
+ pages_not_greater_than: "liczba stron musi być większa niż %{min} (bieżąca liczba stron to %{pages})"
61
+ pages_not_greater_than_or_equal_to: "liczba stron musi być większa lub równa %{min} (bieżąca liczba stron to %{pages})"
62
+ pages_not_between: "liczba stron musi być między %{min} a %{max} (bieżąca liczba stron to %{pages})"
63
+ pages_not_equal_to: "liczba stron musi być równa %{exact} (bieżąca liczba stron to %{pages})"
@@ -12,16 +12,19 @@ pt-BR:
12
12
  file_size_not_greater_than: "o tamanho do arquivo deve ser maior que %{min} (o tamanho atual é %{file_size})"
13
13
  file_size_not_greater_than_or_equal_to: "o tamanho do arquivo deve ser maior ou igual a %{min} (o tamanho atual é %{file_size})"
14
14
  file_size_not_between: "o tamanho do arquivo deve estar entre %{min} e %{max} (o tamanho atual é %{file_size})"
15
+ file_size_not_equal_to: "o tamanho do arquivo deve ser igual a %{exact} (o tamanho atual é %{file_size})"
15
16
  total_file_size_not_less_than: "o tamanho total do arquivo deve ser menor que %{max} (o tamanho atual é %{total_file_size})"
16
17
  total_file_size_not_less_than_or_equal_to: "o tamanho total do arquivo deve ser menor ou igual a %{max} (o tamanho atual é %{total_file_size})"
17
18
  total_file_size_not_greater_than: "o tamanho total do arquivo deve ser maior que %{min} (o tamanho atual é %{total_file_size})"
18
19
  total_file_size_not_greater_than_or_equal_to: "o tamanho total do arquivo deve ser maior ou igual a %{min} (o tamanho atual é %{total_file_size})"
19
20
  total_file_size_not_between: "o tamanho total do arquivo deve estar entre %{min} e %{max} (o tamanho atual é %{total_file_size})"
21
+ total_file_size_not_equal_to: "o tamanho total do arquivo deve ser igual a %{exact} (o tamanho atual é %{total_file_size})"
20
22
  duration_not_less_than: "a duração deve ser inferior a %{max} (a duração atual é %{duration})"
21
23
  duration_not_less_than_or_equal_to: "a duração deve ser inferior ou igual a %{max} (a duração atual é %{duration})"
22
24
  duration_not_greater_than: "a duração tem de ser superior a %{min} (a duração atual é %{duration})"
23
25
  duration_not_greater_than_or_equal_to: "a duração deve ser maior ou igual a %{min} (a duração atual é %{duration})"
24
26
  duration_not_between: "a duração deve estar entre %{min} e %{max} (a duração atual é %{duration})"
27
+ duration_not_equal_to: "a duração deve ser igual a %{exact} (a duração atual é %{duration})"
25
28
  limit_out_of_range:
26
29
  zero: "nenhum arquivo anexado (deve ter entre %{min} e %{max})"
27
30
  one: "apenas 1 arquivo anexado (deve ter entre %{min} e %{max})"
@@ -52,3 +55,9 @@ pt-BR:
52
55
  aspect_ratio_not_x_y: "deve ser %{authorized_aspect_ratios} (o arquivo atual é %{width}x%{height}px)"
53
56
  aspect_ratio_invalid: "tem uma proporção inválida (as proporções válidas são %{authorized_aspect_ratios})"
54
57
  file_not_processable: "não é identificado como um arquivo de mídia válido"
58
+ pages_not_less_than: "a contagem de páginas deve ser menor que %{max} (a contagem atual de páginas é %{pages})"
59
+ pages_not_less_than_or_equal_to: "a contagem de páginas deve ser menor ou igual a %{max} (a contagem atual de páginas é %{pages})"
60
+ pages_not_greater_than: "a contagem de páginas deve ser maior que %{min} (a contagem atual de páginas é %{pages})"
61
+ pages_not_greater_than_or_equal_to: "a contagem de páginas deve ser maior ou igual a %{min} (a contagem atual de páginas é %{pages})"
62
+ pages_not_between: "a contagem de páginas deve estar entre %{min} e %{max} (a contagem atual de páginas é %{pages})"
63
+ pages_not_equal_to: "a contagem de páginas deve ser igual a %{exact} (a contagem atual de páginas é %{pages})"
@@ -12,16 +12,19 @@ ru:
12
12
  file_size_not_greater_than: "размер файла должен быть больше %{min} (текущий размер %{file_size})"
13
13
  file_size_not_greater_than_or_equal_to: "размер файла должен быть больше или равен %{min} (текущий размер %{file_size})"
14
14
  file_size_not_between: "размер файла должен быть между %{min} и %{max} (текущий размер %{file_size})"
15
+ file_size_not_equal_to: "размер файла должен быть равен %{exact} (текущий размер %{file_size})"
15
16
  total_file_size_not_less_than: "общий размер файла должен быть меньше %{max} (текущий размер %{total_file_size})"
16
17
  total_file_size_not_less_than_or_equal_to: "общий размер файла должен быть меньше или равен %{max} (текущий размер %{total_file_size})"
17
18
  total_file_size_not_greater_than: "общий размер файла должен быть больше %{min} (текущий размер %{total_file_size})"
18
19
  total_file_size_not_greater_than_or_equal_to: "общий размер файла должен быть больше или равен %{min} (текущий размер %{total_file_size})"
19
20
  total_file_size_not_between: "общий размер файла должен быть между %{min} и %{max} (текущий размер %{total_file_size})"
21
+ total_file_size_not_equal_to: "общий размер файла должен быть равен %{exact} (текущий размер %{total_file_size})"
20
22
  duration_not_less_than: "длительность должна быть меньше %{max} (текущая продолжительность составляет %{duration})"
21
23
  duration_not_less_than_or_equal_to: "длительность должна быть меньше или равна %{max} (текущая продолжительность составляет %{duration})"
22
24
  duration_not_greater_than: "длительность должна быть больше %{min} (текущая продолжительность составляет %{duration})"
23
25
  duration_not_greater_than_or_equal_to: "длительность должна быть больше или равна %{min} (текущая продолжительность составляет %{duration})"
24
26
  duration_not_between: "длительность должна быть между %{min} и %{max} (текущая продолжительность составляет %{duration})"
27
+ duration_not_equal_to: "длительность должна быть равна %{exact} (текущая продолжительность составляет %{duration})"
25
28
  limit_out_of_range:
26
29
  zero: "никаких файлов не прилагается (должно иметь от %{min} до %{max} файлов)"
27
30
  one: "прилагается только 1 файл (должно иметь от %{min} до %{max} файлов)"
@@ -52,3 +55,9 @@ ru:
52
55
  aspect_ratio_not_x_y: "должно быть %{authorized_aspect_ratios} (текущий файл %{width}x%{height} пикселей)"
53
56
  aspect_ratio_invalid: "имеет недействительное соотношение сторон (действительные соотношения сторон %{authorized_aspect_ratios})"
54
57
  file_not_processable: "не идентифицируется как действительный медиафайл"
58
+ pages_not_less_than: "количество страниц должно быть меньше %{max} (текущее количество страниц %{pages})"
59
+ pages_not_less_than_or_equal_to: "количество страниц должно быть меньше или равно %{max} (текущее количество страниц %{pages})"
60
+ pages_not_greater_than: "количество страниц должно быть больше %{min} (текущее количество страниц %{pages})"
61
+ pages_not_greater_than_or_equal_to: "количество страниц должно быть больше или равно %{min} (текущее количество страниц %{pages})"
62
+ pages_not_between: "количество страниц должно быть между %{min} и %{max} (текущее количество страниц %{pages})"
63
+ pages_not_equal_to: "количество страниц должно быть равно %{exact} (текущее количество страниц %{pages})"
@@ -12,16 +12,19 @@ sv:
12
12
  file_size_not_greater_than: "filstorleken måste vara större än %{min} (nuvarande storlek är %{file_size})"
13
13
  file_size_not_greater_than_or_equal_to: "filstorleken måste vara större än eller lika med %{min} (nuvarande storlek är %{file_size})"
14
14
  file_size_not_between: "filstorleken måste vara mellan %{min} och %{max} (nuvarande storlek är %{file_size})"
15
+ file_size_not_equal_to: "filstorleken måste vara lika med %{exact} (nuvarande storlek är %{file_size})"
15
16
  total_file_size_not_less_than: "total filstorlek måste vara mindre än %{max} (nuvarande storlek är %{total_file_size})"
16
17
  total_file_size_not_less_than_or_equal_to: "total filstorlek måste vara mindre än eller lika med %{max} (nuvarande storlek är %{total_file_size})"
17
18
  total_file_size_not_greater_than: "total filstorlek måste vara större än %{min} (nuvarande storlek är %{total_file_size})"
18
19
  total_file_size_not_greater_than_or_equal_to: "total filstorlek måste vara större än eller lika med %{min} (nuvarande storlek är %{total_file_size})"
19
20
  total_file_size_not_between: "total filstorlek måste vara mellan %{min} och %{max} (nuvarande storlek är %{total_file_size})"
21
+ total_file_size_not_equal_to: "total filstorlek måste vara lika med %{exact} (nuvarande storlek är %{total_file_size})"
20
22
  duration_not_less_than: "varaktigheten måste vara mindre än %{max} (nuvarande varaktighet är %{varaktighet})"
21
23
  duration_not_less_than_or_equal_to: "varaktigheten måste vara mindre än eller lika med %{max} (nuvarande varaktighet är %{varaktighet})"
22
24
  duration_not_greater_than: "varaktigheten måste vara större än %{min} (nuvarande varaktighet är %{varaktighet})"
23
25
  duration_not_greater_than_or_equal_to: "varaktigheten måste vara större än eller lika med %{min} (nuvarande varaktighet är %{varaktighet})"
24
- duration_not_between: "varaktighet måste vara mellan %{min} och %{max} (nuvarande varaktighet är %{varaktighet})"
26
+ duration_not_between: "varaktigheten måste vara mellan %{min} och %{max} (nuvarande varaktighet är %{varaktighet})"
27
+ duration_not_equal_to: "varaktigheten måste vara lika med %{exact} (nuvarande varaktighet är %{varaktighet})"
25
28
  limit_out_of_range:
26
29
  zero: "inga filer bifogade (måste ha mellan %{min} och %{max} filer)"
27
30
  one: "endast en fil bifogad (måste ha mellan %{min} och %{max} filer)"
@@ -52,3 +55,9 @@ sv:
52
55
  aspect_ratio_not_x_y: "måste vara% { authorized_aspect_ratios } (nuvarande fil är %{width}x%{height} pixlar)"
53
56
  aspect_ratio_invalid: "har ett ogiltigt bildförhållande (giltiga bildförhållanden är %{förväntat_aspect_ratios})"
54
57
  file_not_processable: "identifieras inte som en giltig mediefil"
58
+ pages_not_less_than: "sidantal måste vara mindre än %{max} (nuvarande sidantal är %{pages})"
59
+ pages_not_less_than_or_equal_to: "sidantal måste vara mindre än eller lika med %{max} (nuvarande antal sidor är %{pages})"
60
+ pages_not_greater_than: "sidantal måste vara större än %{min} (nuvarande sidantal är %{pages})"
61
+ pages_not_greater_than_or_equal_to: "sidantal måste vara större än eller lika med %{min} (nuvarande antal sidor är %{pages})"
62
+ pages_not_between: "sidantal måste vara mellan %{min} och %{max} (nuvarande antal sidor är %{pages})"
63
+ pages_not_equal_to: "sidantal måste vara lika med %{exact} (nuvarande sidantal är %{pages})"
@@ -12,16 +12,19 @@ tr:
12
12
  file_size_not_greater_than: "dosya boyutu %{min} boyutundan büyük olmalıdır (geçerli boyut %{file_size}'dir)"
13
13
  file_size_not_greater_than_or_equal_to: "dosya boyutu %{min} değerinden büyük veya eşit olmalıdır (geçerli boyut %{file_size}'dir)"
14
14
  file_size_not_between: "dosya boyutu %{min} ile %{max} arasında olmalıdır (geçerli boyut %{file_size}'dir)"
15
+ file_size_not_equal_to: "dosya boyutu %{exact} değerine eşit olmalıdır (geçerli boyut %{file_size}'dir)"
15
16
  total_file_size_not_less_than: "toplam dosya boyutu %{max} boyutundan küçük olmalıdır (geçerli boyut %{total_file_size}'dir)"
16
17
  total_file_size_not_less_than_or_equal_to: "toplam dosya boyutu %{max} değerinden küçük veya ona eşit olmalıdır (geçerli boyut %{total_file_size}'dir)"
17
18
  total_file_size_not_greater_than: "toplam dosya boyutu %{min} boyutundan büyük olmalıdır (geçerli boyut %{total_file_size}'dir)"
18
19
  total_file_size_not_greater_than_or_equal_to: "toplam dosya boyutu %{min} değerinden büyük veya eşit olmalıdır (geçerli boyut %{total_file_size}'dir)"
19
20
  total_file_size_not_between: "toplam dosya boyutu %{min} ile %{max} arasında olmalıdır (geçerli boyut %{total_file_size}'dir)"
21
+ total_file_size_not_equal_to: "toplam dosya boyutu %{exact} değerine eşit olmalıdır (geçerli boyut %{total_file_size}'dir)"
20
22
  duration_not_less_than: "süre %{max} değerinden az olmalıdır (geçerli süre %{duration})"
21
23
  duration_not_less_than_or_equal_to: "süre %{max} değerine eşit veya daha düşük olmalıdır (geçerli süre %{duration})"
22
24
  duration_not_greater_than: "süre %{min} değerinden büyük olmalıdır (geçerli süre %{duration})"
23
25
  duration_not_greater_than_or_equal_to: "süre %{min} değerine eşit veya daha büyük olmalıdır (geçerli süre %{duration})"
24
26
  duration_not_between: "süre %{min} ile %{max} arasında olmalıdır (geçerli süre %{duration})"
27
+ duration_not_equal_to: "süre %{exact} değerine eşit olmalıdır (geçerli süre %{duration})"
25
28
  limit_out_of_range:
26
29
  zero: "dosya eklenmedi ( %{min} ve %{max} dosyaları arasında olmalıdır)"
27
30
  one: "yalnızca 1 dosya eklenmiş ( %{min} ve %{max} dosyaları arasında olmalıdır)"
@@ -52,3 +55,9 @@ tr:
52
55
  aspect_ratio_not_x_y: "%{authorized_aspect_ratios} olmalıdır (geçerli dosya %{width}x%{height}px)"
53
56
  aspect_ratio_invalid: "geçersiz bir en boy oranına sahiptir (geçerli en boy oranları %{authorized_aspect_ratios})"
54
57
  file_not_processable: "geçerli bir medya dosyası olarak tanımlanmadı"
58
+ pages_not_less_than: "sayfa sayısı %{max} değerinden az olmalıdır (geçerli sayfa sayısı %{pages})"
59
+ pages_not_less_than_or_equal_to: "sayfa sayısı %{max} değerinden az veya eşit olmalıdır (geçerli sayfa sayısı %{pages})"
60
+ pages_not_greater_than: "sayfa sayısı %{min} değerinden fazla olmalıdır (geçerli sayfa sayısı %{pages})"
61
+ pages_not_greater_than_or_equal_to: "sayfa sayısı %{min} değerinden fazla veya eşit olmalıdır (geçerli sayfa sayısı %{pages})"
62
+ pages_not_between: "sayfa sayısı %{min} ile %{max} arasında olmalıdır (geçerli sayfa sayısı %{pages})"
63
+ pages_not_equal_to: "sayfa sayısı %{exact} değerine eşit olmalıdır (geçerli sayfa sayısı %{pages})"
@@ -12,16 +12,19 @@ uk:
12
12
  file_size_not_greater_than: "розмір файлу має бути більшим ніж %{min} (поточний розмір %{file_size})"
13
13
  file_size_not_greater_than_or_equal_to: "розмір файлу має бути більшим або дорівнювати %{min} (поточний розмір %{file_size})"
14
14
  file_size_not_between: "розмір файлу має бути від %{min} до %{max} (поточний розмір %{file_size})"
15
+ file_size_not_equal_to: "розмір файлу має бути рівний %{exact} (поточний розмір %{file_size})"
15
16
  total_file_size_not_less_than: "загальний розмір файлу має бути менше %{max} (поточний розмір %{total_file_size})"
16
17
  total_file_size_not_less_than_or_equal_to: "загальний розмір файлу має бути меншим або дорівнювати %{max} (поточний розмір %{total_file_size})"
17
18
  total_file_size_not_greater_than: "загальний розмір файлу має бути більшим ніж %{min} (поточний розмір %{total_file_size})"
18
19
  total_file_size_not_greater_than_or_equal_to: "загальний розмір файлу має бути більшим або дорівнювати %{min} (поточний розмір %{total_file_size})"
19
20
  total_file_size_not_between: "загальний розмір файлу має бути від %{min} до %{max} (поточний розмір %{total_file_size})"
21
+ total_file_size_not_equal_to: "загальний розмір файлу має бути рівний %{exact} (поточний розмір %{total_file_size})"
20
22
  duration_not_less_than: "тривалість повинна бути меншою ніж %{max} (поточна тривалість становить %{duration})"
21
23
  duration_not_less_than_or_equal_to: "тривалість повинна бути меншою або дорівнює %{max} (поточна тривалість становить %{duration})"
22
24
  duration_not_greater_than: "тривалість повинна бути більше %{min} (поточна тривалість становить %{duration})"
23
25
  duration_not_greater_than_or_equal_to: "тривалість повинна бути більшою або дорівнює %{min} (поточна тривалість становить %{duration})"
24
26
  duration_not_between: "тривалість повинна бути між %{min} і %{max} (поточна тривалість становить %{duration})"
27
+ duration_not_equal_to: "тривалість повинна бути рівна %{exact} (поточна тривалість становить %{duration})"
25
28
  limit_out_of_range:
26
29
  zero: "немає доданих файлів (повинно мати між %{min} і %{max} файли)"
27
30
  one: "додається лише 1 файл (повинен мати між %{min} і %{max} файли)"
@@ -52,3 +55,9 @@ uk:
52
55
  aspect_ratio_not_x_y: "повинен бути %{authorized_aspect_ratios} (поточний файл становить %{width}x%{height} пікселям)"
53
56
  aspect_ratio_invalid: "має недійсне співвідношення сторін (дійсні співвідношення сторін %{authorized_aspect_ratios})"
54
57
  file_not_processable: "не ідентифікується як дійсний медіа-файл"
58
+ pages_not_less_than: "кількість сторінок має бути менше %{max} (поточна кількість сторінок %{pages})"
59
+ pages_not_less_than_or_equal_to: "кількість сторінок має бути меншою або дорівнювати %{max} (поточна кількість сторінок становить %{pages})"
60
+ pages_not_greater_than: "кількість сторінок має бути більшою за %{min} (поточна кількість сторінок становить %{pages})"
61
+ pages_not_greater_than_or_equal_to: "кількість сторінок має бути більшою або дорівнювати %{min} (поточна кількість сторінок становить %{pages})"
62
+ pages_not_between: "кількість сторінок має бути від %{min} до %{max} (поточна кількість сторінок становить %{pages})"
63
+ pages_not_equal_to: "кількість сторінок має дорівнювати %{exact} (поточна кількість сторінок - %{pages})"
@@ -12,16 +12,19 @@ vi:
12
12
  file_size_not_greater_than: "kích thước tệp phải lớn hơn %{min} (kích thước hiện tại là %{file_size})"
13
13
  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} (kích thước hiện tại là %{file_size})"
14
14
  file_size_not_between: "kích thước tệp phải nằm trong khoảng từ %{min} đến %{max} (kích thước hiện tại là %{file_size})"
15
+ file_size_not_equal_to: "kích thước tệp phải bằng %{exact} (kích thước hiện tại là %{file_size})"
15
16
  total_file_size_not_less_than: "tổng kích thước tệp phải nhỏ hơn %{max} (kích thước hiện tại là %{total_file_size})"
16
17
  total_file_size_not_less_than_or_equal_to: "tổng kích thước tệp phải nhỏ hơn hoặc bằng %{max} (kích thước hiện tại là %{total_file_size})"
17
18
  total_file_size_not_greater_than: "tổng kích thước tệp phải lớn hơn %{min} (kích thước hiện tại là %{total_file_size})"
18
19
  total_file_size_not_greater_than_or_equal_to: "tổng kích thước tệp phải lớn hơn hoặc bằng %{min} (kích thước hiện tại là %{total_file_size})"
19
20
  total_file_size_not_between: "tổng kích thước tệp phải nằm trong khoảng từ %{min} đến %{max} (kích thước hiện tại là %{total_file_size})"
21
+ total_file_size_not_equal_to: "tổng kích thước tệp phải bằng %{exact} (kích thước hiện tại là %{total_file_size})"
20
22
  duration_not_less_than: "thời lượng phải nhỏ hơn %{max} (thời lượng hiện tại là %{duration})"
21
23
  duration_not_less_than_or_equal_to: "thời lượng phải nhỏ hơn hoặc bằng %{max} (thời lượng hiện tại là %{duration})"
22
24
  duration_not_greater_than: "thời lượng phải lớn hơn %{min} (thời lượng hiện tại là %{duration})"
23
25
  duration_not_greater_than_or_equal_to: "thời lượng phải lớn hơn hoặc bằng %{min} (thời lượng hiện tại là %{duration})"
24
26
  duration_not_between: "thời lượng phải nằm trong khoảng từ %{min} và %{max} (thời lượng hiện tại là %{duration})"
27
+ duration_not_equal_to: "thời lượng phải bằng %{exact} (thời lượng hiện tại là %{duration})"
25
28
  limit_out_of_range:
26
29
  zero: "không có tệp nào được đính kèm (phải có giữa các tệp %{min} và %{max})"
27
30
  one: "chỉ có 1 tệp được đính kèm (phải có giữa các tệp %{min} và %{max})"
@@ -52,3 +55,9 @@ vi:
52
55
  aspect_ratio_not_x_y: "phải là %{authorized_aspect_ratios} (tệp hiện tại là %{width}x%{height}px)"
53
56
  aspect_ratio_invalid: "có tỷ lệ khung hình không hợp lệ (tỷ lệ khung hình hợp lệ là %{authorized_aspect_ratios})"
54
57
  file_not_processable: "không được xác định là tệp phương tiện hợp lệ"
58
+ pages_not_less_than: "số trang phải nhỏ hơn %{max} (số trang hiện tại là %{pages})"
59
+ pages_not_less_than_or_equal_to: "số trang phải nhỏ hơn hoặc bằng %{max} (số trang hiện tại là %{pages})"
60
+ pages_not_greater_than: "số trang phải lớn hơn %{min} (số trang hiện tại là %{pages})"
61
+ pages_not_greater_than_or_equal_to: "số trang phải lớn hơn hoặc bằng %{min} (số trang hiện tại là %{pages})"
62
+ pages_not_between: "số trang phải nằm trong khoảng từ %{min} đến %{max} (số trang hiện tại là %{pages})"
63
+ pages_not_equal_to: "số trang phải bằng %{exact} (số trang hiện tại là %{pages})"
@@ -12,16 +12,19 @@ zh-CN:
12
12
  file_size_not_greater_than: "文件大小必须大于 %{min}(当前大小为 %{file_size})"
13
13
  file_size_not_greater_than_or_equal_to: "文件大小必须大于或等于 %{min}(当前大小为 %{file_size})"
14
14
  file_size_not_between: "文件大小必须介于 %{min} 和 %{max} 之间(当前大小为 %{file_size})"
15
+ file_size_not_equal_to: "文件大小必须等于 %{exact}(当前大小为 %{file_size})"
15
16
  total_file_size_not_less_than: "总文件大小必须小于 %{max}(当前大小为 %{total_file_size})"
16
17
  total_file_size_not_less_than_or_equal_to: "文件总大小必须小于或等于 %{max}(当前大小为 %{total_file_size})"
17
18
  total_file_size_not_greater_than: "总文件大小必须大于 %{min}(当前大小为 %{total_file_size})"
18
19
  total_file_size_not_greater_than_or_equal_to: "文件总大小必须大于或等于 %{min}(当前大小为 %{total_file_size})"
19
20
  total_file_size_not_between: "总文件大小必须介于 %{min} 和 %{max} 之间(当前大小为 %{total_file_size})"
21
+ total_file_size_not_equal_to: "总文件大小必须等于 %{exact}(当前大小为 %{total_file_size})"
20
22
  duration_not_less_than: "持续时间必须小于%{max}(当前持续时间为%{duration})"
21
23
  duration_not_less_than_or_equal_to: "持续时间必须小于或等于%{max}(当前持续时间为%{duration})"
22
24
  duration_not_greater_than: "持续时间必须大于%{min}(当前持续时间为%{duration})"
23
25
  duration_not_greater_than_or_equal_to: "持续时间必须大于或等于%{min}(当前持续时间为%{duration})"
24
26
  duration_not_between: "持续时间必须在%{min}和%{max}之间(当前持续时间为%{duration})"
27
+ duration_not_equal_to: "持续时间必须等于%{exact}(当前持续时间为%{duration})"
25
28
  limit_out_of_range:
26
29
  zero: "未附加文件(必须具有%{min}和%{max}文件之间)"
27
30
  one: "仅附加1个文件(必须具有%{min}和%{max}文件之间)"
@@ -52,3 +55,9 @@ zh-CN:
52
55
  expect_ratio_not_x_y: "必须为 %{authorized_aspect_ratios}(当前文件为 %{width} x%{height} 像素)"
53
56
  expect_ratio_not_in_list: "具有无效的长宽比(有效长宽比为 %{authorized_aspect_ratios})"
54
57
  file_not_processable: "未标识为有效的媒体文件"
58
+ pages_not_less_than: "页数必须小于 %{max}(当前页数为 %{pages})"
59
+ pages_not_less_than_or_equal_to: "页数必须小于或等于 %{max}(当前页数为 %{pages})"
60
+ pages_not_greater_than: "页数必须大于 %{min}(当前页数为 %{pages})"
61
+ pages_not_greater_than_or_equal_to: "页数必须大于或等于 %{min}(当前页数为 %{pages})"
62
+ pages_not_between: "页数必须介于 %{min} 和 %{max} 之间(当前页数为 %{pages})"
63
+ pages_not_equal_to: "页数必须等于 %{exact}(当前页数为%{pages})"
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module ActiveStorageValidations
6
+ # = ActiveStorageValidations PDF \Analyzer
7
+ #
8
+ # Extracts the following from a pdf attachable:
9
+ #
10
+ # * Width (pts) => for the first page only
11
+ # * Height (pts) => for the first page only
12
+ # * Pages (integer) => number of pages in the pdf
13
+ #
14
+ # Example:
15
+ #
16
+ # ActiveStorageValidations::Analyzer::PdfAnalyzer.new(attachable).metadata
17
+ # # => { width: 150, height: 150, pages: 1 }
18
+ #
19
+ # This analyzer requires the {poppler}[https://pdf2image.readthedocs.io/en/latest/installation.html] system library, which is not provided by \Rails.
20
+ class Analyzer::PdfAnalyzer < Analyzer
21
+ def metadata
22
+ read_media do |media|
23
+ {
24
+ width: width,
25
+ height: height,
26
+ pages: pages
27
+ }.compact
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def read_media
34
+ Tempfile.create(binmode: true) do |tempfile|
35
+ begin
36
+ if media(tempfile).present?
37
+ yield media(tempfile)
38
+ else
39
+ logger.info "Skipping pdf file metadata analysis because poppler doesn't support the file"
40
+ {}
41
+ end
42
+ ensure
43
+ tempfile.close
44
+ end
45
+ end
46
+ rescue Errno::ENOENT
47
+ logger.info "Skipping pdf file metadata analysis because poppler isn't installed"
48
+ {}
49
+ end
50
+
51
+ def media_from_path(path)
52
+ instrument(File.basename(pdfinfo_path)) do
53
+ stdout, _stderr, status = Open3.capture3(
54
+ pdfinfo_path,
55
+ path
56
+ )
57
+
58
+ status.success? ? stdout_to_hash(stdout) : nil
59
+ end
60
+ end
61
+
62
+ def stdout_to_hash(stdout)
63
+ stdout.lines.each_with_object({}) do |line, hash|
64
+ key, value = line.strip.split(":", 2)
65
+ hash[normalize_stdout_key(key)] = value.strip if key && value
66
+ end
67
+ end
68
+
69
+ def normalize_stdout_key(key)
70
+ key.strip.underscore.gsub(/\s+/, "_").gsub(/"/, "")
71
+ end
72
+
73
+ def pdfinfo_path
74
+ ActiveStorage.paths[:pdfinfo] || "pdfinfo"
75
+ end
76
+
77
+ def width
78
+ @media["page_size"].scan(/\d+/)[0].to_i
79
+ end
80
+
81
+ def height
82
+ @media["page_size"].scan(/\d+/)[1].to_i
83
+ end
84
+
85
+ def pages
86
+ @media["pages"].to_i
87
+ end
88
+ end
89
+ end
@@ -18,6 +18,7 @@ module ActiveStorageValidations
18
18
  greater_than
19
19
  greater_than_or_equal_to
20
20
  between
21
+ equal_to
21
22
  ].freeze
22
23
 
23
24
  def initialize(*args)
@@ -29,10 +30,14 @@ module ActiveStorageValidations
29
30
 
30
31
  def check_validity!
31
32
  unless AVAILABLE_CHECKS.one? { |argument| options.key?(argument) }
32
- raise ArgumentError, "You must pass either :less_than(_or_equal_to), :greater_than(_or_equal_to), or :between to the validator"
33
+ raise ArgumentError, "You must pass either :less_than(_or_equal_to), :greater_than(_or_equal_to), :between or :equal_to to the validator"
33
34
  end
34
35
  end
35
36
 
37
+ def validate_each(record, attribute, value)
38
+ raise NotImplementedError
39
+ end
40
+
36
41
  private
37
42
 
38
43
  def is_valid?(value, flat_options)
@@ -48,11 +53,14 @@ module ActiveStorageValidations
48
53
  value > flat_options[:greater_than]
49
54
  elsif flat_options[:greater_than_or_equal_to].present?
50
55
  value >= flat_options[:greater_than_or_equal_to]
56
+ elsif flat_options[:equal_to].present?
57
+ value == flat_options[:equal_to]
51
58
  end
52
59
  end
53
60
 
54
61
  def populate_error_options(errors_options, flat_options)
55
62
  errors_options[:min] = format_bound_value(min(flat_options))
63
+ errors_options[:exact] = format_bound_value(exact(flat_options))
56
64
  errors_options[:max] = format_bound_value(max(flat_options))
57
65
  end
58
66
 
@@ -64,6 +72,10 @@ module ActiveStorageValidations
64
72
  flat_options[:between]&.min || flat_options[:greater_than] || flat_options[:greater_than_or_equal_to]
65
73
  end
66
74
 
75
+ def exact(flat_options)
76
+ flat_options[:equal_to]
77
+ end
78
+
67
79
  def max(flat_options)
68
80
  flat_options[:between]&.max || flat_options[:less_than] || flat_options[:less_than_or_equal_to]
69
81
  end
@@ -13,6 +13,7 @@ module ActiveStorageValidations
13
13
  duration_not_greater_than
14
14
  duration_not_greater_than_or_equal_to
15
15
  duration_not_between
16
+ duration_not_equal_to
16
17
  ].freeze
17
18
  METADATA_KEYS = %i[duration].freeze
18
19
 
@@ -58,6 +58,11 @@ module ActiveStorageValidations
58
58
  self
59
59
  end
60
60
 
61
+ def equal_to(value)
62
+ @exact = value
63
+ self
64
+ end
65
+
61
66
  def matches?(subject)
62
67
  @subject = subject.is_a?(Class) ? subject.new : subject
63
68
 
@@ -68,7 +73,8 @@ module ActiveStorageValidations
68
73
  not_lower_than_min? &&
69
74
  higher_than_min? &&
70
75
  lower_than_max? &&
71
- not_higher_than_max?
76
+ not_higher_than_max? &&
77
+ equal_to_exact?
72
78
  end
73
79
 
74
80
  protected
@@ -103,6 +109,10 @@ module ActiveStorageValidations
103
109
  @max.nil? || @max == Float::INFINITY || !passes_validation_with_value(@max + 1)
104
110
  end
105
111
 
112
+ def equal_to_exact?
113
+ @exact.nil? || passes_validation_with_value(@exact)
114
+ end
115
+
106
116
  def smallest_measurement
107
117
  raise NotImplementedError
108
118
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_comparison_validator_matcher"
4
+
5
+ module ActiveStorageValidations
6
+ module Matchers
7
+ def validate_pages_of(attribute_name)
8
+ PagesValidatorMatcher.new(attribute_name)
9
+ end
10
+
11
+ class PagesValidatorMatcher < BaseComparisonValidatorMatcher
12
+ def description
13
+ "validate file number of pages of :#{@attribute_name}"
14
+ end
15
+
16
+ def failure_message
17
+ message = [ "is expected to validate file number of pages of :#{@attribute_name}" ]
18
+ build_failure_message(message)
19
+ message.join("\n")
20
+ end
21
+
22
+ private
23
+
24
+ def failure_message_unit
25
+ "pages"
26
+ end
27
+
28
+ def smallest_measurement
29
+ 1
30
+ end
31
+
32
+ def mock_value_for(io, pages)
33
+ Matchers.mock_metadata(io, { pages: pages }) do
34
+ yield
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -9,6 +9,7 @@ require "active_storage_validations/matchers/dimension_validator_matcher"
9
9
  require "active_storage_validations/matchers/duration_validator_matcher"
10
10
  require "active_storage_validations/matchers/size_validator_matcher"
11
11
  require "active_storage_validations/matchers/total_size_validator_matcher"
12
+ require "active_storage_validations/matchers/pages_validator_matcher"
12
13
 
13
14
  module ActiveStorageValidations
14
15
  module Matchers
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_comparison_validator"
4
+ require_relative "shared/asv_analyzable"
5
+ require_relative "shared/asv_attachable"
6
+
7
+ module ActiveStorageValidations
8
+ class PagesValidator < BaseComparisonValidator
9
+ include ASVAnalyzable
10
+ include ASVAttachable
11
+
12
+ ERROR_TYPES = %i[
13
+ pages_not_less_than
14
+ pages_not_less_than_or_equal_to
15
+ pages_not_greater_than
16
+ pages_not_greater_than_or_equal_to
17
+ pages_not_between
18
+ pages_not_equal_to
19
+ ].freeze
20
+ METADATA_KEYS = %i[pages].freeze
21
+
22
+ delegate :number_to_delimited, to: ActiveSupport::NumberHelper
23
+
24
+ def validate_each(record, attribute, _value)
25
+ return if no_attachments?(record, attribute)
26
+
27
+ validate_changed_files_from_metadata(record, attribute, METADATA_KEYS)
28
+ end
29
+
30
+ private
31
+
32
+ def is_valid?(record, attribute, file, metadata)
33
+ flat_options = set_flat_options(record)
34
+ errors_options = initialize_error_options(options, file)
35
+
36
+ unless valid_metadata?(metadata)
37
+ add_media_metadata_missing_error(record, attribute, file, errors_options)
38
+ return false
39
+ end
40
+
41
+ return true if super(metadata[:pages], flat_options)
42
+
43
+ populate_error_options(errors_options, flat_options)
44
+ errors_options[:pages] = format_bound_value(metadata[:pages])
45
+
46
+ keys = AVAILABLE_CHECKS & flat_options.keys
47
+ error_type = "pages_not_#{keys.first}".to_sym
48
+
49
+ add_error(record, attribute, error_type, **errors_options)
50
+ false
51
+ end
52
+
53
+ def valid_metadata?(metadata)
54
+ metadata[:pages].to_i > 0
55
+ end
56
+
57
+ def format_bound_value(value)
58
+ number_to_delimited(value)
59
+ end
60
+ end
61
+ end
@@ -2,15 +2,18 @@
2
2
 
3
3
  module ActiveStorageValidations
4
4
  class Railtie < ::Rails::Railtie
5
- initializer "active_storage_validations.configure", after: :load_config_initializers do
5
+ # Using after: :load_config_initializers would cause a stack level too deep error
6
+ # See: https://github.com/igorkasyanchuk/active_storage_validations/issues/364
7
+
8
+ initializer "active_storage_validations.configure" do
6
9
  ActiveSupport.on_load(:active_record) do
7
- send :include, ActiveStorageValidations
10
+ include ActiveStorageValidations
8
11
  end
9
12
  end
10
13
 
11
14
  initializer "active_storage_validations.extend_active_storage_blob" do
12
15
  ActiveSupport.on_load(:active_storage_blob) do
13
- include(ActiveStorageValidations::ASVBlobMetadatable)
16
+ include ActiveStorageValidations::ASVBlobMetadatable
14
17
  end
15
18
  end
16
19
  end
@@ -38,6 +38,8 @@ module ActiveStorageValidations
38
38
  end
39
39
 
40
40
  def metadata_analyzer_for(attachable)
41
+ return pdf_analyzer_for(attachable) if attachable_content_type(attachable) == "application/pdf"
42
+
41
43
  case attachable_media_type(attachable)
42
44
  when "image" then image_analyzer_for(attachable)
43
45
  when "video" then video_analyzer_for(attachable)
@@ -46,6 +48,10 @@ module ActiveStorageValidations
46
48
  end
47
49
  end
48
50
 
51
+ def pdf_analyzer_for(attachable)
52
+ ActiveStorageValidations::Analyzer::PdfAnalyzer.new(attachable)
53
+ end
54
+
49
55
  def image_analyzer_for(attachable)
50
56
  case image_processor
51
57
  when :mini_magick
@@ -88,7 +88,7 @@ module ActiveStorageValidations
88
88
  # Retrieve the declared content_type from attachable without potential mime
89
89
  # type parameters (e.g. 'application/x-rar-compressed;version=5')
90
90
  def attachable_content_type(attachable)
91
- full_attachable_content_type(attachable) && content_type_without_parameters(full_attachable_content_type(attachable))
91
+ (full_attachable_content_type(attachable) && content_type_without_parameters(full_attachable_content_type(attachable)) || marcel_content_type_from_filename(attachable))
92
92
  end
93
93
 
94
94
  # Remove the potential mime type parameters from the content_type (e.g.