active_storage_validations 1.0.4 → 1.1.4
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 +126 -48
- data/config/locales/de.yml +6 -1
- data/config/locales/en.yml +5 -1
- data/config/locales/es.yml +6 -1
- data/config/locales/fr.yml +6 -1
- data/config/locales/it.yml +6 -1
- data/config/locales/ja.yml +6 -1
- data/config/locales/nl.yml +6 -1
- data/config/locales/pl.yml +6 -1
- data/config/locales/pt-BR.yml +6 -1
- data/config/locales/ru.yml +7 -2
- data/config/locales/sv.yml +23 -0
- data/config/locales/tr.yml +6 -1
- data/config/locales/uk.yml +6 -1
- data/config/locales/vi.yml +6 -1
- data/config/locales/zh-CN.yml +6 -1
- data/lib/active_storage_validations/aspect_ratio_validator.rb +57 -27
- data/lib/active_storage_validations/attached_validator.rb +20 -5
- data/lib/active_storage_validations/concerns/errorable.rb +38 -0
- data/lib/active_storage_validations/concerns/symbolizable.rb +12 -0
- data/lib/active_storage_validations/content_type_validator.rb +47 -7
- data/lib/active_storage_validations/dimension_validator.rb +61 -30
- data/lib/active_storage_validations/limit_validator.rb +49 -5
- data/lib/active_storage_validations/matchers/aspect_ratio_validator_matcher.rb +128 -0
- data/lib/active_storage_validations/matchers/attached_validator_matcher.rb +54 -23
- data/lib/active_storage_validations/matchers/concerns/active_storageable.rb +17 -0
- data/lib/active_storage_validations/matchers/concerns/allow_blankable.rb +26 -0
- data/lib/active_storage_validations/matchers/concerns/contextable.rb +35 -0
- data/lib/active_storage_validations/matchers/concerns/messageable.rb +26 -0
- data/lib/active_storage_validations/matchers/concerns/rspecable.rb +25 -0
- data/lib/active_storage_validations/matchers/concerns/validatable.rb +48 -0
- data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +75 -32
- data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +99 -52
- data/lib/active_storage_validations/matchers/size_validator_matcher.rb +96 -31
- data/lib/active_storage_validations/matchers.rb +1 -0
- data/lib/active_storage_validations/metadata.rb +42 -28
- data/lib/active_storage_validations/processable_image_validator.rb +16 -10
- data/lib/active_storage_validations/size_validator.rb +32 -9
- data/lib/active_storage_validations/version.rb +1 -1
- data/lib/active_storage_validations.rb +3 -0
- metadata +29 -4
@@ -1,5 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'concerns/active_storageable.rb'
|
4
|
+
require_relative 'concerns/allow_blankable.rb'
|
5
|
+
require_relative 'concerns/contextable.rb'
|
6
|
+
require_relative 'concerns/messageable.rb'
|
7
|
+
require_relative 'concerns/rspecable.rb'
|
8
|
+
require_relative 'concerns/validatable.rb'
|
9
|
+
|
3
10
|
module ActiveStorageValidations
|
4
11
|
module Matchers
|
5
12
|
def validate_dimensions_of(name)
|
@@ -7,14 +14,35 @@ module ActiveStorageValidations
|
|
7
14
|
end
|
8
15
|
|
9
16
|
class DimensionValidatorMatcher
|
17
|
+
include ActiveStorageable
|
18
|
+
include AllowBlankable
|
19
|
+
include Contextable
|
20
|
+
include Messageable
|
21
|
+
include Rspecable
|
22
|
+
include Validatable
|
23
|
+
|
10
24
|
def initialize(attribute_name)
|
25
|
+
initialize_allow_blankable
|
26
|
+
initialize_contextable
|
27
|
+
initialize_messageable
|
28
|
+
initialize_rspecable
|
11
29
|
@attribute_name = attribute_name
|
12
30
|
@width_min = @width_max = @height_min = @height_max = nil
|
13
|
-
@custom_message = nil
|
14
31
|
end
|
15
32
|
|
16
33
|
def description
|
17
|
-
"validate image dimensions of
|
34
|
+
"validate the image dimensions of :#{@attribute_name}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def failure_message
|
38
|
+
message = ["is expected to validate dimensions of :#{@attribute_name}"]
|
39
|
+
build_failure_message(message)
|
40
|
+
message.join("\n")
|
41
|
+
end
|
42
|
+
|
43
|
+
def width(width)
|
44
|
+
@width_min = @width_max = width
|
45
|
+
self
|
18
46
|
end
|
19
47
|
|
20
48
|
def width_min(width)
|
@@ -27,13 +55,13 @@ module ActiveStorageValidations
|
|
27
55
|
self
|
28
56
|
end
|
29
57
|
|
30
|
-
def
|
31
|
-
@
|
58
|
+
def width_between(range)
|
59
|
+
@width_min, @width_max = range.first, range.last
|
32
60
|
self
|
33
61
|
end
|
34
62
|
|
35
|
-
def
|
36
|
-
@
|
63
|
+
def height(height)
|
64
|
+
@height_min = @height_max = height
|
37
65
|
self
|
38
66
|
end
|
39
67
|
|
@@ -47,42 +75,40 @@ module ActiveStorageValidations
|
|
47
75
|
self
|
48
76
|
end
|
49
77
|
|
50
|
-
def width_between(range)
|
51
|
-
@width_min, @width_max = range.first, range.last
|
52
|
-
self
|
53
|
-
end
|
54
|
-
|
55
78
|
def height_between(range)
|
56
79
|
@height_min, @height_max = range.first, range.last
|
57
80
|
self
|
58
81
|
end
|
59
82
|
|
60
|
-
def height(height)
|
61
|
-
@height_min = @height_max = height
|
62
|
-
self
|
63
|
-
end
|
64
|
-
|
65
83
|
def matches?(subject)
|
66
84
|
@subject = subject.is_a?(Class) ? subject.new : subject
|
67
|
-
responds_to_methods &&
|
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
|
-
end
|
71
85
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
86
|
+
is_a_valid_active_storage_attribute? &&
|
87
|
+
is_context_valid? &&
|
88
|
+
is_allowing_blank? &&
|
89
|
+
is_custom_message_valid? &&
|
90
|
+
width_not_smaller_than_min? &&
|
91
|
+
width_larger_than_min? &&
|
92
|
+
width_smaller_than_max? &&
|
93
|
+
width_not_larger_than_max? &&
|
94
|
+
width_equals? &&
|
95
|
+
height_not_smaller_than_min? &&
|
96
|
+
height_larger_than_min? &&
|
97
|
+
height_smaller_than_max? &&
|
98
|
+
height_not_larger_than_max? &&
|
99
|
+
height_equals?
|
78
100
|
end
|
79
101
|
|
80
102
|
protected
|
81
103
|
|
82
|
-
def
|
83
|
-
@
|
84
|
-
|
85
|
-
|
104
|
+
def build_failure_message(message)
|
105
|
+
return unless @failure_message_artefacts.present?
|
106
|
+
|
107
|
+
message << " but there seem to have issues with the matcher methods you used, since:"
|
108
|
+
@failure_message_artefacts.each do |error_case|
|
109
|
+
message << " validation failed when provided with a #{error_case[:width]}x#{error_case[:height]}px test image"
|
110
|
+
end
|
111
|
+
message << " whereas it should have passed"
|
86
112
|
end
|
87
113
|
|
88
114
|
def valid_width
|
@@ -94,62 +120,83 @@ module ActiveStorageValidations
|
|
94
120
|
end
|
95
121
|
|
96
122
|
def width_not_smaller_than_min?
|
97
|
-
@width_min.nil? || !passes_validation_with_dimensions(@width_min - 1, valid_height
|
123
|
+
@width_min.nil? || !passes_validation_with_dimensions(@width_min - 1, valid_height)
|
98
124
|
end
|
99
125
|
|
100
126
|
def width_larger_than_min?
|
101
|
-
@width_min.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_min + 1, valid_height
|
127
|
+
@width_min.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_min + 1, valid_height)
|
102
128
|
end
|
103
129
|
|
104
130
|
def width_smaller_than_max?
|
105
|
-
@width_max.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_max - 1, valid_height
|
131
|
+
@width_max.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_max - 1, valid_height)
|
106
132
|
end
|
107
133
|
|
108
134
|
def width_not_larger_than_max?
|
109
|
-
@width_max.nil? || !passes_validation_with_dimensions(@width_max + 1, valid_height
|
135
|
+
@width_max.nil? || !passes_validation_with_dimensions(@width_max + 1, valid_height)
|
110
136
|
end
|
111
137
|
|
112
138
|
def width_equals?
|
113
|
-
@width_min.nil? || @width_min != @width_max || passes_validation_with_dimensions(@width_min, valid_height
|
139
|
+
@width_min.nil? || @width_min != @width_max || passes_validation_with_dimensions(@width_min, valid_height)
|
114
140
|
end
|
115
141
|
|
116
142
|
def height_not_smaller_than_min?
|
117
|
-
@height_min.nil? || !passes_validation_with_dimensions(valid_width, @height_min - 1
|
143
|
+
@height_min.nil? || !passes_validation_with_dimensions(valid_width, @height_min - 1)
|
118
144
|
end
|
119
145
|
|
120
146
|
def height_larger_than_min?
|
121
|
-
@height_min.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_min + 1
|
147
|
+
@height_min.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_min + 1)
|
122
148
|
end
|
123
149
|
|
124
150
|
def height_smaller_than_max?
|
125
|
-
@height_max.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_max - 1
|
151
|
+
@height_max.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_max - 1)
|
126
152
|
end
|
127
153
|
|
128
154
|
def height_not_larger_than_max?
|
129
|
-
@height_max.nil? || !passes_validation_with_dimensions(valid_width, @height_max + 1
|
155
|
+
@height_max.nil? || !passes_validation_with_dimensions(valid_width, @height_max + 1)
|
130
156
|
end
|
131
157
|
|
132
158
|
def height_equals?
|
133
|
-
@height_min.nil? || @height_min != @height_max || passes_validation_with_dimensions(valid_width, @height_min
|
159
|
+
@height_min.nil? || @height_min != @height_max || passes_validation_with_dimensions(valid_width, @height_min)
|
134
160
|
end
|
135
161
|
|
136
|
-
def passes_validation_with_dimensions(width, height
|
137
|
-
|
162
|
+
def passes_validation_with_dimensions(width, height)
|
163
|
+
mock_dimensions_for(attach_file, width, height) do
|
164
|
+
validate
|
165
|
+
is_valid? || add_failure_message_artefact(width, height)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def add_failure_message_artefact(width, height)
|
170
|
+
@failure_message_artefacts << { width: width, height: height }
|
171
|
+
false
|
172
|
+
end
|
173
|
+
|
174
|
+
def is_custom_message_valid?
|
175
|
+
return true unless @custom_message
|
138
176
|
|
139
|
-
|
177
|
+
mock_dimensions_for(attach_file, -1, -1) do
|
178
|
+
validate
|
179
|
+
has_an_error_message_which_is_custom_message?
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def mock_dimensions_for(attachment, width, height)
|
140
184
|
Matchers.mock_metadata(attachment, width, height) do
|
141
|
-
|
142
|
-
exclude_error_message = @custom_message || "dimension_#{check}"
|
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
|
185
|
+
yield
|
148
186
|
end
|
149
187
|
end
|
150
188
|
|
151
|
-
def
|
152
|
-
|
189
|
+
def attach_file
|
190
|
+
@subject.public_send(@attribute_name).attach(dummy_file)
|
191
|
+
@subject.public_send(@attribute_name)
|
192
|
+
end
|
193
|
+
|
194
|
+
def dummy_file
|
195
|
+
{
|
196
|
+
io: Tempfile.new('Hello world!'),
|
197
|
+
filename: 'test.png',
|
198
|
+
content_type: 'image/png'
|
199
|
+
}
|
153
200
|
end
|
154
201
|
end
|
155
202
|
end
|
@@ -2,6 +2,14 @@
|
|
2
2
|
|
3
3
|
# Big thank you to the paperclip validation matchers:
|
4
4
|
# https://github.com/thoughtbot/paperclip/blob/v6.1.0/lib/paperclip/matchers/validate_attachment_size_matcher.rb
|
5
|
+
|
6
|
+
require_relative 'concerns/active_storageable.rb'
|
7
|
+
require_relative 'concerns/allow_blankable.rb'
|
8
|
+
require_relative 'concerns/contextable.rb'
|
9
|
+
require_relative 'concerns/messageable.rb'
|
10
|
+
require_relative 'concerns/rspecable.rb'
|
11
|
+
require_relative 'concerns/validatable.rb'
|
12
|
+
|
5
13
|
module ActiveStorageValidations
|
6
14
|
module Matchers
|
7
15
|
def validate_size_of(name)
|
@@ -9,85 +17,142 @@ module ActiveStorageValidations
|
|
9
17
|
end
|
10
18
|
|
11
19
|
class SizeValidatorMatcher
|
20
|
+
include ActiveStorageable
|
21
|
+
include AllowBlankable
|
22
|
+
include Contextable
|
23
|
+
include Messageable
|
24
|
+
include Rspecable
|
25
|
+
include Validatable
|
26
|
+
|
12
27
|
def initialize(attribute_name)
|
28
|
+
initialize_allow_blankable
|
29
|
+
initialize_contextable
|
30
|
+
initialize_messageable
|
31
|
+
initialize_rspecable
|
13
32
|
@attribute_name = attribute_name
|
14
|
-
@
|
33
|
+
@min = @max = nil
|
15
34
|
end
|
16
35
|
|
17
36
|
def description
|
18
|
-
"validate file size of
|
37
|
+
"validate file size of :#{@attribute_name}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def failure_message
|
41
|
+
message = ["is expected to validate file size of :#{@attribute_name}"]
|
42
|
+
build_failure_message(message)
|
43
|
+
message.join("\n")
|
19
44
|
end
|
20
45
|
|
21
46
|
def less_than(size)
|
22
|
-
@
|
47
|
+
@max = size - 1.byte
|
23
48
|
self
|
24
49
|
end
|
25
50
|
|
26
51
|
def less_than_or_equal_to(size)
|
27
|
-
@
|
52
|
+
@max = size
|
28
53
|
self
|
29
54
|
end
|
30
55
|
|
31
56
|
def greater_than(size)
|
32
|
-
@
|
57
|
+
@min = size + 1.byte
|
33
58
|
self
|
34
59
|
end
|
35
60
|
|
36
61
|
def greater_than_or_equal_to(size)
|
37
|
-
@
|
62
|
+
@min = size
|
38
63
|
self
|
39
64
|
end
|
40
65
|
|
41
66
|
def between(range)
|
42
|
-
@
|
67
|
+
@min, @max = range.first, range.last
|
43
68
|
self
|
44
69
|
end
|
45
70
|
|
46
71
|
def matches?(subject)
|
47
72
|
@subject = subject.is_a?(Class) ? subject.new : subject
|
48
|
-
|
73
|
+
|
74
|
+
is_a_valid_active_storage_attribute? &&
|
75
|
+
is_context_valid? &&
|
76
|
+
is_allowing_blank? &&
|
77
|
+
is_custom_message_valid? &&
|
78
|
+
not_lower_than_min? &&
|
79
|
+
higher_than_min? &&
|
80
|
+
lower_than_max? &&
|
81
|
+
not_higher_than_max?
|
49
82
|
end
|
50
83
|
|
51
|
-
|
52
|
-
|
84
|
+
protected
|
85
|
+
|
86
|
+
def build_failure_message(message)
|
87
|
+
return unless @failure_message_artefacts.present?
|
88
|
+
|
89
|
+
message << " but there seem to have issues with the matcher methods you used, since:"
|
90
|
+
@failure_message_artefacts.each do |error_case|
|
91
|
+
message << " validation failed when provided with a #{error_case[:size]} bytes test file"
|
92
|
+
end
|
93
|
+
message << " whereas it should have passed"
|
53
94
|
end
|
54
95
|
|
55
|
-
def
|
56
|
-
|
96
|
+
def not_lower_than_min?
|
97
|
+
@min.nil? || !passes_validation_with_size(@min - 1)
|
57
98
|
end
|
58
99
|
|
59
|
-
|
100
|
+
def higher_than_min?
|
101
|
+
@min.nil? || passes_validation_with_size(@min + 1)
|
102
|
+
end
|
60
103
|
|
61
|
-
def
|
62
|
-
@
|
63
|
-
@subject.public_send(@attribute_name).respond_to?(:attach) &&
|
64
|
-
@subject.public_send(@attribute_name).respond_to?(:detach)
|
104
|
+
def lower_than_max?
|
105
|
+
@max.nil? || @max == Float::INFINITY || passes_validation_with_size(@max - 1)
|
65
106
|
end
|
66
107
|
|
67
|
-
def
|
68
|
-
@
|
108
|
+
def not_higher_than_max?
|
109
|
+
@max.nil? || @max == Float::INFINITY || !passes_validation_with_size(@max + 1)
|
69
110
|
end
|
70
111
|
|
71
|
-
def
|
72
|
-
|
112
|
+
def passes_validation_with_size(size)
|
113
|
+
mock_size_for(io, size) do
|
114
|
+
attach_file
|
115
|
+
validate
|
116
|
+
is_valid? || add_failure_message_artefact(size)
|
117
|
+
end
|
73
118
|
end
|
74
119
|
|
75
|
-
def
|
76
|
-
@
|
120
|
+
def add_failure_message_artefact(size)
|
121
|
+
@failure_message_artefacts << { size: size }
|
122
|
+
false
|
77
123
|
end
|
78
124
|
|
79
|
-
def
|
80
|
-
|
125
|
+
def is_custom_message_valid?
|
126
|
+
return true unless @custom_message
|
127
|
+
|
128
|
+
mock_size_for(io, -1.kilobytes) do
|
129
|
+
attach_file
|
130
|
+
validate
|
131
|
+
has_an_error_message_which_is_custom_message?
|
132
|
+
end
|
81
133
|
end
|
82
134
|
|
83
|
-
def
|
84
|
-
io
|
85
|
-
|
86
|
-
@subject.public_send(@attribute_name).attach(io: io, filename: 'test.png', content_type: 'image/pg')
|
87
|
-
@subject.validate
|
88
|
-
@subject.errors.details[@attribute_name].all? { |error| error[:error] != :file_size_out_of_range }
|
135
|
+
def mock_size_for(io, size)
|
136
|
+
Matchers.stub_method(io, :size, size) do
|
137
|
+
yield
|
89
138
|
end
|
90
139
|
end
|
140
|
+
|
141
|
+
def attach_file
|
142
|
+
@subject.public_send(@attribute_name).attach(dummy_file)
|
143
|
+
end
|
144
|
+
|
145
|
+
def dummy_file
|
146
|
+
{
|
147
|
+
io: io,
|
148
|
+
filename: 'test.png',
|
149
|
+
content_type: 'image/png'
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
def io
|
154
|
+
@io ||= Tempfile.new('Hello world!')
|
155
|
+
end
|
91
156
|
end
|
92
157
|
end
|
93
158
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_storage_validations/matchers/aspect_ratio_validator_matcher'
|
3
4
|
require 'active_storage_validations/matchers/attached_validator_matcher'
|
4
5
|
require 'active_storage_validations/matchers/content_type_validator_matcher'
|
5
6
|
require 'active_storage_validations/matchers/dimension_validator_matcher'
|
@@ -4,29 +4,18 @@ module ActiveStorageValidations
|
|
4
4
|
|
5
5
|
attr_reader :file
|
6
6
|
|
7
|
+
DEFAULT_IMAGE_PROCESSOR = :mini_magick.freeze
|
8
|
+
|
7
9
|
def initialize(file)
|
8
10
|
require_image_processor
|
9
11
|
@file = file
|
10
12
|
end
|
11
13
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
if image_processor == :vips && defined?(Vips)
|
18
|
-
Vips::Error
|
19
|
-
elsif defined?(MiniMagick)
|
20
|
-
MiniMagick::Error
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def require_image_processor
|
25
|
-
if image_processor == :vips
|
26
|
-
require 'vips' unless defined?(Vips)
|
27
|
-
else
|
28
|
-
require 'mini_magick' unless defined?(MiniMagick)
|
29
|
-
end
|
14
|
+
def valid?
|
15
|
+
read_image
|
16
|
+
true
|
17
|
+
rescue InvalidImageError
|
18
|
+
false
|
30
19
|
end
|
31
20
|
|
32
21
|
def metadata
|
@@ -42,14 +31,31 @@ module ActiveStorageValidations
|
|
42
31
|
{}
|
43
32
|
end
|
44
33
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
34
|
+
private
|
35
|
+
|
36
|
+
def image_processor
|
37
|
+
# Rails returns nil for default image processor, because it is set in an after initiliaze callback
|
38
|
+
# https://github.com/rails/rails/blob/89d8569abe2564c8187debf32dd3b4e33d6ad983/activestorage/lib/active_storage/engine.rb
|
39
|
+
Rails.application.config.active_storage.variant_processor || DEFAULT_IMAGE_PROCESSOR
|
50
40
|
end
|
51
41
|
|
52
|
-
|
42
|
+
def require_image_processor
|
43
|
+
case image_processor
|
44
|
+
when :vips then require 'vips' unless defined?(Vips)
|
45
|
+
when :mini_magick then require 'mini_magick' unless defined?(MiniMagick)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def exception_class
|
50
|
+
case image_processor
|
51
|
+
when :vips then Vips::Error
|
52
|
+
when :mini_magick then MiniMagick::Error
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def vips_image_processor?
|
57
|
+
image_processor == :vips
|
58
|
+
end
|
53
59
|
|
54
60
|
def read_image
|
55
61
|
is_string = file.is_a?(String)
|
@@ -95,8 +101,16 @@ module ActiveStorageValidations
|
|
95
101
|
end
|
96
102
|
|
97
103
|
def new_image_from_path(path)
|
98
|
-
if
|
99
|
-
|
104
|
+
if vips_image_processor? && (Vips::get_suffixes.include?(File.extname(path).downcase) || !Vips::respond_to?(:vips_foreign_get_suffixes))
|
105
|
+
begin
|
106
|
+
Vips::Image.new_from_file(path)
|
107
|
+
rescue exception_class
|
108
|
+
# We handle cases where an error is raised when reading the file
|
109
|
+
# because Vips can throw errors rather than returning false
|
110
|
+
# We stumble upon this issue while reading 0 byte size file
|
111
|
+
# https://github.com/janko/image_processing/issues/97
|
112
|
+
false
|
113
|
+
end
|
100
114
|
elsif defined?(MiniMagick)
|
101
115
|
MiniMagick::Image.new(path)
|
102
116
|
end
|
@@ -105,13 +119,13 @@ module ActiveStorageValidations
|
|
105
119
|
def valid_image?(image)
|
106
120
|
return false unless image
|
107
121
|
|
108
|
-
|
122
|
+
vips_image_processor? && image.is_a?(Vips::Image) ? image.avg : image.valid?
|
109
123
|
rescue exception_class
|
110
124
|
false
|
111
125
|
end
|
112
126
|
|
113
127
|
def rotated_image?(image)
|
114
|
-
if
|
128
|
+
if vips_image_processor? && image.is_a?(Vips::Image)
|
115
129
|
image.get('exif-ifd0-Orientation').include?('Right-top') ||
|
116
130
|
image.get('exif-ifd0-Orientation').include?('Left-bottom')
|
117
131
|
else
|
@@ -1,10 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'concerns/errorable.rb'
|
4
|
+
require_relative 'concerns/symbolizable.rb'
|
3
5
|
require_relative 'metadata.rb'
|
4
6
|
|
5
7
|
module ActiveStorageValidations
|
6
8
|
class ProcessableImageValidator < ActiveModel::EachValidator # :nodoc
|
7
9
|
include OptionProcUnfolding
|
10
|
+
include Errorable
|
11
|
+
include Symbolizable
|
12
|
+
|
13
|
+
ERROR_TYPES = %i[
|
14
|
+
image_not_processable
|
15
|
+
].freeze
|
8
16
|
|
9
17
|
if Rails.gem_version >= Gem::Version.new('6.0.0')
|
10
18
|
def validate_each(record, attribute, _value)
|
@@ -16,7 +24,10 @@ module ActiveStorageValidations
|
|
16
24
|
files = Array.wrap(changes.is_a?(ActiveStorage::Attached::Changes::CreateMany) ? changes.attachables : changes.attachable)
|
17
25
|
|
18
26
|
files.each do |file|
|
19
|
-
|
27
|
+
if !Metadata.new(file).valid?
|
28
|
+
errors_options = initialize_error_options(options, file)
|
29
|
+
add_error(record, attribute, ERROR_TYPES.first , **errors_options) unless Metadata.new(file).valid?
|
30
|
+
end
|
20
31
|
end
|
21
32
|
end
|
22
33
|
else
|
@@ -27,17 +38,12 @@ module ActiveStorageValidations
|
|
27
38
|
files = Array.wrap(record.send(attribute))
|
28
39
|
|
29
40
|
files.each do |file|
|
30
|
-
|
41
|
+
if !Metadata.new(file).valid?
|
42
|
+
errors_options = initialize_error_options(options, file)
|
43
|
+
add_error(record, attribute, ERROR_TYPES.first , **errors_options) unless Metadata.new(file).valid?
|
44
|
+
end
|
31
45
|
end
|
32
46
|
end
|
33
47
|
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
48
|
end
|
43
49
|
end
|
@@ -1,16 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'concerns/errorable.rb'
|
4
|
+
require_relative 'concerns/symbolizable.rb'
|
5
|
+
|
3
6
|
module ActiveStorageValidations
|
4
7
|
class SizeValidator < ActiveModel::EachValidator # :nodoc:
|
5
8
|
include OptionProcUnfolding
|
9
|
+
include Errorable
|
10
|
+
include Symbolizable
|
6
11
|
|
7
12
|
delegate :number_to_human_size, to: ActiveSupport::NumberHelper
|
8
13
|
|
9
|
-
AVAILABLE_CHECKS = %i[
|
14
|
+
AVAILABLE_CHECKS = %i[
|
15
|
+
less_than
|
16
|
+
less_than_or_equal_to
|
17
|
+
greater_than
|
18
|
+
greater_than_or_equal_to
|
19
|
+
between
|
20
|
+
].freeze
|
21
|
+
ERROR_TYPES = %i[
|
22
|
+
file_size_not_less_than
|
23
|
+
file_size_not_less_than_or_equal_to
|
24
|
+
file_size_not_greater_than
|
25
|
+
file_size_not_greater_than_or_equal_to
|
26
|
+
file_size_not_between
|
27
|
+
].freeze
|
10
28
|
|
11
29
|
def check_validity!
|
12
|
-
|
13
|
-
|
30
|
+
unless AVAILABLE_CHECKS.one? { |argument| options.key?(argument) }
|
31
|
+
raise ArgumentError, 'You must pass either :less_than(_or_equal_to), :greater_than(_or_equal_to), or :between to the validator'
|
32
|
+
end
|
14
33
|
end
|
15
34
|
|
16
35
|
def validate_each(record, attribute, _value)
|
@@ -18,24 +37,28 @@ module ActiveStorageValidations
|
|
18
37
|
return true unless record.send(attribute).attached?
|
19
38
|
|
20
39
|
files = Array.wrap(record.send(attribute))
|
21
|
-
|
22
|
-
errors_options = {}
|
23
|
-
errors_options[:message] = options[:message] if options[:message].present?
|
24
40
|
flat_options = unfold_procs(record, self.options, AVAILABLE_CHECKS)
|
25
41
|
|
26
42
|
files.each do |file|
|
27
|
-
next if
|
43
|
+
next if is_valid?(file.blob.byte_size, flat_options)
|
28
44
|
|
45
|
+
errors_options = initialize_error_options(options, file)
|
29
46
|
errors_options[:file_size] = number_to_human_size(file.blob.byte_size)
|
30
47
|
errors_options[:min_size] = number_to_human_size(min_size(flat_options))
|
31
48
|
errors_options[:max_size] = number_to_human_size(max_size(flat_options))
|
49
|
+
keys = AVAILABLE_CHECKS & flat_options.keys
|
50
|
+
error_type = "file_size_not_#{keys.first}".to_sym
|
32
51
|
|
33
|
-
record
|
52
|
+
add_error(record, attribute, error_type, **errors_options)
|
34
53
|
break
|
35
54
|
end
|
36
55
|
end
|
37
56
|
|
38
|
-
|
57
|
+
private
|
58
|
+
|
59
|
+
def is_valid?(file_size, flat_options)
|
60
|
+
return false if file_size < 0
|
61
|
+
|
39
62
|
if flat_options[:between].present?
|
40
63
|
flat_options[:between].include?(file_size)
|
41
64
|
elsif flat_options[:less_than].present?
|