active_storage_validations 3.0.5 → 4.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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +178 -115
- data/Rakefile +13 -15
- data/lib/active_storage_validations/analyzer/audio_analyzer.rb +0 -1
- data/lib/active_storage_validations/analyzer/content_type_analyzer/file.rb +31 -0
- data/lib/active_storage_validations/analyzer/content_type_analyzer/magika.rb +44 -0
- data/lib/active_storage_validations/analyzer/content_type_analyzer.rb +15 -24
- data/lib/active_storage_validations/analyzer/image_analyzer/image_magick.rb +41 -7
- data/lib/active_storage_validations/analyzer/image_analyzer/vips.rb +36 -10
- data/lib/active_storage_validations/analyzer/pdf_analyzer.rb +3 -9
- data/lib/active_storage_validations/analyzer/shared/asv_ff_probable.rb +5 -4
- data/lib/active_storage_validations/analyzer/video_analyzer.rb +0 -1
- data/lib/active_storage_validations/analyzer.rb +35 -3
- data/lib/active_storage_validations/content_type_validator.rb +24 -6
- data/lib/active_storage_validations/dimension_validator.rb +4 -2
- data/lib/active_storage_validations/form_builder.rb +43 -0
- data/lib/active_storage_validations/matchers/aspect_ratio_validator_matcher.rb +8 -0
- data/lib/active_storage_validations/matchers/attached_validator_matcher.rb +4 -0
- data/lib/active_storage_validations/matchers/base_comparison_validator_matcher.rb +11 -1
- data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +12 -0
- data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +8 -0
- data/lib/active_storage_validations/matchers/duration_validator_matcher.rb +8 -0
- data/lib/active_storage_validations/matchers/limit_validator_matcher.rb +4 -0
- data/lib/active_storage_validations/matchers/pages_validator_matcher.rb +8 -0
- data/lib/active_storage_validations/matchers/processable_file_validator_matcher.rb +8 -0
- data/lib/active_storage_validations/matchers/shared/asv_except_onable.rb +57 -0
- data/lib/active_storage_validations/matchers/shared/asv_spoofing_protectable.rb +44 -0
- data/lib/active_storage_validations/matchers/shared/asv_timeoutable.rb +32 -0
- data/lib/active_storage_validations/matchers.rb +9 -1
- data/lib/active_storage_validations/processable_file_validator.rb +60 -1
- data/lib/active_storage_validations/railtie.rb +6 -0
- data/lib/active_storage_validations/shared/asv_analyzable.rb +66 -8
- data/lib/active_storage_validations/shared/asv_attachable.rb +9 -10
- data/lib/active_storage_validations/shared/asv_commandable.rb +95 -0
- data/lib/active_storage_validations/shared/asv_optionable.rb +5 -1
- data/lib/active_storage_validations/version.rb +1 -1
- data/lib/active_storage_validations.rb +22 -0
- metadata +25 -58
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveStorageValidations
|
|
4
|
+
# ActiveStorageValidations::ASVCommandable
|
|
5
|
+
#
|
|
6
|
+
# Runs external analyzer commands with an optional deadline and process-group
|
|
7
|
+
# kill. Used by analyzers that shell out (ffprobe, pdfinfo, file, identify).
|
|
8
|
+
module ASVCommandable
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
CommandResult = Struct.new(:stdout, :stderr, :status, :timed_out, keyword_init: true) do
|
|
12
|
+
def success?
|
|
13
|
+
!timed_out && status.respond_to?(:success?) && status.success?
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
TERM_GRACE_SECONDS = 1
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def run_command(*argv, payload: nil)
|
|
22
|
+
timeout = timeout_in_seconds
|
|
23
|
+
stdout_reader, stdout_writer = IO.pipe
|
|
24
|
+
stderr_reader, stderr_writer = IO.pipe
|
|
25
|
+
|
|
26
|
+
pid = Process.spawn(*argv.map(&:to_s), out: stdout_writer, err: stderr_writer, pgroup: true)
|
|
27
|
+
stdout_writer.close
|
|
28
|
+
stderr_writer.close
|
|
29
|
+
|
|
30
|
+
# Drain pipes concurrently so a chatty child cannot fill the buffer and
|
|
31
|
+
# deadlock while we wait / kill it.
|
|
32
|
+
stdout_thread = Thread.new { stdout_reader.read }
|
|
33
|
+
stderr_thread = Thread.new { stderr_reader.read }
|
|
34
|
+
|
|
35
|
+
status, timed_out = wait_for_command(pid, timeout)
|
|
36
|
+
mark_timed_out!(payload, argv.first) if timed_out
|
|
37
|
+
|
|
38
|
+
CommandResult.new(
|
|
39
|
+
stdout: stdout_thread.value,
|
|
40
|
+
stderr: stderr_thread.value,
|
|
41
|
+
status: status,
|
|
42
|
+
timed_out: timed_out
|
|
43
|
+
)
|
|
44
|
+
ensure
|
|
45
|
+
close_pipe(stdout_reader)
|
|
46
|
+
close_pipe(stderr_reader)
|
|
47
|
+
close_pipe(stdout_writer)
|
|
48
|
+
close_pipe(stderr_writer)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Waits via a reaper thread + Thread#join(timeout) so fast commands return
|
|
52
|
+
# as soon as they exit (no polling sleep). On timeout, TERM then always KILL
|
|
53
|
+
# the process group — KILL is unconditional because the direct child can exit
|
|
54
|
+
# on TERM while a grandchild forked after the group signal is still alive.
|
|
55
|
+
# The same waiter reaps so we never leave zombies.
|
|
56
|
+
def wait_for_command(pid, timeout)
|
|
57
|
+
return [ Process.wait2(pid)[1], false ] if timeout.nil?
|
|
58
|
+
|
|
59
|
+
waiter = Thread.new do
|
|
60
|
+
Process.wait2(pid)
|
|
61
|
+
rescue Errno::ECHILD
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if waiter.join(timeout)
|
|
66
|
+
return [ wait_status_from(waiter), false ]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
signal_process_group(pid, :TERM)
|
|
70
|
+
waiter.join(TERM_GRACE_SECONDS)
|
|
71
|
+
signal_process_group(pid, :KILL)
|
|
72
|
+
waiter.join
|
|
73
|
+
|
|
74
|
+
[ wait_status_from(waiter), true ]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def wait_status_from(waiter)
|
|
78
|
+
waiter.value&.last
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def signal_process_group(pid, signal)
|
|
82
|
+
Process.kill(signal, -pid)
|
|
83
|
+
rescue Errno::ESRCH
|
|
84
|
+
# Already exited; waiter will reap.
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def close_pipe(io)
|
|
88
|
+
io&.close unless io.nil? || io.closed?
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def monotonic_time
|
|
92
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -22,7 +22,11 @@ module ActiveStorageValidations
|
|
|
22
22
|
when Array
|
|
23
23
|
options.map { |option| flatten_options(record, option, available_checks) }
|
|
24
24
|
else
|
|
25
|
-
options.is_a?(Proc)
|
|
25
|
+
if options.is_a?(Proc)
|
|
26
|
+
options.arity == 0 ? options.call : options.call(record)
|
|
27
|
+
else
|
|
28
|
+
options
|
|
29
|
+
end
|
|
26
30
|
end
|
|
27
31
|
end
|
|
28
32
|
end
|
|
@@ -2,8 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
require "active_model"
|
|
4
4
|
require "active_support/concern"
|
|
5
|
+
require "active_support/core_ext/module/attribute_accessors"
|
|
6
|
+
require "active_support/core_ext/numeric/time"
|
|
7
|
+
|
|
8
|
+
module ActiveStorageValidations
|
|
9
|
+
# When true (default), FormBuilder#file_field automatically sets the HTML
|
|
10
|
+
# +accept+ attribute from +content_type+ validators. Can also be overridden
|
|
11
|
+
# per field with +infer_accept:+.
|
|
12
|
+
mattr_accessor :infer_file_field_accept, instance_accessor: false, default: true
|
|
13
|
+
|
|
14
|
+
# Maximum time allowed for external analyzer commands (ffprobe, pdfinfo, file,
|
|
15
|
+
# magika, MiniMagick, libvips). Set to +nil+ to disable. Per-validator +timeout:+
|
|
16
|
+
# overrides this value for the analysis that validator triggers.
|
|
17
|
+
mattr_accessor :command_timeout, instance_accessor: false, default: 10.seconds
|
|
18
|
+
|
|
19
|
+
def self.configure
|
|
20
|
+
yield self
|
|
21
|
+
end
|
|
22
|
+
end
|
|
5
23
|
|
|
6
24
|
require "active_storage_validations/analyzer"
|
|
25
|
+
require "active_storage_validations/analyzer/content_type_analyzer"
|
|
26
|
+
require "active_storage_validations/analyzer/content_type_analyzer/file"
|
|
27
|
+
require "active_storage_validations/analyzer/content_type_analyzer/magika"
|
|
7
28
|
require "active_storage_validations/analyzer/image_analyzer"
|
|
8
29
|
require "active_storage_validations/analyzer/image_analyzer/image_magick"
|
|
9
30
|
require "active_storage_validations/analyzer/image_analyzer/vips"
|
|
@@ -26,5 +47,6 @@ require "active_storage_validations/size_validator"
|
|
|
26
47
|
require "active_storage_validations/total_size_validator"
|
|
27
48
|
require "active_storage_validations/pages_validator"
|
|
28
49
|
|
|
50
|
+
require "active_storage_validations/form_builder"
|
|
29
51
|
require "active_storage_validations/engine"
|
|
30
52
|
require "active_storage_validations/railtie"
|
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:
|
|
4
|
+
version: 4.0.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: 2026-
|
|
11
|
+
date: 2026-08-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activejob
|
|
@@ -16,56 +16,56 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: 7.0.1
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: 7.0.1
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: activemodel
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
33
|
+
version: 7.0.1
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
40
|
+
version: 7.0.1
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: activestorage
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version:
|
|
47
|
+
version: 7.0.1
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version:
|
|
54
|
+
version: 7.0.1
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: activesupport
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
59
|
- - ">="
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version:
|
|
61
|
+
version: 7.0.1
|
|
62
62
|
type: :runtime
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
66
|
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version:
|
|
68
|
+
version: 7.0.1
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: marcel
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -109,73 +109,33 @@ dependencies:
|
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: 4.9.5
|
|
111
111
|
- !ruby/object:Gem::Dependency
|
|
112
|
-
name:
|
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
|
114
|
-
requirements:
|
|
115
|
-
- - "~>"
|
|
116
|
-
- !ruby/object:Gem::Version
|
|
117
|
-
version: '1.4'
|
|
118
|
-
type: :development
|
|
119
|
-
prerelease: false
|
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
-
requirements:
|
|
122
|
-
- - "~>"
|
|
123
|
-
- !ruby/object:Gem::Version
|
|
124
|
-
version: '1.4'
|
|
125
|
-
- !ruby/object:Gem::Dependency
|
|
126
|
-
name: minitest-mock_expectations
|
|
112
|
+
name: pry
|
|
127
113
|
requirement: !ruby/object:Gem::Requirement
|
|
128
114
|
requirements:
|
|
129
|
-
- - "~>"
|
|
130
|
-
- !ruby/object:Gem::Version
|
|
131
|
-
version: '1.2'
|
|
132
115
|
- - ">="
|
|
133
116
|
- !ruby/object:Gem::Version
|
|
134
|
-
version:
|
|
117
|
+
version: '0'
|
|
135
118
|
type: :development
|
|
136
119
|
prerelease: false
|
|
137
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
138
121
|
requirements:
|
|
139
|
-
- - "~>"
|
|
140
|
-
- !ruby/object:Gem::Version
|
|
141
|
-
version: '1.2'
|
|
142
122
|
- - ">="
|
|
143
123
|
- !ruby/object:Gem::Version
|
|
144
|
-
version:
|
|
124
|
+
version: '0'
|
|
145
125
|
- !ruby/object:Gem::Dependency
|
|
146
|
-
name:
|
|
126
|
+
name: rspec-rails
|
|
147
127
|
requirement: !ruby/object:Gem::Requirement
|
|
148
128
|
requirements:
|
|
149
129
|
- - "~>"
|
|
150
130
|
- !ruby/object:Gem::Version
|
|
151
|
-
version: '
|
|
152
|
-
- - ">="
|
|
153
|
-
- !ruby/object:Gem::Version
|
|
154
|
-
version: 1.0.3
|
|
131
|
+
version: '7.0'
|
|
155
132
|
type: :development
|
|
156
133
|
prerelease: false
|
|
157
134
|
version_requirements: !ruby/object:Gem::Requirement
|
|
158
135
|
requirements:
|
|
159
136
|
- - "~>"
|
|
160
137
|
- !ruby/object:Gem::Version
|
|
161
|
-
version: '
|
|
162
|
-
- - ">="
|
|
163
|
-
- !ruby/object:Gem::Version
|
|
164
|
-
version: 1.0.3
|
|
165
|
-
- !ruby/object:Gem::Dependency
|
|
166
|
-
name: pry
|
|
167
|
-
requirement: !ruby/object:Gem::Requirement
|
|
168
|
-
requirements:
|
|
169
|
-
- - ">="
|
|
170
|
-
- !ruby/object:Gem::Version
|
|
171
|
-
version: '0'
|
|
172
|
-
type: :development
|
|
173
|
-
prerelease: false
|
|
174
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
175
|
-
requirements:
|
|
176
|
-
- - ">="
|
|
177
|
-
- !ruby/object:Gem::Version
|
|
178
|
-
version: '0'
|
|
138
|
+
version: '7.0'
|
|
179
139
|
- !ruby/object:Gem::Dependency
|
|
180
140
|
name: ruby-vips
|
|
181
141
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -232,7 +192,7 @@ dependencies:
|
|
|
232
192
|
- - ">="
|
|
233
193
|
- !ruby/object:Gem::Version
|
|
234
194
|
version: '3'
|
|
235
|
-
description: Validations for Active Storage (presence)
|
|
195
|
+
description: Validations for Active Storage (presence, size, content type, ...)
|
|
236
196
|
email:
|
|
237
197
|
- igorkasyanchuk@gmail.com
|
|
238
198
|
executables: []
|
|
@@ -263,6 +223,8 @@ files:
|
|
|
263
223
|
- lib/active_storage_validations/analyzer.rb
|
|
264
224
|
- lib/active_storage_validations/analyzer/audio_analyzer.rb
|
|
265
225
|
- lib/active_storage_validations/analyzer/content_type_analyzer.rb
|
|
226
|
+
- lib/active_storage_validations/analyzer/content_type_analyzer/file.rb
|
|
227
|
+
- lib/active_storage_validations/analyzer/content_type_analyzer/magika.rb
|
|
266
228
|
- lib/active_storage_validations/analyzer/image_analyzer.rb
|
|
267
229
|
- lib/active_storage_validations/analyzer/image_analyzer/image_magick.rb
|
|
268
230
|
- lib/active_storage_validations/analyzer/image_analyzer/vips.rb
|
|
@@ -279,6 +241,7 @@ files:
|
|
|
279
241
|
- lib/active_storage_validations/engine.rb
|
|
280
242
|
- lib/active_storage_validations/extensors/asv_blob_metadatable.rb
|
|
281
243
|
- lib/active_storage_validations/extensors/asv_marcelable.rb
|
|
244
|
+
- lib/active_storage_validations/form_builder.rb
|
|
282
245
|
- lib/active_storage_validations/limit_validator.rb
|
|
283
246
|
- lib/active_storage_validations/matchers.rb
|
|
284
247
|
- lib/active_storage_validations/matchers/aspect_ratio_validator_matcher.rb
|
|
@@ -294,8 +257,11 @@ files:
|
|
|
294
257
|
- lib/active_storage_validations/matchers/shared/asv_allow_blankable.rb
|
|
295
258
|
- lib/active_storage_validations/matchers/shared/asv_attachable.rb
|
|
296
259
|
- lib/active_storage_validations/matchers/shared/asv_contextable.rb
|
|
260
|
+
- lib/active_storage_validations/matchers/shared/asv_except_onable.rb
|
|
297
261
|
- lib/active_storage_validations/matchers/shared/asv_messageable.rb
|
|
298
262
|
- lib/active_storage_validations/matchers/shared/asv_rspecable.rb
|
|
263
|
+
- lib/active_storage_validations/matchers/shared/asv_spoofing_protectable.rb
|
|
264
|
+
- lib/active_storage_validations/matchers/shared/asv_timeoutable.rb
|
|
299
265
|
- lib/active_storage_validations/matchers/shared/asv_validatable.rb
|
|
300
266
|
- lib/active_storage_validations/matchers/size_validator_matcher.rb
|
|
301
267
|
- lib/active_storage_validations/matchers/total_size_validator_matcher.rb
|
|
@@ -305,6 +271,7 @@ files:
|
|
|
305
271
|
- lib/active_storage_validations/shared/asv_active_storageable.rb
|
|
306
272
|
- lib/active_storage_validations/shared/asv_analyzable.rb
|
|
307
273
|
- lib/active_storage_validations/shared/asv_attachable.rb
|
|
274
|
+
- lib/active_storage_validations/shared/asv_commandable.rb
|
|
308
275
|
- lib/active_storage_validations/shared/asv_errorable.rb
|
|
309
276
|
- lib/active_storage_validations/shared/asv_loggable.rb
|
|
310
277
|
- lib/active_storage_validations/shared/asv_optionable.rb
|
|
@@ -328,7 +295,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
328
295
|
requirements:
|
|
329
296
|
- - ">="
|
|
330
297
|
- !ruby/object:Gem::Version
|
|
331
|
-
version:
|
|
298
|
+
version: 3.3.0
|
|
332
299
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
333
300
|
requirements:
|
|
334
301
|
- - ">="
|