activestorage-validator 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/rspec.yml +1 -1
- data/README.md +110 -10
- data/activestorage-validator.gemspec +1 -1
- data/config/locales/de.yml +1 -0
- data/config/locales/en.yml +1 -0
- data/config/locales/es.yml +1 -0
- data/config/locales/fr.yml +1 -0
- data/config/locales/ja.yml +1 -0
- data/config/locales/pl.yml +1 -0
- data/config/locales/pt-br.yml +1 -0
- data/lib/activestorage/validator/blob.rb +23 -0
- data/lib/activestorage/validator/version.rb +1 -1
- data/skills/activestorage-validator/SKILL.md +132 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: acc9fae7338e419736df276e25e94e198cdc8a0ca9a2b80210c3175d684f5f22
|
|
4
|
+
data.tar.gz: 6b9be22b0d25c70bcf08cc51a05f9443982963d7e42e3817b90c60a596e5e071
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 57e1e9cd4353cfcb2aaeee8a2eef4bbedfe49b3382a3cacc7281fa1e5ec5ca1aa10158465a42e6eff8c2dbfd375b16f9ddb26c3027d1b337ca8fd0b41824c94b
|
|
7
|
+
data.tar.gz: 597d5cd7f32091fcf00a196fa4bd9d7404e5f129db45d0235c70768a01167d47ae9ec361b635b2732add9ba8be45c2bb2fef6db5a4b67e676130dfe7cf9fd13a
|
data/.github/workflows/rspec.yml
CHANGED
data/README.md
CHANGED
|
@@ -3,43 +3,143 @@
|
|
|
3
3
|
|
|
4
4
|
# ActiveStorage Validator
|
|
5
5
|
|
|
6
|
-
ActiveStorage
|
|
6
|
+
A Rails gem that makes it easy to add validations such as `content_type` and file size to ActiveStorage attachments.
|
|
7
|
+
|
|
8
|
+
- Supports both `has_one_attached` and `has_many_attached` of ActiveStorage
|
|
9
|
+
- Flexible validations for content type and file size
|
|
7
10
|
|
|
8
11
|
## Installation
|
|
9
12
|
|
|
13
|
+
### Using Bundler
|
|
14
|
+
|
|
10
15
|
Add this line to your application's Gemfile:
|
|
11
16
|
|
|
12
17
|
```ruby
|
|
13
18
|
gem 'activestorage-validator'
|
|
14
19
|
```
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
Then execute:
|
|
17
22
|
|
|
18
|
-
$ bundle
|
|
23
|
+
$ bundle install
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
### Install with gem command
|
|
21
26
|
|
|
22
27
|
$ gem install activestorage-validator
|
|
23
28
|
|
|
24
29
|
## Usage
|
|
25
30
|
|
|
31
|
+
You can add validations to models with ActiveStorage attachments using the `blob` validator.
|
|
32
|
+
|
|
26
33
|
```ruby
|
|
27
34
|
class User < ApplicationRecord
|
|
28
35
|
has_one_attached :avatar
|
|
29
36
|
has_many_attached :photos
|
|
30
37
|
|
|
31
|
-
|
|
32
|
-
validates :
|
|
33
|
-
|
|
38
|
+
# Allow only web image files
|
|
39
|
+
validates :avatar, presence: true, blob: { content_type: :web_image }
|
|
40
|
+
|
|
41
|
+
# Allow only JPEG/PNG, up to 5MB per file
|
|
42
|
+
validates :photos, presence: true, blob: { content_type: ["image/png", "image/jpg", "image/jpeg"], size_range: 1..(5.megabytes) }
|
|
43
|
+
|
|
44
|
+
# You can also use a regular expression for content_type
|
|
45
|
+
# validates :photos, blob: { content_type: %r{^image/}, size_range: 1..(5.megabytes) }
|
|
46
|
+
|
|
47
|
+
# Require the filename to have one of the allowed extensions
|
|
48
|
+
# validates :audio, blob: { content_type: "audio/mpeg", extension: %w[mp3] }
|
|
34
49
|
end
|
|
35
50
|
```
|
|
36
51
|
|
|
37
|
-
Note
|
|
52
|
+
> **Note:** For `has_many_attached`, the size validation is applied to each file individually.
|
|
53
|
+
|
|
54
|
+
## Validation Options
|
|
55
|
+
|
|
56
|
+
| Option | Description |
|
|
57
|
+
|--------------|---------------------------------------------------------------------------------------------|
|
|
58
|
+
| content_type | Allowed MIME types. Accepts a symbol (`:web_image`, `:image`, `:audio`, `:video`, `:text`), an array of MIME types, a regular expression, or a string (single MIME type). |
|
|
59
|
+
| size_range | Allowed file size range (e.g. `1..5.megabytes`) |
|
|
60
|
+
| extension | Allowed file extensions. Accepts a String or an Array of Strings. Case-insensitive, leading dot optional. Files without an extension are rejected. |
|
|
61
|
+
|
|
62
|
+
### content_type Examples
|
|
63
|
+
|
|
64
|
+
- **Symbol**
|
|
65
|
+
- `:web_image` ... PNG, JPEG, GIF, WebP (special handling)
|
|
66
|
+
- `:image`, `:audio`, `:video`, `:text` ... Calls `blob.image?`, `blob.audio?`, etc. (ActiveStorage built-in predicate)
|
|
67
|
+
- **Array**
|
|
68
|
+
- `["image/png", "image/jpg", "image/jpeg"]` ... Only allow these MIME types
|
|
69
|
+
- **Regexp**
|
|
70
|
+
- `%r{^image/}` ... Allow any image type (MIME type starts with `image/`)
|
|
71
|
+
- **String**
|
|
72
|
+
- `"application/pdf"` ... Only allow PDF files
|
|
73
|
+
|
|
74
|
+
### extension Examples
|
|
75
|
+
|
|
76
|
+
- **String**
|
|
77
|
+
- `extension: "mp3"` ... Only allow `.mp3` files
|
|
78
|
+
- **Array**
|
|
79
|
+
- `extension: %w[mp3 m4a]` ... Allow either `.mp3` or `.m4a`
|
|
80
|
+
- **Combined with content_type (AND)**
|
|
81
|
+
- `blob: { content_type: "audio/mpeg", extension: %w[mp3] }` ... Reject both `my_podcast` (no extension) and `my_podcast.wav` (wrong extension), even when their MIME types match.
|
|
82
|
+
|
|
83
|
+
The leading dot is optional (`"mp3"` and `".mp3"` behave the same), and matching is case-insensitive (`PHOTO.JPG` matches `extension: "jpg"`).
|
|
84
|
+
|
|
85
|
+
## I18n Error Message Options
|
|
86
|
+
|
|
87
|
+
Validation error messages are I18n compatible. The following interpolation keys are available in your translation files, according to the validator's implementation:
|
|
88
|
+
|
|
89
|
+
| Key | Description |
|
|
90
|
+
|----------------|---------------------------------------------|
|
|
91
|
+
| filename | The uploaded file's name |
|
|
92
|
+
| min_size | The minimum allowed file size (humanized) |
|
|
93
|
+
| max_size | The maximum allowed file size (humanized) |
|
|
94
|
+
| extension | The list of allowed extensions, joined by `, ` |
|
|
95
|
+
|
|
96
|
+
The following error types are used:
|
|
97
|
+
|
|
98
|
+
- `content_type` (invalid content type)
|
|
99
|
+
- `min_size_error` (file is too small)
|
|
100
|
+
- `max_size_error` (file is too large)
|
|
101
|
+
- `extension` (invalid file extension)
|
|
102
|
+
|
|
103
|
+
Example (config/locales/en.yml):
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
en:
|
|
107
|
+
errors:
|
|
108
|
+
messages:
|
|
109
|
+
content_type: "%{filename} has an invalid content type"
|
|
110
|
+
min_size_error: "%{filename} is too small (minimum is %{min_size})"
|
|
111
|
+
max_size_error: "%{filename} is too large (maximum is %{max_size})"
|
|
112
|
+
extension: "%{filename} has an invalid file extension (allowed: %{extension})"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Notes
|
|
116
|
+
|
|
117
|
+
- Some features may not work depending on your ActiveStorage or Rails version.
|
|
118
|
+
- Validation error messages are I18n compatible and support interpolation keys as described above.
|
|
119
|
+
|
|
120
|
+
## Agent skill
|
|
121
|
+
|
|
122
|
+
This repo ships an [agent skill](skills/activestorage-validator/) (`SKILL.md`,
|
|
123
|
+
plus a Japanese `SKILL-ja.md`) that teaches coding agents when and how to apply
|
|
124
|
+
`activestorage-validator`. Install it into your project with the GitHub CLI:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
gh skill install aki77/activestorage-validator
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
> `gh skill` is currently a GitHub CLI preview feature.
|
|
131
|
+
|
|
132
|
+
Alternatively, if your project already pulls `activestorage-validator` in via
|
|
133
|
+
Bundler, the [bundler-skills](https://github.com/aki77/bundler-skills) plugin
|
|
134
|
+
auto-syncs this skill on `bundle install` — keeping the skill version locked to
|
|
135
|
+
the gem version.
|
|
38
136
|
|
|
39
137
|
## Contributing
|
|
40
138
|
|
|
41
|
-
Bug reports and pull requests are welcome on GitHub
|
|
139
|
+
Bug reports and pull requests are welcome on GitHub via [issues](https://github.com/aki77/activestorage-validator/issues) or [pull requests](https://github.com/aki77/activestorage-validator/pulls).
|
|
140
|
+
|
|
141
|
+
This project adheres to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
|
42
142
|
|
|
43
143
|
## License
|
|
44
144
|
|
|
45
|
-
|
|
145
|
+
This gem is released under the MIT License. See [LICENSE.txt](LICENSE.txt) for details.
|
|
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
|
17
17
|
# Specify which files should be added to the gem when it is released.
|
|
18
18
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
19
19
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
20
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
20
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) || f.match(%r{^skills/.*-ja\.md$}) }
|
|
21
21
|
end
|
|
22
22
|
spec.bindir = "exe"
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
data/config/locales/de.yml
CHANGED
data/config/locales/en.yml
CHANGED
data/config/locales/es.yml
CHANGED
data/config/locales/fr.yml
CHANGED
|
@@ -5,3 +5,4 @@ fr:
|
|
|
5
5
|
content_type: "n'est pas un format de fichier valide"
|
|
6
6
|
min_size_error: "La taille du fichier doit être supérieure à %{min_size}"
|
|
7
7
|
max_size_error: "La taille du fichier doit être inférieure à %{max_size}"
|
|
8
|
+
extension: "a une extension de fichier non valide (autorisées : %{extension})"
|
data/config/locales/ja.yml
CHANGED
data/config/locales/pl.yml
CHANGED
data/config/locales/pt-br.yml
CHANGED
|
@@ -16,6 +16,15 @@ module ActiveRecord
|
|
|
16
16
|
unless valid_content_type?(value.blob)
|
|
17
17
|
record.errors.add(attribute, :content_type, filename: value.blob.filename.to_s)
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
unless valid_extension?(value.blob)
|
|
21
|
+
record.errors.add(
|
|
22
|
+
attribute,
|
|
23
|
+
:extension,
|
|
24
|
+
filename: value.blob.filename.to_s,
|
|
25
|
+
extension: Array(options[:extension]).map { |e| normalize_extension(e) }.join(', ')
|
|
26
|
+
)
|
|
27
|
+
end
|
|
19
28
|
end
|
|
20
29
|
end
|
|
21
30
|
|
|
@@ -37,6 +46,20 @@ module ActiveRecord
|
|
|
37
46
|
options[:content_type] == blob.content_type
|
|
38
47
|
end
|
|
39
48
|
end
|
|
49
|
+
|
|
50
|
+
def valid_extension?(blob)
|
|
51
|
+
return true if options[:extension].nil?
|
|
52
|
+
|
|
53
|
+
allowed = Array(options[:extension]).map { |e| normalize_extension(e) }
|
|
54
|
+
actual = normalize_extension(blob.filename.extension)
|
|
55
|
+
return false if actual.empty?
|
|
56
|
+
|
|
57
|
+
allowed.include?(actual)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def normalize_extension(value)
|
|
61
|
+
value.to_s.downcase.delete_prefix('.')
|
|
62
|
+
end
|
|
40
63
|
end
|
|
41
64
|
end
|
|
42
65
|
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: activestorage-validator
|
|
3
|
+
description: "Validate ActiveStorage attachments (content type, file size, file extension) with the activestorage-validator gem's `blob` validator. Use when adding validations to has_one_attached / has_many_attached attributes."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# activestorage-validator
|
|
7
|
+
|
|
8
|
+
A Rails gem that adds a `blob` validator for ActiveStorage attachments. It
|
|
9
|
+
validates content type, file size, and file extension on both
|
|
10
|
+
`has_one_attached` and `has_many_attached` attributes.
|
|
11
|
+
|
|
12
|
+
## First: determine applicability
|
|
13
|
+
|
|
14
|
+
Use this gem **only** for validating ActiveStorage attachments. For ordinary
|
|
15
|
+
(non-attachment) attributes, use the standard Rails validators (`presence`,
|
|
16
|
+
`length`, `numericality`, etc.).
|
|
17
|
+
|
|
18
|
+
| What you validate | Tool |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| ActiveStorage attachment (content type / size / extension) | **`blob:` validator (this gem)** |
|
|
21
|
+
| Plain string/number/boolean columns | Standard Rails validators |
|
|
22
|
+
| Presence of the attachment itself | `presence: true` (works alongside `blob:`) |
|
|
23
|
+
|
|
24
|
+
There is no special read path — declare `validates ..., blob: { ... }` and the
|
|
25
|
+
attachment is validated like any other Rails validation on `save`/`valid?`.
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
Declare validations on the model with the `blob:` validator.
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
class User < ApplicationRecord
|
|
33
|
+
has_one_attached :avatar
|
|
34
|
+
has_many_attached :photos
|
|
35
|
+
|
|
36
|
+
# Allow only web image files (PNG, JPEG, GIF, WebP)
|
|
37
|
+
validates :avatar, presence: true, blob: { content_type: :web_image }
|
|
38
|
+
|
|
39
|
+
# Allow only JPEG/PNG, up to 5MB per file
|
|
40
|
+
validates :photos, presence: true, blob: {
|
|
41
|
+
content_type: ["image/png", "image/jpg", "image/jpeg"],
|
|
42
|
+
size_range: 1..(5.megabytes)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Regexp content_type + required extension (AND condition)
|
|
46
|
+
validates :audio, blob: { content_type: "audio/mpeg", extension: %w[mp3] }
|
|
47
|
+
end
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> For `has_many_attached`, every option (including `size_range`) is applied to
|
|
51
|
+
> **each file individually**.
|
|
52
|
+
|
|
53
|
+
## Options (all optional, combine freely)
|
|
54
|
+
|
|
55
|
+
| Option | Accepts | Notes |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| `content_type` | Symbol / Array / Regexp / String | Allowed MIME type(s). See matching below. |
|
|
58
|
+
| `size_range` | Range | Allowed byte size, e.g. `1..(5.megabytes)`. |
|
|
59
|
+
| `extension` | String / Array | Allowed filename extension(s). |
|
|
60
|
+
|
|
61
|
+
### `content_type` matching
|
|
62
|
+
|
|
63
|
+
- **`:web_image`** — special-cased to `ActiveStorage.web_image_content_types`
|
|
64
|
+
(PNG, JPEG, GIF, WebP). Use this instead of `:image` when you want only
|
|
65
|
+
browser-safe images.
|
|
66
|
+
- **Other Symbol** (`:image`, `:audio`, `:video`, `:text`) — calls the
|
|
67
|
+
ActiveStorage built-in predicate `blob.image?` / `blob.audio?` / etc.
|
|
68
|
+
- **Array** — `["image/png", "image/jpeg"]`: exact membership of the blob's
|
|
69
|
+
content type in the list.
|
|
70
|
+
- **Regexp** — `%r{^image/}`: matched against the blob's content type.
|
|
71
|
+
- **String** — `"application/pdf"`: exact equality.
|
|
72
|
+
|
|
73
|
+
### `extension` matching
|
|
74
|
+
|
|
75
|
+
- Accepts a String (`"mp3"`) or Array (`%w[mp3 m4a]`).
|
|
76
|
+
- Leading dot is optional (`"mp3"` == `".mp3"`).
|
|
77
|
+
- Case-insensitive (`PHOTO.JPG` matches `extension: "jpg"`).
|
|
78
|
+
- **A file with no extension is always rejected** when `extension` is set.
|
|
79
|
+
- When combined with `content_type`, both must pass (AND). E.g.
|
|
80
|
+
`{ content_type: "audio/mpeg", extension: %w[mp3] }` rejects a `.wav` file
|
|
81
|
+
even if its MIME type happens to be `audio/mpeg`.
|
|
82
|
+
|
|
83
|
+
## I18n error messages
|
|
84
|
+
|
|
85
|
+
Errors are I18n-compatible. Error types and their interpolation keys:
|
|
86
|
+
|
|
87
|
+
| Error type | Interpolation keys | When |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| `content_type` | `filename` | content type not allowed |
|
|
90
|
+
| `min_size_error` | `filename`, `min_size` | smaller than `size_range.min` |
|
|
91
|
+
| `max_size_error` | `filename`, `max_size` | larger than `size_range.max` |
|
|
92
|
+
| `extension` | `filename`, `extension` | extension not allowed (or missing) |
|
|
93
|
+
|
|
94
|
+
`min_size` / `max_size` are humanized (`ActiveSupport::NumberHelper.number_to_human_size`).
|
|
95
|
+
`extension` is the allowed list joined by `, `.
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
# config/locales/en.yml
|
|
99
|
+
en:
|
|
100
|
+
errors:
|
|
101
|
+
messages:
|
|
102
|
+
content_type: "%{filename} has an invalid content type"
|
|
103
|
+
min_size_error: "%{filename} is too small (minimum is %{min_size})"
|
|
104
|
+
max_size_error: "%{filename} is too large (maximum is %{max_size})"
|
|
105
|
+
extension: "%{filename} has an invalid file extension (allowed: %{extension})"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The gem ships default messages for 7 locales: `en`, `ja`, `de`, `es`, `fr`,
|
|
109
|
+
`pl`, `pt-br`. Override them in your app's locale files using the keys above.
|
|
110
|
+
|
|
111
|
+
## Behavior details
|
|
112
|
+
|
|
113
|
+
- **Unattached values are skipped.** Validation returns early unless the
|
|
114
|
+
attachment is attached, so `blob:` does not enforce presence — add
|
|
115
|
+
`presence: true` separately if the attachment is required.
|
|
116
|
+
- **Size boundaries are inclusive.** A blob is valid when
|
|
117
|
+
`size_range.min <= byte_size <= size_range.max`. Below min → `min_size_error`;
|
|
118
|
+
above max → `max_size_error`.
|
|
119
|
+
- **Extension normalization** is `downcase` + strip a single leading dot; an
|
|
120
|
+
empty resulting extension (no extension on the file) fails.
|
|
121
|
+
- Options are independent — omit any you don't need; only the present ones run.
|
|
122
|
+
|
|
123
|
+
## Pitfalls
|
|
124
|
+
|
|
125
|
+
- A file **without an extension** is rejected whenever `extension:` is set —
|
|
126
|
+
even if its content type is allowed. Don't set `extension:` if you accept
|
|
127
|
+
extension-less uploads.
|
|
128
|
+
- For `has_many_attached`, `size_range` applies **per file**, not to the total.
|
|
129
|
+
- `blob:` alone does **not** require an attachment; pair it with `presence: true`
|
|
130
|
+
when the upload is mandatory.
|
|
131
|
+
- No special read/preload path exists — this is a plain `EachValidator`; it runs
|
|
132
|
+
on standard `valid?` / `save`.
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activestorage-validator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- aki
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rails
|
|
@@ -115,6 +115,7 @@ files:
|
|
|
115
115
|
- lib/activestorage/validator.rb
|
|
116
116
|
- lib/activestorage/validator/blob.rb
|
|
117
117
|
- lib/activestorage/validator/version.rb
|
|
118
|
+
- skills/activestorage-validator/SKILL.md
|
|
118
119
|
homepage: https://github.com/aki77/activestorage-validator
|
|
119
120
|
licenses:
|
|
120
121
|
- MIT
|
|
@@ -133,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
133
134
|
- !ruby/object:Gem::Version
|
|
134
135
|
version: '0'
|
|
135
136
|
requirements: []
|
|
136
|
-
rubygems_version:
|
|
137
|
+
rubygems_version: 4.0.10
|
|
137
138
|
specification_version: 4
|
|
138
139
|
summary: ActiveStorage blob validator.
|
|
139
140
|
test_files: []
|