activestorage-validator 0.7.0 → 0.8.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 +5 -11
- data/.gitignore +1 -0
- data/README.md +26 -3
- data/activestorage-validator.gemspec +3 -3
- data/gemfiles/rails72.gemfile +1 -1
- data/gemfiles/rails80.gemfile +1 -1
- data/gemfiles/rails81.gemfile +6 -0
- data/lib/activestorage/validator/blob.rb +37 -20
- data/lib/activestorage/validator/version.rb +1 -1
- data/skills/activestorage-validator/SKILL.md +9 -3
- metadata +7 -9
- data/gemfiles/rails61.gemfile +0 -7
- data/gemfiles/rails70.gemfile +0 -7
- data/gemfiles/rails71.gemfile +0 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7dd3596f7f7c5643a6bb22c897877b3d622acf3d525884108b047e02bfb36792
|
|
4
|
+
data.tar.gz: f705a000c6d22a36770bee09629c28c8fd3f5322c7e7ab48194aa8b5a976571d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb4c1f92c407d40b7bc8658f59bb94e5126db2f6418b9667ad45538c8a1cd91251b896db728a422c403a5c04292bb93f8759f6c9db3e606e71c767940f91a6fb
|
|
7
|
+
data.tar.gz: 50763f805cf34602d88bd07b805dcf0321e5f64ab2b50078209d6a201cfa95095cf7009cef688ebf85706eb8105f2669dee43656ba046a7ec86b542c7304f0a6
|
data/.github/workflows/rspec.yml
CHANGED
|
@@ -23,21 +23,15 @@ jobs:
|
|
|
23
23
|
fail-fast: false
|
|
24
24
|
matrix:
|
|
25
25
|
include:
|
|
26
|
-
- { ruby: "3.0", gemfile: "rails61" }
|
|
27
|
-
- { ruby: "3.1", gemfile: "rails61" }
|
|
28
|
-
- { ruby: "3.2", gemfile: "rails61" }
|
|
29
|
-
- { ruby: "3.0", gemfile: "rails70" }
|
|
30
|
-
- { ruby: "3.1", gemfile: "rails70" }
|
|
31
|
-
- { ruby: "3.2", gemfile: "rails70" }
|
|
32
|
-
- { ruby: "3.0", gemfile: "rails71" }
|
|
33
|
-
- { ruby: "3.1", gemfile: "rails71" }
|
|
34
|
-
- { ruby: "3.2", gemfile: "rails71" }
|
|
35
|
-
- { ruby: "3.2", gemfile: "rails72" }
|
|
36
26
|
- { ruby: "3.3", gemfile: "rails72" }
|
|
37
27
|
- { ruby: "3.4", gemfile: "rails72" }
|
|
38
|
-
- { ruby: "
|
|
28
|
+
- { ruby: "4.0", gemfile: "rails72" }
|
|
39
29
|
- { ruby: "3.3", gemfile: "rails80" }
|
|
40
30
|
- { ruby: "3.4", gemfile: "rails80" }
|
|
31
|
+
- { ruby: "4.0", gemfile: "rails80" }
|
|
32
|
+
- { ruby: "3.3", gemfile: "rails81" }
|
|
33
|
+
- { ruby: "3.4", gemfile: "rails81" }
|
|
34
|
+
- { ruby: "4.0", gemfile: "rails81" }
|
|
41
35
|
steps:
|
|
42
36
|
- name: Install packages
|
|
43
37
|
run: |
|
data/.gitignore
CHANGED
data/README.md
CHANGED
|
@@ -55,9 +55,9 @@ end
|
|
|
55
55
|
|
|
56
56
|
| Option | Description |
|
|
57
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. |
|
|
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). Or a Proc/lambda returning one of the above. |
|
|
59
|
+
| size_range | Allowed file size range (e.g. `1..5.megabytes`). Or a Proc/lambda returning a Range. |
|
|
60
|
+
| extension | Allowed file extensions. Accepts a String or an Array of Strings. Case-insensitive, leading dot optional. Files without an extension are rejected. Or a Proc/lambda returning one of the above. |
|
|
61
61
|
|
|
62
62
|
### content_type Examples
|
|
63
63
|
|
|
@@ -82,6 +82,29 @@ end
|
|
|
82
82
|
|
|
83
83
|
The leading dot is optional (`"mp3"` and `".mp3"` behave the same), and matching is case-insensitive (`PHOTO.JPG` matches `extension: "jpg"`).
|
|
84
84
|
|
|
85
|
+
## Dynamic Options (Proc/lambda)
|
|
86
|
+
|
|
87
|
+
Every option (`content_type`, `size_range`, `extension`) also accepts a Proc/lambda,
|
|
88
|
+
so you can decide the allowed values per record at validation time:
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
validates :avatar, blob: {
|
|
92
|
+
content_type: ->(record) { record.premium? ? %w[image/png image/jpeg] : :web_image },
|
|
93
|
+
size_range: ->(record) { 1..(record.premium? ? 20.megabytes : 5.megabytes) }
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The Proc is resolved with the same arity convention as Rails' built-in validators
|
|
98
|
+
(the same rule used by `if:` / `unless:`):
|
|
99
|
+
|
|
100
|
+
- **No argument** (`-> { ... }`) — called as-is.
|
|
101
|
+
- **One argument** (`->(record) { ... }`) — the record is passed in.
|
|
102
|
+
|
|
103
|
+
The Proc must return a value of one of the types the option already accepts
|
|
104
|
+
(a Symbol/Array/Regexp/String for `content_type`, a Range for `size_range`, a
|
|
105
|
+
String/Array for `extension`). For `has_many_attached`, the Proc is evaluated
|
|
106
|
+
once per record (not per attached file).
|
|
107
|
+
|
|
85
108
|
## I18n Error Message Options
|
|
86
109
|
|
|
87
110
|
Validation error messages are I18n compatible. The following interpolation keys are available in your translation files, according to the validator's implementation:
|
|
@@ -23,10 +23,10 @@ Gem::Specification.new do |spec|
|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
24
24
|
spec.require_paths = ["lib"]
|
|
25
25
|
|
|
26
|
-
spec.required_ruby_version = '>= 3.
|
|
26
|
+
spec.required_ruby_version = '>= 3.3.0'
|
|
27
27
|
|
|
28
|
-
spec.add_dependency "rails", ">=
|
|
29
|
-
spec.add_development_dependency "bundler", "
|
|
28
|
+
spec.add_dependency "rails", ">= 7.2.0"
|
|
29
|
+
spec.add_development_dependency "bundler", ">= 2.0"
|
|
30
30
|
spec.add_development_dependency "rake", ">= 12.3.3"
|
|
31
31
|
spec.add_development_dependency "rspec", "~> 3.0"
|
|
32
32
|
spec.add_development_dependency "combustion"
|
data/gemfiles/rails72.gemfile
CHANGED
data/gemfiles/rails80.gemfile
CHANGED
|
@@ -4,25 +4,23 @@ module ActiveRecord
|
|
|
4
4
|
def validate_each(record, attribute, values) # rubocop:disable Metrics/AbcSize
|
|
5
5
|
return unless values.attached?
|
|
6
6
|
|
|
7
|
+
size_range = resolve_option(record, options[:size_range])
|
|
8
|
+
content_type = resolve_option(record, options[:content_type])
|
|
9
|
+
extension = resolve_option(record, options[:extension])
|
|
10
|
+
|
|
7
11
|
Array(values).each do |value|
|
|
8
|
-
if
|
|
9
|
-
if options[:size_range].min > value.blob.byte_size
|
|
10
|
-
record.errors.add(attribute, :min_size_error, min_size: ActiveSupport::NumberHelper.number_to_human_size(options[:size_range].min), filename: value.blob.filename.to_s)
|
|
11
|
-
elsif options[:size_range].max < value.blob.byte_size
|
|
12
|
-
record.errors.add(attribute, :max_size_error, max_size: ActiveSupport::NumberHelper.number_to_human_size(options[:size_range].max), filename: value.blob.filename.to_s)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
12
|
+
validate_size(record, attribute, value, size_range) if size_range.present?
|
|
15
13
|
|
|
16
|
-
unless valid_content_type?(value.blob)
|
|
14
|
+
unless valid_content_type?(value.blob, content_type)
|
|
17
15
|
record.errors.add(attribute, :content_type, filename: value.blob.filename.to_s)
|
|
18
16
|
end
|
|
19
17
|
|
|
20
|
-
unless valid_extension?(value.blob)
|
|
18
|
+
unless valid_extension?(value.blob, extension)
|
|
21
19
|
record.errors.add(
|
|
22
20
|
attribute,
|
|
23
21
|
:extension,
|
|
24
22
|
filename: value.blob.filename.to_s,
|
|
25
|
-
extension: Array(
|
|
23
|
+
extension: Array(extension).map { |e| normalize_extension(e) }.join(', ')
|
|
26
24
|
)
|
|
27
25
|
end
|
|
28
26
|
end
|
|
@@ -30,27 +28,46 @@ module ActiveRecord
|
|
|
30
28
|
|
|
31
29
|
private
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
# Resolve only when the value is a Proc, using the same arity convention
|
|
32
|
+
# as Rails' built-in validators: arity 0 calls the proc as-is, otherwise
|
|
33
|
+
# the record is passed in. Anything else (Symbol/String/Array/Regexp/
|
|
34
|
+
# Range/nil) is returned untouched, preserving backward compatibility.
|
|
35
|
+
def resolve_option(record, value)
|
|
36
|
+
return value unless value.is_a?(Proc)
|
|
37
|
+
|
|
38
|
+
value.arity.zero? ? value.call : value.call(record)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def validate_size(record, attribute, value, size_range)
|
|
42
|
+
byte_size = value.blob.byte_size
|
|
43
|
+
if size_range.min > byte_size
|
|
44
|
+
record.errors.add(attribute, :min_size_error, min_size: ActiveSupport::NumberHelper.number_to_human_size(size_range.min), filename: value.blob.filename.to_s)
|
|
45
|
+
elsif size_range.max < byte_size
|
|
46
|
+
record.errors.add(attribute, :max_size_error, max_size: ActiveSupport::NumberHelper.number_to_human_size(size_range.max), filename: value.blob.filename.to_s)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def valid_content_type?(blob, content_type)
|
|
51
|
+
return true if content_type.nil?
|
|
35
52
|
|
|
36
|
-
case
|
|
53
|
+
case content_type
|
|
37
54
|
when Regexp
|
|
38
|
-
|
|
55
|
+
content_type.match?(blob.content_type)
|
|
39
56
|
when Array
|
|
40
|
-
|
|
57
|
+
content_type.include?(blob.content_type)
|
|
41
58
|
when :web_image
|
|
42
59
|
ActiveStorage.web_image_content_types.include?(blob.content_type)
|
|
43
60
|
when Symbol
|
|
44
|
-
blob.public_send("#{
|
|
61
|
+
blob.public_send("#{content_type}?")
|
|
45
62
|
else
|
|
46
|
-
|
|
63
|
+
content_type == blob.content_type
|
|
47
64
|
end
|
|
48
65
|
end
|
|
49
66
|
|
|
50
|
-
def valid_extension?(blob)
|
|
51
|
-
return true if
|
|
67
|
+
def valid_extension?(blob, extension)
|
|
68
|
+
return true if extension.nil?
|
|
52
69
|
|
|
53
|
-
allowed = Array(
|
|
70
|
+
allowed = Array(extension).map { |e| normalize_extension(e) }
|
|
54
71
|
actual = normalize_extension(blob.filename.extension)
|
|
55
72
|
return false if actual.empty?
|
|
56
73
|
|
|
@@ -54,9 +54,15 @@ end
|
|
|
54
54
|
|
|
55
55
|
| Option | Accepts | Notes |
|
|
56
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). |
|
|
57
|
+
| `content_type` | Symbol / Array / Regexp / String / Proc | Allowed MIME type(s). See matching below. |
|
|
58
|
+
| `size_range` | Range / Proc | Allowed byte size, e.g. `1..(5.megabytes)`. |
|
|
59
|
+
| `extension` | String / Array / Proc | Allowed filename extension(s). |
|
|
60
|
+
|
|
61
|
+
Any option also accepts a **Proc/lambda** returning a value of the listed type,
|
|
62
|
+
resolved with the same arity convention as Rails' built-in validators (`if:` /
|
|
63
|
+
`unless:`): `-> { ... }` is called as-is, `->(record) { ... }` receives the
|
|
64
|
+
record. For `has_many_attached` the Proc is evaluated once per record. Example:
|
|
65
|
+
`content_type: ->(record) { record.premium? ? %w[image/png] : :web_image }`.
|
|
60
66
|
|
|
61
67
|
### `content_type` matching
|
|
62
68
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activestorage-validator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- aki
|
|
@@ -15,26 +15,26 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
18
|
+
version: 7.2.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version:
|
|
25
|
+
version: 7.2.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: bundler
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
|
-
- - "
|
|
30
|
+
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: '2.0'
|
|
33
33
|
type: :development
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '2.0'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
@@ -107,11 +107,9 @@ files:
|
|
|
107
107
|
- config/locales/ja.yml
|
|
108
108
|
- config/locales/pl.yml
|
|
109
109
|
- config/locales/pt-br.yml
|
|
110
|
-
- gemfiles/rails61.gemfile
|
|
111
|
-
- gemfiles/rails70.gemfile
|
|
112
|
-
- gemfiles/rails71.gemfile
|
|
113
110
|
- gemfiles/rails72.gemfile
|
|
114
111
|
- gemfiles/rails80.gemfile
|
|
112
|
+
- gemfiles/rails81.gemfile
|
|
115
113
|
- lib/activestorage/validator.rb
|
|
116
114
|
- lib/activestorage/validator/blob.rb
|
|
117
115
|
- lib/activestorage/validator/version.rb
|
|
@@ -127,7 +125,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
127
125
|
requirements:
|
|
128
126
|
- - ">="
|
|
129
127
|
- !ruby/object:Gem::Version
|
|
130
|
-
version: 3.
|
|
128
|
+
version: 3.3.0
|
|
131
129
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
130
|
requirements:
|
|
133
131
|
- - ">="
|
data/gemfiles/rails61.gemfile
DELETED
data/gemfiles/rails70.gemfile
DELETED