jr-paperclip 8.0.1 → 8.0.3
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/NEWS +9 -0
- data/lib/paperclip/thumbnail.rb +18 -15
- data/lib/paperclip/version.rb +1 -1
- metadata +3 -245
- data/.github/FUNDING.yml +0 -3
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -18
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- data/.github/workflows/reviewdog.yml +0 -23
- data/.github/workflows/tests.yml +0 -56
- data/.gitignore +0 -19
- data/.qlty/.gitignore +0 -7
- data/.qlty/qlty.toml +0 -89
- data/Appraisals +0 -29
- data/Gemfile +0 -18
- data/bin/console +0 -11
- data/features/basic_integration.feature +0 -112
- data/features/migration.feature +0 -29
- data/features/rake_tasks.feature +0 -62
- data/features/step_definitions/attachment_steps.rb +0 -138
- data/features/step_definitions/html_steps.rb +0 -15
- data/features/step_definitions/rails_steps.rb +0 -271
- data/features/step_definitions/s3_steps.rb +0 -16
- data/features/step_definitions/web_steps.rb +0 -106
- data/features/support/env.rb +0 -12
- data/features/support/file_helpers.rb +0 -34
- data/features/support/fixtures/boot_config.txt +0 -15
- data/features/support/fixtures/gemfile.txt +0 -5
- data/features/support/fixtures/preinitializer.txt +0 -20
- data/features/support/paths.rb +0 -28
- data/features/support/rails.rb +0 -39
- data/features/support/selectors.rb +0 -19
- data/features/support/webmock_setup.rb +0 -8
- data/gemfiles/7.0.gemfile +0 -21
- data/gemfiles/7.1.gemfile +0 -21
- data/gemfiles/7.2.gemfile +0 -21
- data/gemfiles/8.0.gemfile +0 -21
- data/gemfiles/8.1.gemfile +0 -21
- data/paperclip.gemspec +0 -52
- data/spec/database.yml +0 -4
- data/spec/paperclip/attachment_definitions_spec.rb +0 -313
- data/spec/paperclip/attachment_processing_spec.rb +0 -79
- data/spec/paperclip/attachment_registry_spec.rb +0 -158
- data/spec/paperclip/attachment_spec.rb +0 -1617
- data/spec/paperclip/content_type_detector_spec.rb +0 -58
- data/spec/paperclip/file_command_content_type_detector_spec.rb +0 -40
- data/spec/paperclip/filename_cleaner_spec.rb +0 -13
- data/spec/paperclip/geometry_detector_spec.rb +0 -96
- data/spec/paperclip/geometry_parser_spec.rb +0 -73
- data/spec/paperclip/geometry_spec.rb +0 -270
- data/spec/paperclip/glue_spec.rb +0 -63
- data/spec/paperclip/has_attached_file_spec.rb +0 -78
- data/spec/paperclip/helpers_spec.rb +0 -49
- data/spec/paperclip/integration_spec.rb +0 -702
- data/spec/paperclip/interpolations_spec.rb +0 -270
- data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +0 -160
- data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +0 -167
- data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +0 -88
- data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +0 -17
- data/spec/paperclip/io_adapters/file_adapter_spec.rb +0 -134
- data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +0 -142
- data/spec/paperclip/io_adapters/identity_adapter_spec.rb +0 -8
- data/spec/paperclip/io_adapters/nil_adapter_spec.rb +0 -25
- data/spec/paperclip/io_adapters/registry_spec.rb +0 -35
- data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +0 -64
- data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +0 -146
- data/spec/paperclip/io_adapters/uri_adapter_spec.rb +0 -231
- data/spec/paperclip/lazy_thumbnail_compatibility_spec.rb +0 -266
- data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +0 -19
- data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +0 -108
- data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +0 -69
- data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +0 -88
- data/spec/paperclip/media_type_spoof_detector_spec.rb +0 -126
- data/spec/paperclip/meta_class_spec.rb +0 -30
- data/spec/paperclip/migration_guide_example_spec.rb +0 -44
- data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +0 -88
- data/spec/paperclip/paperclip_spec.rb +0 -196
- data/spec/paperclip/plural_cache_spec.rb +0 -37
- data/spec/paperclip/processor_helpers_spec.rb +0 -57
- data/spec/paperclip/processor_spec.rb +0 -60
- data/spec/paperclip/rails_environment_spec.rb +0 -30
- data/spec/paperclip/rake_spec.rb +0 -103
- data/spec/paperclip/schema_spec.rb +0 -298
- data/spec/paperclip/storage/filesystem_spec.rb +0 -102
- data/spec/paperclip/storage/fog_spec.rb +0 -606
- data/spec/paperclip/storage/s3_live_spec.rb +0 -188
- data/spec/paperclip/storage/s3_spec.rb +0 -1974
- data/spec/paperclip/style_spec.rb +0 -309
- data/spec/paperclip/tempfile_factory_spec.rb +0 -33
- data/spec/paperclip/tempfile_spec.rb +0 -35
- data/spec/paperclip/thumbnail_custom_options_spec.rb +0 -225
- data/spec/paperclip/thumbnail_loader_options_spec.rb +0 -53
- data/spec/paperclip/thumbnail_security_spec.rb +0 -42
- data/spec/paperclip/thumbnail_spec.rb +0 -1460
- data/spec/paperclip/url_generator_spec.rb +0 -231
- data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +0 -410
- data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +0 -249
- data/spec/paperclip/validators/attachment_presence_validator_spec.rb +0 -85
- data/spec/paperclip/validators/attachment_size_validator_spec.rb +0 -325
- data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +0 -48
- data/spec/paperclip/validators_spec.rb +0 -179
- data/spec/spec_helper.rb +0 -52
- data/spec/support/assertions.rb +0 -84
- data/spec/support/fake_model.rb +0 -24
- data/spec/support/fake_rails.rb +0 -12
- data/spec/support/fixtures/12k.png +0 -0
- data/spec/support/fixtures/50x50.png +0 -0
- data/spec/support/fixtures/5k.png +0 -0
- data/spec/support/fixtures/animated +0 -0
- data/spec/support/fixtures/animated.gif +0 -0
- data/spec/support/fixtures/animated.unknown +0 -0
- data/spec/support/fixtures/aws_s3.yml +0 -13
- data/spec/support/fixtures/bad.png +0 -1
- data/spec/support/fixtures/empty.html +0 -1
- data/spec/support/fixtures/empty.xlsx +0 -0
- data/spec/support/fixtures/fog.yml +0 -8
- data/spec/support/fixtures/rotated.jpg +0 -0
- data/spec/support/fixtures/s3.yml +0 -8
- data/spec/support/fixtures/sample.xlsm +0 -0
- data/spec/support/fixtures/spaced file.jpg +0 -0
- data/spec/support/fixtures/spaced file.png +0 -0
- data/spec/support/fixtures/text.txt +0 -1
- data/spec/support/fixtures/twopage.pdf +0 -0
- data/spec/support/fixtures/uppercase.PNG +0 -0
- data/spec/support/matchers/accept.rb +0 -5
- data/spec/support/matchers/exist.rb +0 -5
- data/spec/support/matchers/have_column.rb +0 -23
- data/spec/support/mock_attachment.rb +0 -24
- data/spec/support/mock_interpolator.rb +0 -24
- data/spec/support/mock_url_generator_builder.rb +0 -26
- data/spec/support/model_reconstruction.rb +0 -72
- data/spec/support/reporting.rb +0 -11
- data/spec/support/test_data.rb +0 -13
- data/spec/support/version_helper.rb +0 -9
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
|
|
3
|
-
describe Paperclip::UriAdapter do
|
|
4
|
-
let(:content_type) { "image/png" }
|
|
5
|
-
let(:meta) { {} }
|
|
6
|
-
|
|
7
|
-
before do
|
|
8
|
-
@open_return = StringIO.new("xxx")
|
|
9
|
-
allow(@open_return).to receive(:content_type).and_return(content_type)
|
|
10
|
-
allow(@open_return).to receive(:meta).and_return(meta)
|
|
11
|
-
Paperclip::UriAdapter.register
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
after do
|
|
15
|
-
Paperclip.io_adapters.unregister(described_class)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
context "a new instance" do
|
|
19
|
-
let(:meta) { { "content-type" => "image/png" } }
|
|
20
|
-
|
|
21
|
-
before do
|
|
22
|
-
allow_any_instance_of(Paperclip::UriAdapter).
|
|
23
|
-
to receive(:download_content).and_return(@open_return)
|
|
24
|
-
|
|
25
|
-
@uri = URI.parse("http://thoughtbot.com/images/thoughtbot-logo.png")
|
|
26
|
-
@subject = Paperclip.io_adapters.for(@uri, hash_digest: Digest::MD5)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
it "returns a file name" do
|
|
30
|
-
assert_equal "thoughtbot-logo.png", @subject.original_filename
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it "closes open handle after reading" do
|
|
34
|
-
assert_equal true, @open_return.closed?
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
it "returns a content type" do
|
|
38
|
-
assert_equal "image/png", @subject.content_type
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
it "returns the size of the data" do
|
|
42
|
-
assert_equal @open_return.size, @subject.size
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
it "generates an MD5 hash of the contents" do
|
|
46
|
-
assert_equal Digest::MD5.hexdigest("xxx"), @subject.fingerprint
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
it "generates correct fingerprint after read" do
|
|
50
|
-
fingerprint = Digest::MD5.hexdigest(@subject.read)
|
|
51
|
-
assert_equal fingerprint, @subject.fingerprint
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
it "generates same fingerprint" do
|
|
55
|
-
assert_equal @subject.fingerprint, @subject.fingerprint
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
it "returns the data contained in the StringIO" do
|
|
59
|
-
assert_equal "xxx", @subject.read
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it "accepts a content_type" do
|
|
63
|
-
@subject.content_type = "image/png"
|
|
64
|
-
assert_equal "image/png", @subject.content_type
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
it "accepts an original_filename" do
|
|
68
|
-
@subject.original_filename = "image.png"
|
|
69
|
-
assert_equal "image.png", @subject.original_filename
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
context "a directory index url" do
|
|
74
|
-
let(:content_type) { "text/html" }
|
|
75
|
-
let(:meta) { { "content-type" => "text/html" } }
|
|
76
|
-
|
|
77
|
-
before do
|
|
78
|
-
allow_any_instance_of(Paperclip::UriAdapter).
|
|
79
|
-
to receive(:download_content).and_return(@open_return)
|
|
80
|
-
|
|
81
|
-
@uri = URI.parse("http://thoughtbot.com")
|
|
82
|
-
@subject = Paperclip.io_adapters.for(@uri)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
it "returns a file name" do
|
|
86
|
-
assert_equal "index.html", @subject.original_filename
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
it "returns a content type" do
|
|
90
|
-
assert_equal "text/html", @subject.content_type
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
context "a url with query params" do
|
|
95
|
-
before do
|
|
96
|
-
allow_any_instance_of(Paperclip::UriAdapter).
|
|
97
|
-
to receive(:download_content).and_return(@open_return)
|
|
98
|
-
|
|
99
|
-
@uri = URI.parse("https://github.com/thoughtbot/paperclip?file=test")
|
|
100
|
-
@subject = Paperclip.io_adapters.for(@uri)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
it "returns a file name" do
|
|
104
|
-
assert_equal "paperclip", @subject.original_filename
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
context "a url with content disposition headers" do
|
|
109
|
-
let(:file_name) { "test_document.pdf" }
|
|
110
|
-
let(:filename_from_path) { "paperclip" }
|
|
111
|
-
|
|
112
|
-
before do
|
|
113
|
-
allow_any_instance_of(Paperclip::UriAdapter).
|
|
114
|
-
to receive(:download_content).and_return(@open_return)
|
|
115
|
-
|
|
116
|
-
@uri = URI.parse(
|
|
117
|
-
"https://github.com/thoughtbot/#{filename_from_path}?file=test"
|
|
118
|
-
)
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
it "returns file name from path" do
|
|
122
|
-
meta["content-disposition"] = "inline;"
|
|
123
|
-
|
|
124
|
-
@subject = Paperclip.io_adapters.for(@uri)
|
|
125
|
-
|
|
126
|
-
assert_equal filename_from_path, @subject.original_filename
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
it "returns a file name enclosed in double quotes" do
|
|
130
|
-
file_name = "john's test document.pdf"
|
|
131
|
-
meta["content-disposition"] = "attachment; filename=\"#{file_name}\";"
|
|
132
|
-
|
|
133
|
-
@subject = Paperclip.io_adapters.for(@uri)
|
|
134
|
-
|
|
135
|
-
assert_equal file_name, @subject.original_filename
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
it "returns a file name not enclosed in double quotes" do
|
|
139
|
-
meta["content-disposition"] = "ATTACHMENT; FILENAME=#{file_name};"
|
|
140
|
-
|
|
141
|
-
@subject = Paperclip.io_adapters.for(@uri)
|
|
142
|
-
|
|
143
|
-
assert_equal file_name, @subject.original_filename
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
it "does not crash when an empty filename is given" do
|
|
147
|
-
meta["content-disposition"] = "ATTACHMENT; FILENAME=\"\";"
|
|
148
|
-
|
|
149
|
-
@subject = Paperclip.io_adapters.for(@uri)
|
|
150
|
-
|
|
151
|
-
assert_equal "", @subject.original_filename
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
it "returns a file name ignoring RFC 5987 encoding" do
|
|
155
|
-
meta["content-disposition"] =
|
|
156
|
-
"attachment; filename=#{file_name}; filename* = utf-8''%e2%82%ac%20rates"
|
|
157
|
-
|
|
158
|
-
@subject = Paperclip.io_adapters.for(@uri)
|
|
159
|
-
|
|
160
|
-
assert_equal file_name, @subject.original_filename
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
context "when file name has consecutive periods" do
|
|
164
|
-
let(:file_name) { "test_document..pdf" }
|
|
165
|
-
|
|
166
|
-
it "returns a file name" do
|
|
167
|
-
@uri = URI.parse(
|
|
168
|
-
"https://github.com/thoughtbot/#{file_name}?file=test"
|
|
169
|
-
)
|
|
170
|
-
@subject = Paperclip.io_adapters.for(@uri)
|
|
171
|
-
assert_equal file_name, @subject.original_filename
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
context "a url with restricted characters in the filename" do
|
|
177
|
-
before do
|
|
178
|
-
allow_any_instance_of(Paperclip::UriAdapter).
|
|
179
|
-
to receive(:download_content).and_return(@open_return)
|
|
180
|
-
|
|
181
|
-
@uri = URI.parse("https://github.com/thoughtbot/paper:clip.jpg")
|
|
182
|
-
@subject = Paperclip.io_adapters.for(@uri)
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
it "does not generate filenames that include restricted characters" do
|
|
186
|
-
assert_equal "paper_clip.jpg", @subject.original_filename
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
it "does not generate paths that include restricted characters" do
|
|
190
|
-
expect(@subject.path).to_not match(/:/)
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
describe "#download_content" do
|
|
195
|
-
before do
|
|
196
|
-
allowed_mock =
|
|
197
|
-
if RUBY_VERSION < '2.5'
|
|
198
|
-
allow_any_instance_of(Paperclip::UriAdapter)
|
|
199
|
-
else
|
|
200
|
-
allow(URI)
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
allowed_mock.to receive(:open).and_return(@open_return)
|
|
204
|
-
|
|
205
|
-
@uri = URI.parse("https://github.com/thoughtbot/paper:clip.jpg")
|
|
206
|
-
@subject = Paperclip.io_adapters.for(@uri)
|
|
207
|
-
@uri_opener = RUBY_VERSION < '2.5' ? @subject : URI
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
after do
|
|
211
|
-
@subject.send(:download_content)
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
context "with default read_timeout" do
|
|
215
|
-
it "calls open without options" do
|
|
216
|
-
expect(@uri_opener).to receive(:open).with(@uri, {}).at_least(1).times
|
|
217
|
-
end
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
context "with custom read_timeout" do
|
|
221
|
-
before do
|
|
222
|
-
Paperclip.options[:read_timeout] = 120
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
it "calls open with read_timeout option" do
|
|
226
|
-
expect(@uri_opener)
|
|
227
|
-
.to receive(:open).with(@uri, { read_timeout: 120 }).at_least(1).times
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
end
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
|
|
3
|
-
# This spec tests compatibility with Mastodon's LazyThumbnail pattern.
|
|
4
|
-
# LazyThumbnail is a subclass of Paperclip::Thumbnail that conditionally
|
|
5
|
-
# skips processing when the image doesn't need transformation.
|
|
6
|
-
# See: https://github.com/mastodon/mastodon/blob/main/lib/paperclip/lazy_thumbnail.rb
|
|
7
|
-
|
|
8
|
-
module Paperclip
|
|
9
|
-
class LazyThumbnail < Paperclip::Thumbnail
|
|
10
|
-
def make
|
|
11
|
-
return File.open(@file.path) unless needs_convert?
|
|
12
|
-
|
|
13
|
-
if options[:geometry]
|
|
14
|
-
min_side = [@current_geometry.width, @current_geometry.height].min.to_i
|
|
15
|
-
options[:geometry] = "#{min_side}x#{min_side}#" if @target_geometry.square? && min_side < @target_geometry.width
|
|
16
|
-
elsif options[:pixels]
|
|
17
|
-
width = Math.sqrt(options[:pixels] * (@current_geometry.width.to_f / @current_geometry.height)).round.to_i
|
|
18
|
-
height = Math.sqrt(options[:pixels] * (@current_geometry.height.to_f / @current_geometry.width)).round.to_i
|
|
19
|
-
options[:geometry] = "#{width}x#{height}>"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
Paperclip::Thumbnail.make(file, options, attachment)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
private
|
|
26
|
-
|
|
27
|
-
def needs_convert?
|
|
28
|
-
needs_different_geometry? || needs_different_format? || needs_metadata_stripping?
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def needs_different_geometry?
|
|
32
|
-
(options[:geometry] && @current_geometry.width != @target_geometry.width && @current_geometry.height != @target_geometry.height) ||
|
|
33
|
-
(options[:pixels] && @current_geometry.width * @current_geometry.height > options[:pixels])
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def needs_different_format?
|
|
37
|
-
@format.present? && @current_format != @format
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def needs_metadata_stripping?
|
|
41
|
-
@attachment.respond_to?(:instance) && @attachment.instance.respond_to?(:local?) && @attachment.instance.local?
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
describe Paperclip::LazyThumbnail do
|
|
47
|
-
let(:file) { File.new(fixture_file("5k.png"), "rb") }
|
|
48
|
-
let(:rotated_file) { File.new(fixture_file("rotated.jpg"), "rb") }
|
|
49
|
-
let(:attachment) { double("Attachment", options: {}, instance: double(local?: false)) }
|
|
50
|
-
|
|
51
|
-
after do
|
|
52
|
-
file.close
|
|
53
|
-
rotated_file.close rescue nil
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
describe "basic compatibility" do
|
|
57
|
-
it "inherits from Paperclip::Thumbnail" do
|
|
58
|
-
expect(Paperclip::LazyThumbnail.superclass).to eq(Paperclip::Thumbnail)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
it "can access current_geometry" do
|
|
62
|
-
processor = described_class.new(file, { geometry: "100x100" }, attachment)
|
|
63
|
-
expect(processor.current_geometry).to be_a(Paperclip::Geometry)
|
|
64
|
-
expect(processor.current_geometry.width).to be > 0
|
|
65
|
-
expect(processor.current_geometry.height).to be > 0
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
it "can access target_geometry" do
|
|
69
|
-
processor = described_class.new(file, { geometry: "100x100#" }, attachment)
|
|
70
|
-
expect(processor.target_geometry).to be_a(Paperclip::Geometry)
|
|
71
|
-
expect(processor.target_geometry.width).to eq(100)
|
|
72
|
-
expect(processor.target_geometry.height).to eq(100)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
it "target_geometry responds to square?" do
|
|
76
|
-
processor = described_class.new(file, { geometry: "100x100#" }, attachment)
|
|
77
|
-
expect(processor.target_geometry).to respond_to(:square?)
|
|
78
|
-
expect(processor.target_geometry.square?).to be true
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
it "can access format and current_format" do
|
|
82
|
-
processor = described_class.new(file, { geometry: "100x100", format: :jpg }, attachment)
|
|
83
|
-
expect(processor.format).to eq(:jpg)
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
describe "with ImageMagick backend" do
|
|
88
|
-
let(:attachment) { double("Attachment", options: { backend: :image_magick }, instance: double(local?: false)) }
|
|
89
|
-
|
|
90
|
-
it "processes images when geometry change is needed" do
|
|
91
|
-
processor = described_class.new(file, { geometry: "50x50#" }, attachment)
|
|
92
|
-
result = processor.make
|
|
93
|
-
|
|
94
|
-
expect(File.exist?(result.path)).to be true
|
|
95
|
-
dimensions = `identify -format "%wx%h" "#{result.path}"`.strip
|
|
96
|
-
expect(dimensions).to eq("50x50")
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
it "processes images when format change is needed" do
|
|
100
|
-
processor = described_class.new(file, { geometry: "100x100", format: :jpg }, attachment)
|
|
101
|
-
result = processor.make
|
|
102
|
-
|
|
103
|
-
expect(File.exist?(result.path)).to be true
|
|
104
|
-
expect(result.path).to end_with(".jpg")
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
it "skips processing when no changes needed" do
|
|
108
|
-
# Create a processor where geometry matches source
|
|
109
|
-
processor = described_class.new(file, { geometry: "434x66" }, attachment)
|
|
110
|
-
|
|
111
|
-
# Since the source is 434x66 (5k.png dimensions), and target is same,
|
|
112
|
-
# needs_different_geometry? returns false
|
|
113
|
-
result = processor.make
|
|
114
|
-
|
|
115
|
-
# Result should be the original file (File.open(@file.path))
|
|
116
|
-
expect(File.exist?(result.path)).to be true
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
it "handles square geometry optimization" do
|
|
120
|
-
# Source is 434x66, min_side is 66
|
|
121
|
-
# Requesting 100x100# square, but min_side (66) < target width (100)
|
|
122
|
-
# So geometry should be adjusted to "66x66#"
|
|
123
|
-
processor = described_class.new(file, { geometry: "100x100#" }, attachment)
|
|
124
|
-
result = processor.make
|
|
125
|
-
|
|
126
|
-
expect(File.exist?(result.path)).to be true
|
|
127
|
-
dimensions = `identify -format "%wx%h" "#{result.path}"`.strip
|
|
128
|
-
expect(dimensions).to eq("66x66")
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
describe "with libvips backend" do
|
|
133
|
-
let(:attachment) { double("Attachment", options: { backend: :vips }, instance: double(local?: false)) }
|
|
134
|
-
|
|
135
|
-
before do
|
|
136
|
-
begin
|
|
137
|
-
require "vips"
|
|
138
|
-
rescue LoadError
|
|
139
|
-
skip "libvips not installed"
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
it "processes images when geometry change is needed" do
|
|
144
|
-
processor = described_class.new(file, { geometry: "50x50#" }, attachment)
|
|
145
|
-
result = processor.make
|
|
146
|
-
|
|
147
|
-
expect(File.exist?(result.path)).to be true
|
|
148
|
-
dimensions = `identify -format "%wx%h" "#{result.path}"`.strip
|
|
149
|
-
expect(dimensions).to eq("50x50")
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
it "processes images when format change is needed" do
|
|
153
|
-
processor = described_class.new(file, { geometry: "100x100", format: :jpg }, attachment)
|
|
154
|
-
result = processor.make
|
|
155
|
-
|
|
156
|
-
expect(File.exist?(result.path)).to be true
|
|
157
|
-
expect(result.path).to end_with(".jpg")
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
it "skips processing when no changes needed" do
|
|
161
|
-
processor = described_class.new(file, { geometry: "434x66" }, attachment)
|
|
162
|
-
result = processor.make
|
|
163
|
-
expect(File.exist?(result.path)).to be true
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
it "handles square geometry optimization" do
|
|
167
|
-
processor = described_class.new(file, { geometry: "100x100#" }, attachment)
|
|
168
|
-
result = processor.make
|
|
169
|
-
|
|
170
|
-
expect(File.exist?(result.path)).to be true
|
|
171
|
-
dimensions = `identify -format "%wx%h" "#{result.path}"`.strip
|
|
172
|
-
expect(dimensions).to eq("66x66")
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
describe "pixels-based resizing (Mastodon-specific)" do
|
|
177
|
-
let(:attachment) { double("Attachment", options: {}, instance: double(local?: false)) }
|
|
178
|
-
|
|
179
|
-
context "with ImageMagick backend" do
|
|
180
|
-
let(:attachment) { double("Attachment", options: { backend: :image_magick }, instance: double(local?: false)) }
|
|
181
|
-
|
|
182
|
-
it "resizes based on maximum pixel count" do
|
|
183
|
-
# Source is 434x66 = 28,644 pixels
|
|
184
|
-
# Request max 10,000 pixels
|
|
185
|
-
processor = described_class.new(file, { pixels: 10_000 }, attachment)
|
|
186
|
-
result = processor.make
|
|
187
|
-
|
|
188
|
-
expect(File.exist?(result.path)).to be true
|
|
189
|
-
dimensions = `identify -format "%wx%h" "#{result.path}"`.strip
|
|
190
|
-
width, height = dimensions.split("x").map(&:to_i)
|
|
191
|
-
expect(width * height).to be <= 10_000
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
it "skips processing when image is already small enough" do
|
|
195
|
-
# Source is 434x66 = 28,644 pixels
|
|
196
|
-
# Request max 50,000 pixels - no resize needed
|
|
197
|
-
processor = described_class.new(file, { pixels: 50_000 }, attachment)
|
|
198
|
-
result = processor.make
|
|
199
|
-
|
|
200
|
-
expect(File.exist?(result.path)).to be true
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
context "with libvips backend" do
|
|
205
|
-
let(:attachment) { double("Attachment", options: { backend: :vips }, instance: double(local?: false)) }
|
|
206
|
-
|
|
207
|
-
before do
|
|
208
|
-
begin
|
|
209
|
-
require "vips"
|
|
210
|
-
rescue LoadError
|
|
211
|
-
skip "libvips not installed"
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
it "resizes based on maximum pixel count" do
|
|
216
|
-
processor = described_class.new(file, { pixels: 10_000 }, attachment)
|
|
217
|
-
result = processor.make
|
|
218
|
-
|
|
219
|
-
expect(File.exist?(result.path)).to be true
|
|
220
|
-
dimensions = `identify -format "%wx%h" "#{result.path}"`.strip
|
|
221
|
-
width, height = dimensions.split("x").map(&:to_i)
|
|
222
|
-
expect(width * height).to be <= 10_000
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
describe "metadata stripping trigger" do
|
|
228
|
-
it "processes when attachment instance is local" do
|
|
229
|
-
local_instance = double(local?: true)
|
|
230
|
-
local_attachment = double("Attachment", options: {}, instance: local_instance)
|
|
231
|
-
|
|
232
|
-
processor = described_class.new(file, { geometry: "434x66" }, local_attachment)
|
|
233
|
-
|
|
234
|
-
# Even with matching geometry, should process because local? is true
|
|
235
|
-
# This is verified by the fact that make doesn't just return the original file
|
|
236
|
-
result = processor.make
|
|
237
|
-
expect(File.exist?(result.path)).to be true
|
|
238
|
-
end
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
describe "Thumbnail.make class method" do
|
|
242
|
-
it "is available and works correctly" do
|
|
243
|
-
expect(Paperclip::Thumbnail).to respond_to(:make)
|
|
244
|
-
|
|
245
|
-
result = Paperclip::Thumbnail.make(file, { geometry: "50x50#" }, attachment)
|
|
246
|
-
expect(File.exist?(result.path)).to be true
|
|
247
|
-
|
|
248
|
-
dimensions = `identify -format "%wx%h" "#{result.path}"`.strip
|
|
249
|
-
expect(dimensions).to eq("50x50")
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
it "respects backend option" do
|
|
253
|
-
begin
|
|
254
|
-
require "vips"
|
|
255
|
-
rescue LoadError
|
|
256
|
-
skip "libvips not installed"
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
result = Paperclip::Thumbnail.make(file, { geometry: "50x50#", backend: :vips }, attachment)
|
|
260
|
-
expect(File.exist?(result.path)).to be true
|
|
261
|
-
|
|
262
|
-
dimensions = `identify -format "%wx%h" "#{result.path}"`.strip
|
|
263
|
-
expect(dimensions).to eq("50x50")
|
|
264
|
-
end
|
|
265
|
-
end
|
|
266
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
require "paperclip/matchers"
|
|
3
|
-
|
|
4
|
-
describe Paperclip::Shoulda::Matchers::HaveAttachedFileMatcher do
|
|
5
|
-
extend Paperclip::Shoulda::Matchers
|
|
6
|
-
|
|
7
|
-
it "rejects the dummy class if it has no attachment" do
|
|
8
|
-
reset_table "dummies"
|
|
9
|
-
reset_class "Dummy"
|
|
10
|
-
matcher = self.class.have_attached_file(:avatar)
|
|
11
|
-
expect(matcher).to_not accept(Dummy)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
it "accepts the dummy class if it has an attachment" do
|
|
15
|
-
rebuild_model
|
|
16
|
-
matcher = self.class.have_attached_file(:avatar)
|
|
17
|
-
expect(matcher).to accept(Dummy)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
require "paperclip/matchers"
|
|
3
|
-
|
|
4
|
-
describe Paperclip::Shoulda::Matchers::ValidateAttachmentContentTypeMatcher do
|
|
5
|
-
extend Paperclip::Shoulda::Matchers
|
|
6
|
-
|
|
7
|
-
before do
|
|
8
|
-
reset_table("dummies") do |d|
|
|
9
|
-
d.string :title
|
|
10
|
-
d.string :avatar_file_name
|
|
11
|
-
d.string :avatar_content_type
|
|
12
|
-
end
|
|
13
|
-
reset_class "Dummy"
|
|
14
|
-
Dummy.do_not_validate_attachment_file_type :avatar
|
|
15
|
-
Dummy.has_attached_file :avatar
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it "rejects a class with no validation" do
|
|
19
|
-
expect(matcher).to_not accept(Dummy)
|
|
20
|
-
expect { matcher.failure_message }.to_not raise_error
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it "rejects a class when the validation fails" do
|
|
24
|
-
Dummy.validates_attachment_content_type :avatar, content_type: %r{audio/.*}
|
|
25
|
-
expect(matcher).to_not accept(Dummy)
|
|
26
|
-
expect { matcher.failure_message }.to_not raise_error
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
it "accepts a class with a matching validation" do
|
|
30
|
-
Dummy.validates_attachment_content_type :avatar, content_type: %r{image/.*}
|
|
31
|
-
expect(matcher).to accept(Dummy)
|
|
32
|
-
expect { matcher.failure_message }.to_not raise_error
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it "accepts a class with other validations but matching types" do
|
|
36
|
-
Dummy.validates_presence_of :title
|
|
37
|
-
Dummy.validates_attachment_content_type :avatar, content_type: %r{image/.*}
|
|
38
|
-
expect(matcher).to accept(Dummy)
|
|
39
|
-
expect { matcher.failure_message }.to_not raise_error
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it "accepts a class that matches and a matcher that only specifies 'allowing'" do
|
|
43
|
-
Dummy.validates_attachment_content_type :avatar, content_type: %r{image/.*}
|
|
44
|
-
matcher = plain_matcher.allowing(%w(image/png image/jpeg))
|
|
45
|
-
|
|
46
|
-
expect(matcher).to accept(Dummy)
|
|
47
|
-
expect { matcher.failure_message }.to_not raise_error
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it "rejects a class that does not match and a matcher that only specifies 'allowing'" do
|
|
51
|
-
Dummy.validates_attachment_content_type :avatar, content_type: %r{audio/.*}
|
|
52
|
-
matcher = plain_matcher.allowing(%w(image/png image/jpeg))
|
|
53
|
-
|
|
54
|
-
expect(matcher).to_not accept(Dummy)
|
|
55
|
-
expect { matcher.failure_message }.to_not raise_error
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
it "accepts a class that matches and a matcher that only specifies 'rejecting'" do
|
|
59
|
-
Dummy.validates_attachment_content_type :avatar, content_type: %r{image/.*}
|
|
60
|
-
matcher = plain_matcher.rejecting(%w(audio/mp3 application/octet-stream))
|
|
61
|
-
|
|
62
|
-
expect(matcher).to accept(Dummy)
|
|
63
|
-
expect { matcher.failure_message }.to_not raise_error
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
it "rejects a class that does not match and a matcher that only specifies 'rejecting'" do
|
|
67
|
-
Dummy.validates_attachment_content_type :avatar, content_type: %r{audio/.*}
|
|
68
|
-
matcher = plain_matcher.rejecting(%w(audio/mp3 application/octet-stream))
|
|
69
|
-
|
|
70
|
-
expect(matcher).to_not accept(Dummy)
|
|
71
|
-
expect { matcher.failure_message }.to_not raise_error
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
context "using an :if to control the validation" do
|
|
75
|
-
before do
|
|
76
|
-
Dummy.class_eval do
|
|
77
|
-
validates_attachment_content_type :avatar, content_type: %r{image/*}, if: :go
|
|
78
|
-
attr_accessor :go
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
it "runs the validation if the control is true" do
|
|
83
|
-
dummy = Dummy.new
|
|
84
|
-
dummy.go = true
|
|
85
|
-
expect(matcher).to accept(dummy)
|
|
86
|
-
expect { matcher.failure_message }.to_not raise_error
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
it "does not run the validation if the control is false" do
|
|
90
|
-
dummy = Dummy.new
|
|
91
|
-
dummy.go = false
|
|
92
|
-
expect(matcher).to_not accept(dummy)
|
|
93
|
-
expect { matcher.failure_message }.to_not raise_error
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
private
|
|
98
|
-
|
|
99
|
-
def plain_matcher
|
|
100
|
-
self.class.validate_attachment_content_type(:avatar)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def matcher
|
|
104
|
-
plain_matcher.
|
|
105
|
-
allowing(%w(image/png image/jpeg)).
|
|
106
|
-
rejecting(%w(audio/mp3 application/octet-stream))
|
|
107
|
-
end
|
|
108
|
-
end
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
require "paperclip/matchers"
|
|
3
|
-
|
|
4
|
-
describe Paperclip::Shoulda::Matchers::ValidateAttachmentPresenceMatcher do
|
|
5
|
-
extend Paperclip::Shoulda::Matchers
|
|
6
|
-
|
|
7
|
-
before do
|
|
8
|
-
reset_table("dummies") do |d|
|
|
9
|
-
d.string :avatar_file_name
|
|
10
|
-
end
|
|
11
|
-
reset_class "Dummy"
|
|
12
|
-
Dummy.has_attached_file :avatar
|
|
13
|
-
Dummy.do_not_validate_attachment_file_type :avatar
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
it "rejects a class with no validation" do
|
|
17
|
-
expect(matcher).to_not accept(Dummy)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
it "accepts a class with a matching validation" do
|
|
21
|
-
Dummy.validates_attachment_presence :avatar
|
|
22
|
-
expect(matcher).to accept(Dummy)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
it "accepts an instance with other attachment validations" do
|
|
26
|
-
reset_table("dummies") do |d|
|
|
27
|
-
d.string :avatar_file_name
|
|
28
|
-
d.string :avatar_content_type
|
|
29
|
-
end
|
|
30
|
-
Dummy.class_eval do
|
|
31
|
-
validates_attachment_presence :avatar
|
|
32
|
-
validates_attachment_content_type :avatar, content_type: "image/gif"
|
|
33
|
-
end
|
|
34
|
-
dummy = Dummy.new
|
|
35
|
-
|
|
36
|
-
dummy.avatar = File.new fixture_file("5k.png")
|
|
37
|
-
|
|
38
|
-
expect(matcher).to accept(dummy)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
context "using an :if to control the validation" do
|
|
42
|
-
before do
|
|
43
|
-
Dummy.class_eval do
|
|
44
|
-
validates_attachment_presence :avatar, if: :go
|
|
45
|
-
attr_accessor :go
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
it "runs the validation if the control is true" do
|
|
50
|
-
dummy = Dummy.new
|
|
51
|
-
dummy.avatar = nil
|
|
52
|
-
dummy.go = true
|
|
53
|
-
expect(matcher).to accept(dummy)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
it "does not run the validation if the control is false" do
|
|
57
|
-
dummy = Dummy.new
|
|
58
|
-
dummy.avatar = nil
|
|
59
|
-
dummy.go = false
|
|
60
|
-
expect(matcher).to_not accept(dummy)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
private
|
|
65
|
-
|
|
66
|
-
def matcher
|
|
67
|
-
self.class.validate_attachment_presence(:avatar)
|
|
68
|
-
end
|
|
69
|
-
end
|