paperclip-fix 4.3.7
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 +7 -0
- data/.gitignore +19 -0
- data/.hound.yml +1066 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +22 -0
- data/Appraisals +11 -0
- data/CONTRIBUTING.md +75 -0
- data/Gemfile +21 -0
- data/LICENSE +24 -0
- data/NEWS +420 -0
- data/README.md +979 -0
- data/RELEASING.md +17 -0
- data/Rakefile +44 -0
- data/UPGRADING +14 -0
- data/cucumber/paperclip_steps.rb +6 -0
- data/features/basic_integration.feature +80 -0
- data/features/migration.feature +94 -0
- data/features/rake_tasks.feature +62 -0
- data/features/step_definitions/attachment_steps.rb +110 -0
- data/features/step_definitions/html_steps.rb +15 -0
- data/features/step_definitions/rails_steps.rb +236 -0
- data/features/step_definitions/s3_steps.rb +14 -0
- data/features/step_definitions/web_steps.rb +107 -0
- data/features/support/env.rb +11 -0
- data/features/support/fakeweb.rb +13 -0
- data/features/support/file_helpers.rb +34 -0
- data/features/support/fixtures/boot_config.txt +15 -0
- data/features/support/fixtures/gemfile.txt +5 -0
- data/features/support/fixtures/preinitializer.txt +20 -0
- data/features/support/paths.rb +28 -0
- data/features/support/rails.rb +63 -0
- data/features/support/selectors.rb +19 -0
- data/gemfiles/3.2.gemfile +19 -0
- data/gemfiles/4.1.gemfile +19 -0
- data/gemfiles/4.2.gemfile +19 -0
- data/lib/generators/paperclip/USAGE +8 -0
- data/lib/generators/paperclip/paperclip_generator.rb +30 -0
- data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +15 -0
- data/lib/paperclip/attachment.rb +608 -0
- data/lib/paperclip/attachment_registry.rb +59 -0
- data/lib/paperclip/callbacks.rb +40 -0
- data/lib/paperclip/content_type_detector.rb +79 -0
- data/lib/paperclip/deprecations.rb +42 -0
- data/lib/paperclip/errors.rb +32 -0
- data/lib/paperclip/file_command_content_type_detector.rb +30 -0
- data/lib/paperclip/filename_cleaner.rb +16 -0
- data/lib/paperclip/geometry.rb +158 -0
- data/lib/paperclip/geometry_detector_factory.rb +48 -0
- data/lib/paperclip/geometry_parser_factory.rb +31 -0
- data/lib/paperclip/glue.rb +17 -0
- data/lib/paperclip/has_attached_file.rb +109 -0
- data/lib/paperclip/helpers.rb +56 -0
- data/lib/paperclip/interpolations/plural_cache.rb +18 -0
- data/lib/paperclip/interpolations.rb +197 -0
- data/lib/paperclip/io_adapters/abstract_adapter.rb +47 -0
- data/lib/paperclip/io_adapters/attachment_adapter.rb +36 -0
- data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
- data/lib/paperclip/io_adapters/empty_string_adapter.rb +18 -0
- data/lib/paperclip/io_adapters/file_adapter.rb +22 -0
- data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +15 -0
- data/lib/paperclip/io_adapters/identity_adapter.rb +12 -0
- data/lib/paperclip/io_adapters/nil_adapter.rb +34 -0
- data/lib/paperclip/io_adapters/registry.rb +32 -0
- data/lib/paperclip/io_adapters/stringio_adapter.rb +33 -0
- data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +42 -0
- data/lib/paperclip/io_adapters/uri_adapter.rb +63 -0
- data/lib/paperclip/locales/de.yml +18 -0
- data/lib/paperclip/locales/en.yml +18 -0
- data/lib/paperclip/locales/es.yml +18 -0
- data/lib/paperclip/locales/ja.yml +18 -0
- data/lib/paperclip/locales/pt-BR.yml +18 -0
- data/lib/paperclip/locales/zh-CN.yml +18 -0
- data/lib/paperclip/locales/zh-HK.yml +18 -0
- data/lib/paperclip/locales/zh-TW.yml +18 -0
- data/lib/paperclip/logger.rb +21 -0
- data/lib/paperclip/matchers/have_attached_file_matcher.rb +54 -0
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +100 -0
- data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +59 -0
- data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +96 -0
- data/lib/paperclip/matchers.rb +64 -0
- data/lib/paperclip/media_type_spoof_detector.rb +89 -0
- data/lib/paperclip/missing_attachment_styles.rb +79 -0
- data/lib/paperclip/processor.rb +48 -0
- data/lib/paperclip/processor_helpers.rb +50 -0
- data/lib/paperclip/rails_environment.rb +25 -0
- data/lib/paperclip/railtie.rb +31 -0
- data/lib/paperclip/schema.rb +83 -0
- data/lib/paperclip/storage/filesystem.rb +90 -0
- data/lib/paperclip/storage/fog.rb +241 -0
- data/lib/paperclip/storage/s3.rb +440 -0
- data/lib/paperclip/storage.rb +3 -0
- data/lib/paperclip/style.rb +109 -0
- data/lib/paperclip/tempfile.rb +43 -0
- data/lib/paperclip/tempfile_factory.rb +23 -0
- data/lib/paperclip/thumbnail.rb +121 -0
- data/lib/paperclip/url_generator.rb +72 -0
- data/lib/paperclip/validators/attachment_content_type_validator.rb +88 -0
- data/lib/paperclip/validators/attachment_file_name_validator.rb +80 -0
- data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +29 -0
- data/lib/paperclip/validators/attachment_presence_validator.rb +30 -0
- data/lib/paperclip/validators/attachment_size_validator.rb +115 -0
- data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +27 -0
- data/lib/paperclip/validators.rb +74 -0
- data/lib/paperclip/version.rb +3 -0
- data/lib/paperclip.rb +213 -0
- data/lib/tasks/paperclip.rake +127 -0
- data/paperclip.gemspec +51 -0
- data/shoulda_macros/paperclip.rb +134 -0
- data/spec/database.yml +4 -0
- data/spec/paperclip/attachment_definitions_spec.rb +13 -0
- data/spec/paperclip/attachment_processing_spec.rb +82 -0
- data/spec/paperclip/attachment_registry_spec.rb +130 -0
- data/spec/paperclip/attachment_spec.rb +1494 -0
- data/spec/paperclip/content_type_detector_spec.rb +48 -0
- data/spec/paperclip/deprecations_spec.rb +65 -0
- data/spec/paperclip/file_command_content_type_detector_spec.rb +26 -0
- data/spec/paperclip/filename_cleaner_spec.rb +14 -0
- data/spec/paperclip/geometry_detector_spec.rb +39 -0
- data/spec/paperclip/geometry_parser_spec.rb +73 -0
- data/spec/paperclip/geometry_spec.rb +255 -0
- data/spec/paperclip/glue_spec.rb +44 -0
- data/spec/paperclip/has_attached_file_spec.rb +142 -0
- data/spec/paperclip/integration_spec.rb +667 -0
- data/spec/paperclip/interpolations_spec.rb +262 -0
- data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +78 -0
- data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +139 -0
- data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +83 -0
- data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
- data/spec/paperclip/io_adapters/file_adapter_spec.rb +131 -0
- data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +104 -0
- data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
- data/spec/paperclip/io_adapters/nil_adapter_spec.rb +25 -0
- data/spec/paperclip/io_adapters/registry_spec.rb +35 -0
- data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +64 -0
- data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +146 -0
- data/spec/paperclip/io_adapters/uri_adapter_spec.rb +127 -0
- data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
- data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +99 -0
- data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
- data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
- data/spec/paperclip/media_type_spoof_detector_spec.rb +79 -0
- data/spec/paperclip/meta_class_spec.rb +30 -0
- data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +84 -0
- data/spec/paperclip/paperclip_spec.rb +222 -0
- data/spec/paperclip/plural_cache_spec.rb +37 -0
- data/spec/paperclip/processor_helpers_spec.rb +57 -0
- data/spec/paperclip/processor_spec.rb +26 -0
- data/spec/paperclip/rails_environment_spec.rb +33 -0
- data/spec/paperclip/rake_spec.rb +103 -0
- data/spec/paperclip/schema_spec.rb +248 -0
- data/spec/paperclip/storage/filesystem_spec.rb +79 -0
- data/spec/paperclip/storage/fog_spec.rb +535 -0
- data/spec/paperclip/storage/s3_live_spec.rb +182 -0
- data/spec/paperclip/storage/s3_spec.rb +1526 -0
- data/spec/paperclip/style_spec.rb +255 -0
- data/spec/paperclip/tempfile_factory_spec.rb +33 -0
- data/spec/paperclip/thumbnail_spec.rb +500 -0
- data/spec/paperclip/url_generator_spec.rb +211 -0
- data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
- data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +160 -0
- data/spec/paperclip/validators/attachment_presence_validator_spec.rb +85 -0
- data/spec/paperclip/validators/attachment_size_validator_spec.rb +229 -0
- data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +52 -0
- data/spec/paperclip/validators_spec.rb +164 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/support/assertions.rb +71 -0
- data/spec/support/deprecations.rb +9 -0
- data/spec/support/fake_model.rb +25 -0
- data/spec/support/fake_rails.rb +12 -0
- 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/bad.png +1 -0
- data/spec/support/fixtures/empty.html +1 -0
- data/spec/support/fixtures/empty.xlsx +0 -0
- data/spec/support/fixtures/fog.yml +8 -0
- data/spec/support/fixtures/rotated.jpg +0 -0
- data/spec/support/fixtures/s3.yml +8 -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 +1 -0
- data/spec/support/fixtures/twopage.pdf +0 -0
- data/spec/support/fixtures/uppercase.PNG +0 -0
- data/spec/support/matchers/accept.rb +5 -0
- data/spec/support/matchers/exist.rb +5 -0
- data/spec/support/matchers/have_column.rb +23 -0
- data/spec/support/mock_attachment.rb +22 -0
- data/spec/support/mock_interpolator.rb +24 -0
- data/spec/support/mock_url_generator_builder.rb +27 -0
- data/spec/support/model_reconstruction.rb +60 -0
- data/spec/support/rails_helpers.rb +7 -0
- data/spec/support/test_data.rb +13 -0
- data/spec/support/version_helper.rb +9 -0
- metadata +606 -0
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
require 'open-uri'
|
|
4
|
+
|
|
5
|
+
describe 'Paperclip' do
|
|
6
|
+
context "Many models at once" do
|
|
7
|
+
before do
|
|
8
|
+
rebuild_model
|
|
9
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
|
10
|
+
300.times do |i|
|
|
11
|
+
Dummy.create! avatar: @file
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
after { @file.close }
|
|
16
|
+
|
|
17
|
+
it "does not exceed the open file limit" do
|
|
18
|
+
assert_nothing_raised do
|
|
19
|
+
Dummy.all.each { |dummy| dummy.avatar }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
context "An attachment" do
|
|
25
|
+
before do
|
|
26
|
+
rebuild_model styles: { thumb: "50x50#" }
|
|
27
|
+
@dummy = Dummy.new
|
|
28
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
|
29
|
+
@dummy.avatar = @file
|
|
30
|
+
assert @dummy.save
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
after { @file.close }
|
|
34
|
+
|
|
35
|
+
it "creates its thumbnails properly" do
|
|
36
|
+
assert_match(/\b50x50\b/, `identify "#{@dummy.avatar.path(:thumb)}"`)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context 'reprocessing with unreadable original' do
|
|
40
|
+
before { File.chmod(0000, @dummy.avatar.path) }
|
|
41
|
+
|
|
42
|
+
it "does not raise an error" do
|
|
43
|
+
assert_nothing_raised do
|
|
44
|
+
silence_stream(STDERR) do
|
|
45
|
+
@dummy.avatar.reprocess!
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "returns false" do
|
|
51
|
+
silence_stream(STDERR) do
|
|
52
|
+
assert !@dummy.avatar.reprocess!
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
after { File.chmod(0644, @dummy.avatar.path) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context "redefining its attachment styles" do
|
|
60
|
+
before do
|
|
61
|
+
Dummy.class_eval do
|
|
62
|
+
has_attached_file :avatar, styles: { thumb: "150x25#", dynamic: lambda { |a| '50x50#' } }
|
|
63
|
+
end
|
|
64
|
+
@d2 = Dummy.find(@dummy.id)
|
|
65
|
+
@original_timestamp = @d2.avatar_updated_at
|
|
66
|
+
@d2.avatar.reprocess!
|
|
67
|
+
@d2.save
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "creates its thumbnails properly" do
|
|
71
|
+
assert_match(/\b150x25\b/, `identify "#{@dummy.avatar.path(:thumb)}"`)
|
|
72
|
+
assert_match(/\b50x50\b/, `identify "#{@dummy.avatar.path(:dynamic)}"`)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "changes the timestamp" do
|
|
76
|
+
assert_not_equal @original_timestamp, @d2.avatar_updated_at
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
context "Attachment" do
|
|
82
|
+
before do
|
|
83
|
+
@thumb_path = "tmp/public/system/dummies/avatars/000/000/001/thumb/5k.png"
|
|
84
|
+
File.delete(@thumb_path) if File.exist?(@thumb_path)
|
|
85
|
+
rebuild_model styles: { thumb: "50x50#" }
|
|
86
|
+
@dummy = Dummy.new
|
|
87
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
after { @file.close }
|
|
92
|
+
|
|
93
|
+
it "does not create the thumbnails upon saving when post-processing is disabled" do
|
|
94
|
+
@dummy.avatar.post_processing = false
|
|
95
|
+
@dummy.avatar = @file
|
|
96
|
+
assert @dummy.save
|
|
97
|
+
assert_file_not_exists @thumb_path
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "creates the thumbnails upon saving when post_processing is enabled" do
|
|
101
|
+
@dummy.avatar.post_processing = true
|
|
102
|
+
@dummy.avatar = @file
|
|
103
|
+
assert @dummy.save
|
|
104
|
+
assert_file_exists @thumb_path
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
context "Attachment with no generated thumbnails" do
|
|
109
|
+
before do
|
|
110
|
+
@thumb_small_path = "tmp/public/system/dummies/avatars/000/000/001/thumb_small/5k.png"
|
|
111
|
+
@thumb_large_path = "tmp/public/system/dummies/avatars/000/000/001/thumb_large/5k.png"
|
|
112
|
+
File.delete(@thumb_small_path) if File.exist?(@thumb_small_path)
|
|
113
|
+
File.delete(@thumb_large_path) if File.exist?(@thumb_large_path)
|
|
114
|
+
rebuild_model styles: { thumb_small: "50x50#", thumb_large: "60x60#" }
|
|
115
|
+
@dummy = Dummy.new
|
|
116
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
|
117
|
+
|
|
118
|
+
@dummy.avatar.post_processing = false
|
|
119
|
+
@dummy.avatar = @file
|
|
120
|
+
assert @dummy.save
|
|
121
|
+
@dummy.avatar.post_processing = true
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
after { @file.close }
|
|
125
|
+
|
|
126
|
+
it "allows us to create all thumbnails in one go" do
|
|
127
|
+
assert_file_not_exists(@thumb_small_path)
|
|
128
|
+
assert_file_not_exists(@thumb_large_path)
|
|
129
|
+
|
|
130
|
+
@dummy.avatar.reprocess!
|
|
131
|
+
|
|
132
|
+
assert_file_exists(@thumb_small_path)
|
|
133
|
+
assert_file_exists(@thumb_large_path)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "allows us to selectively create each thumbnail" do
|
|
137
|
+
assert_file_not_exists(@thumb_small_path)
|
|
138
|
+
assert_file_not_exists(@thumb_large_path)
|
|
139
|
+
|
|
140
|
+
@dummy.avatar.reprocess! :thumb_small
|
|
141
|
+
assert_file_exists(@thumb_small_path)
|
|
142
|
+
assert_file_not_exists(@thumb_large_path)
|
|
143
|
+
|
|
144
|
+
@dummy.avatar.reprocess! :thumb_large
|
|
145
|
+
assert_file_exists(@thumb_large_path)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
context "A model that modifies its original" do
|
|
150
|
+
before do
|
|
151
|
+
rebuild_model styles: { original: "2x2#" }
|
|
152
|
+
@dummy = Dummy.new
|
|
153
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
|
154
|
+
@dummy.avatar = @file
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "reports the file size of the processed file and not the original" do
|
|
158
|
+
assert_not_equal File.size(@file.path), @dummy.avatar.size
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
after { @file.close }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
context "A model with attachments scoped under an id" do
|
|
165
|
+
before do
|
|
166
|
+
rebuild_model styles: { large: "100x100",
|
|
167
|
+
medium: "50x50" },
|
|
168
|
+
path: ":rails_root/tmp/:id/:attachments/:style.:extension"
|
|
169
|
+
@dummy = Dummy.new
|
|
170
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
|
171
|
+
@dummy.avatar = @file
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
after { @file.close }
|
|
175
|
+
|
|
176
|
+
context "when saved" do
|
|
177
|
+
before do
|
|
178
|
+
@dummy.save
|
|
179
|
+
@saved_path = @dummy.avatar.path(:large)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it "has a large file in the right place" do
|
|
183
|
+
assert_file_exists(@dummy.avatar.path(:large))
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
context "and deleted" do
|
|
187
|
+
before do
|
|
188
|
+
@dummy.avatar.clear
|
|
189
|
+
@dummy.save
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it "does not have a large file in the right place anymore" do
|
|
193
|
+
assert_file_not_exists(@saved_path)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it "does not have its next two parent directories" do
|
|
197
|
+
assert_file_not_exists(File.dirname(@saved_path))
|
|
198
|
+
assert_file_not_exists(File.dirname(File.dirname(@saved_path)))
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
context 'and deleted where the delete fails' do
|
|
203
|
+
it "does not die if an unexpected SystemCallError happens" do
|
|
204
|
+
FileUtils.stubs(:rmdir).raises(Errno::EPIPE)
|
|
205
|
+
assert_nothing_raised do
|
|
206
|
+
@dummy.avatar.clear
|
|
207
|
+
@dummy.save
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
[000,002,022].each do |umask|
|
|
215
|
+
context "when the umask is #{umask}" do
|
|
216
|
+
before do
|
|
217
|
+
rebuild_model
|
|
218
|
+
@dummy = Dummy.new
|
|
219
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
|
220
|
+
@umask = File.umask(umask)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
after do
|
|
224
|
+
File.umask @umask
|
|
225
|
+
@file.close
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
it "respects the current umask" do
|
|
229
|
+
@dummy.avatar = @file
|
|
230
|
+
@dummy.save
|
|
231
|
+
assert_equal 0666&~umask, 0666&File.stat(@dummy.avatar.path).mode
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
[0666,0664,0640].each do |perms|
|
|
237
|
+
context "when the perms are #{perms}" do
|
|
238
|
+
before do
|
|
239
|
+
rebuild_model override_file_permissions: perms
|
|
240
|
+
@dummy = Dummy.new
|
|
241
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
after do
|
|
245
|
+
@file.close
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
it "respects the current perms" do
|
|
249
|
+
@dummy.avatar = @file
|
|
250
|
+
@dummy.save
|
|
251
|
+
assert_equal perms, File.stat(@dummy.avatar.path).mode & 0777
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
it "skips chmod operation, when override_file_permissions is set to false (e.g. useful when using CIFS mounts)" do
|
|
257
|
+
FileUtils.expects(:chmod).never
|
|
258
|
+
|
|
259
|
+
rebuild_model override_file_permissions: false
|
|
260
|
+
dummy = Dummy.create!
|
|
261
|
+
dummy.avatar = @file
|
|
262
|
+
dummy.save
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
context "A model with a filesystem attachment" do
|
|
266
|
+
before do
|
|
267
|
+
rebuild_model styles: { large: "300x300>",
|
|
268
|
+
medium: "100x100",
|
|
269
|
+
thumb: ["32x32#", :gif] },
|
|
270
|
+
default_style: :medium,
|
|
271
|
+
url: "/:attachment/:class/:style/:id/:basename.:extension",
|
|
272
|
+
path: ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
|
|
273
|
+
@dummy = Dummy.new
|
|
274
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
|
275
|
+
@bad_file = File.new(fixture_file("bad.png"), 'rb')
|
|
276
|
+
|
|
277
|
+
assert @dummy.avatar = @file
|
|
278
|
+
assert @dummy.valid?, @dummy.errors.full_messages.join(", ")
|
|
279
|
+
assert @dummy.save
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
after { [@file, @bad_file].each(&:close) }
|
|
283
|
+
|
|
284
|
+
it "writes and delete its files" do
|
|
285
|
+
[["434x66", :original],
|
|
286
|
+
["300x46", :large],
|
|
287
|
+
["100x15", :medium],
|
|
288
|
+
["32x32", :thumb]].each do |geo, style|
|
|
289
|
+
cmd = %Q[identify -format "%wx%h" "#{@dummy.avatar.path(style)}"]
|
|
290
|
+
assert_equal geo, `#{cmd}`.chomp, cmd
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
|
|
294
|
+
|
|
295
|
+
@d2 = Dummy.find(@dummy.id)
|
|
296
|
+
assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path}"`.chomp
|
|
297
|
+
assert_equal "434x66", `identify -format "%wx%h" "#{@d2.avatar.path(:original)}"`.chomp
|
|
298
|
+
assert_equal "300x46", `identify -format "%wx%h" "#{@d2.avatar.path(:large)}"`.chomp
|
|
299
|
+
assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path(:medium)}"`.chomp
|
|
300
|
+
assert_equal "32x32", `identify -format "%wx%h" "#{@d2.avatar.path(:thumb)}"`.chomp
|
|
301
|
+
|
|
302
|
+
assert @dummy.valid?
|
|
303
|
+
assert @dummy.save
|
|
304
|
+
|
|
305
|
+
saved_paths.each do |p|
|
|
306
|
+
assert_file_exists(p)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
@dummy.avatar.clear
|
|
310
|
+
assert_nil @dummy.avatar_file_name
|
|
311
|
+
assert @dummy.valid?
|
|
312
|
+
assert @dummy.save
|
|
313
|
+
|
|
314
|
+
saved_paths.each do |p|
|
|
315
|
+
assert_file_not_exists(p)
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
@d2 = Dummy.find(@dummy.id)
|
|
319
|
+
assert_nil @d2.avatar_file_name
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
it "works exactly the same when new as when reloaded" do
|
|
323
|
+
@d2 = Dummy.find(@dummy.id)
|
|
324
|
+
|
|
325
|
+
assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
|
|
326
|
+
[:thumb, :medium, :large, :original].each do |style|
|
|
327
|
+
assert_equal @dummy.avatar.path(style), @d2.avatar.path(style)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
|
|
331
|
+
|
|
332
|
+
@d2.avatar.clear
|
|
333
|
+
assert @d2.save
|
|
334
|
+
|
|
335
|
+
saved_paths.each do |p|
|
|
336
|
+
assert_file_not_exists(p)
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
it "does not abide things that don't have adapters" do
|
|
341
|
+
assert_raises(Paperclip::AdapterRegistry::NoHandlerError) do
|
|
342
|
+
@dummy.avatar = "not a file"
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
it "is not ok with bad files" do
|
|
347
|
+
@dummy.avatar = @bad_file
|
|
348
|
+
assert ! @dummy.valid?
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
it "knows the difference between good files, bad files, and not files when validating" do
|
|
352
|
+
Dummy.validates_attachment_presence :avatar
|
|
353
|
+
@d2 = Dummy.find(@dummy.id)
|
|
354
|
+
@d2.avatar = @file
|
|
355
|
+
assert @d2.valid?, @d2.errors.full_messages.inspect
|
|
356
|
+
@d2.avatar = @bad_file
|
|
357
|
+
assert ! @d2.valid?
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
it "is able to reload without saving and not have the file disappear" do
|
|
361
|
+
@dummy.avatar = @file
|
|
362
|
+
assert @dummy.save, @dummy.errors.full_messages.inspect
|
|
363
|
+
@dummy.avatar.clear
|
|
364
|
+
assert_nil @dummy.avatar_file_name
|
|
365
|
+
@dummy.reload
|
|
366
|
+
assert_equal "5k.png", @dummy.avatar_file_name
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
context "that is assigned its file from another Paperclip attachment" do
|
|
370
|
+
before do
|
|
371
|
+
@dummy2 = Dummy.new
|
|
372
|
+
@file2 = File.new(fixture_file("12k.png"), 'rb')
|
|
373
|
+
assert @dummy2.avatar = @file2
|
|
374
|
+
@dummy2.save
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
after { @file2.close }
|
|
378
|
+
|
|
379
|
+
it "works when assigned a file" do
|
|
380
|
+
assert_not_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
|
|
381
|
+
`identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
|
|
382
|
+
|
|
383
|
+
assert @dummy.avatar = @dummy2.avatar
|
|
384
|
+
@dummy.save
|
|
385
|
+
assert_equal @dummy.avatar_file_name, @dummy2.avatar_file_name
|
|
386
|
+
assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
|
|
387
|
+
`identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
context "A model with an attachments association and a Paperclip attachment" do
|
|
394
|
+
before do
|
|
395
|
+
Dummy.class_eval do
|
|
396
|
+
has_many :attachments, class_name: 'Dummy'
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
|
400
|
+
@dummy = Dummy.new
|
|
401
|
+
@dummy.avatar = @file
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
after { @file.close }
|
|
405
|
+
|
|
406
|
+
it "does not error when saving" do
|
|
407
|
+
@dummy.save!
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
context "A model with an attachment with hash in file name" do
|
|
412
|
+
before do
|
|
413
|
+
@settings = { styles: { thumb: "50x50#" },
|
|
414
|
+
path: ":rails_root/public/system/:attachment/:id_partition/:style/:hash.:extension",
|
|
415
|
+
url: "/system/:attachment/:id_partition/:style/:hash.:extension",
|
|
416
|
+
hash_secret: "somesecret" }
|
|
417
|
+
|
|
418
|
+
rebuild_model @settings
|
|
419
|
+
|
|
420
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
|
421
|
+
@dummy = Dummy.create! avatar: @file
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
after do
|
|
425
|
+
@file.close
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
it "is accessible" do
|
|
429
|
+
assert_file_exists(@dummy.avatar.path(:original))
|
|
430
|
+
assert_file_exists(@dummy.avatar.path(:thumb))
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
context "when new style is added" do
|
|
434
|
+
before do
|
|
435
|
+
@dummy.avatar.options[:styles][:mini] = "25x25#"
|
|
436
|
+
@dummy.avatar.instance_variable_set :@normalized_styles, nil
|
|
437
|
+
Time.stubs(now: Time.now + 10)
|
|
438
|
+
@dummy.avatar.reprocess!
|
|
439
|
+
@dummy.reload
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
it "makes all the styles accessible" do
|
|
443
|
+
assert_file_exists(@dummy.avatar.path(:original))
|
|
444
|
+
assert_file_exists(@dummy.avatar.path(:thumb))
|
|
445
|
+
assert_file_exists(@dummy.avatar.path(:mini))
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
if ENV['S3_BUCKET']
|
|
451
|
+
def s3_files_for attachment
|
|
452
|
+
[:thumb, :medium, :large, :original].inject({}) do |files, style|
|
|
453
|
+
data = `curl "#{attachment.url(style)}" 2>/dev/null`.chomp
|
|
454
|
+
t = Tempfile.new("paperclip-test")
|
|
455
|
+
t.binmode
|
|
456
|
+
t.write(data)
|
|
457
|
+
t.rewind
|
|
458
|
+
files[style] = t
|
|
459
|
+
files
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def s3_headers_for attachment, style
|
|
464
|
+
`curl --head "#{attachment.url(style)}" 2>/dev/null`.split("\n").inject({}) do |h,head|
|
|
465
|
+
split_head = head.chomp.split(/\s*:\s*/, 2)
|
|
466
|
+
h[split_head.first.downcase] = split_head.last unless split_head.empty?
|
|
467
|
+
h
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
context "A model with an S3 attachment" do
|
|
472
|
+
before do
|
|
473
|
+
rebuild_model(
|
|
474
|
+
styles: {
|
|
475
|
+
large: "300x300>",
|
|
476
|
+
medium: "100x100",
|
|
477
|
+
thumb: ["32x32#", :gif],
|
|
478
|
+
custom: {
|
|
479
|
+
geometry: "32x32#",
|
|
480
|
+
s3_headers: { 'Cache-Control' => 'max-age=31557600' },
|
|
481
|
+
s3_metadata: { 'foo' => 'bar'}
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
storage: :s3,
|
|
485
|
+
s3_credentials: File.new(fixture_file('s3.yml')),
|
|
486
|
+
s3_options: { logger: Paperclip.logger },
|
|
487
|
+
default_style: :medium,
|
|
488
|
+
bucket: ENV['S3_BUCKET'],
|
|
489
|
+
path: ":class/:attachment/:id/:style/:basename.:extension"
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
@dummy = Dummy.new
|
|
493
|
+
@file = File.new(fixture_file('5k.png'), 'rb')
|
|
494
|
+
@bad_file = File.new(fixture_file('bad.png'), 'rb')
|
|
495
|
+
|
|
496
|
+
@dummy.avatar = @file
|
|
497
|
+
@dummy.valid?
|
|
498
|
+
@dummy.save!
|
|
499
|
+
|
|
500
|
+
@files_on_s3 = s3_files_for(@dummy.avatar)
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
after do
|
|
504
|
+
@file.close
|
|
505
|
+
@bad_file.close
|
|
506
|
+
@files_on_s3.values.each(&:close) if @files_on_s3
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
context 'assigning itself to a new model' do
|
|
510
|
+
before do
|
|
511
|
+
@d2 = Dummy.new
|
|
512
|
+
@d2.avatar = @dummy.avatar
|
|
513
|
+
@d2.save
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
it "has the same name as the old file" do
|
|
517
|
+
assert_equal @d2.avatar.original_filename, @dummy.avatar.original_filename
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
it "has the same contents as the original" do
|
|
522
|
+
assert_equal @file.read, @files_on_s3[:original].read
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
it "writes and delete its files" do
|
|
526
|
+
[["434x66", :original],
|
|
527
|
+
["300x46", :large],
|
|
528
|
+
["100x15", :medium],
|
|
529
|
+
["32x32", :thumb]].each do |geo, style|
|
|
530
|
+
cmd = %Q[identify -format "%wx%h" "#{@files_on_s3[style].path}"]
|
|
531
|
+
assert_equal geo, `#{cmd}`.chomp, cmd
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
@d2 = Dummy.find(@dummy.id)
|
|
535
|
+
@d2_files = s3_files_for @d2.avatar
|
|
536
|
+
[["434x66", :original],
|
|
537
|
+
["300x46", :large],
|
|
538
|
+
["100x15", :medium],
|
|
539
|
+
["32x32", :thumb]].each do |geo, style|
|
|
540
|
+
cmd = %Q[identify -format "%wx%h" "#{@d2_files[style].path}"]
|
|
541
|
+
assert_equal geo, `#{cmd}`.chomp, cmd
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
@dummy.avatar.clear
|
|
545
|
+
assert_nil @dummy.avatar_file_name
|
|
546
|
+
assert @dummy.valid?
|
|
547
|
+
assert @dummy.save
|
|
548
|
+
|
|
549
|
+
[:thumb, :medium, :large, :original].each do |style|
|
|
550
|
+
assert ! @dummy.avatar.exists?(style)
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
@d2 = Dummy.find(@dummy.id)
|
|
554
|
+
assert_nil @d2.avatar_file_name
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
it "works exactly the same when new as when reloaded" do
|
|
558
|
+
@d2 = Dummy.find(@dummy.id)
|
|
559
|
+
|
|
560
|
+
assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
|
|
561
|
+
|
|
562
|
+
[:thumb, :medium, :large, :original].each do |style|
|
|
563
|
+
begin
|
|
564
|
+
first_file = open(@dummy.avatar.url(style))
|
|
565
|
+
second_file = open(@dummy.avatar.url(style))
|
|
566
|
+
assert_equal first_file.read, second_file.read
|
|
567
|
+
ensure
|
|
568
|
+
first_file.close if first_file
|
|
569
|
+
second_file.close if second_file
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
@d2.avatar.clear
|
|
574
|
+
assert @d2.save
|
|
575
|
+
|
|
576
|
+
[:thumb, :medium, :large, :original].each do |style|
|
|
577
|
+
assert ! @dummy.avatar.exists?(style)
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
it "knows the difference between good files, bad files, and nil" do
|
|
582
|
+
@dummy.avatar = @bad_file
|
|
583
|
+
assert ! @dummy.valid?
|
|
584
|
+
@dummy.avatar = nil
|
|
585
|
+
assert @dummy.valid?
|
|
586
|
+
|
|
587
|
+
Dummy.validates_attachment_presence :avatar
|
|
588
|
+
@d2 = Dummy.find(@dummy.id)
|
|
589
|
+
@d2.avatar = @file
|
|
590
|
+
assert @d2.valid?
|
|
591
|
+
@d2.avatar = @bad_file
|
|
592
|
+
assert ! @d2.valid?
|
|
593
|
+
@d2.avatar = nil
|
|
594
|
+
assert ! @d2.valid?
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
it "is able to reload without saving and not have the file disappear" do
|
|
598
|
+
@dummy.avatar = @file
|
|
599
|
+
assert @dummy.save
|
|
600
|
+
@dummy.avatar = nil
|
|
601
|
+
assert_nil @dummy.avatar_file_name
|
|
602
|
+
@dummy.reload
|
|
603
|
+
assert_equal "5k.png", @dummy.avatar_file_name
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
it "has the right content type" do
|
|
607
|
+
headers = s3_headers_for(@dummy.avatar, :original)
|
|
608
|
+
assert_equal 'image/png', headers['content-type']
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
it "has the right style-specific headers" do
|
|
612
|
+
headers = s3_headers_for(@dummy.avatar, :custom)
|
|
613
|
+
assert_equal 'max-age=31557600', headers['cache-control']
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
it "has the right style-specific metadata" do
|
|
617
|
+
headers = s3_headers_for(@dummy.avatar, :custom)
|
|
618
|
+
assert_equal 'bar', headers['x-amz-meta-foo']
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
context "with non-english character in the file name" do
|
|
622
|
+
before do
|
|
623
|
+
@file.stubs(:original_filename).returns("クリップ.png")
|
|
624
|
+
@dummy.avatar = @file
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
it "does not raise any error" do
|
|
628
|
+
@dummy.save!
|
|
629
|
+
end
|
|
630
|
+
end
|
|
631
|
+
end
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
context "Copying attachments between models" do
|
|
635
|
+
before do
|
|
636
|
+
rebuild_model
|
|
637
|
+
@file = File.new(fixture_file("5k.png"), 'rb')
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
after { @file.close }
|
|
641
|
+
|
|
642
|
+
it "succeeds when original attachment is a file" do
|
|
643
|
+
original = Dummy.new
|
|
644
|
+
original.avatar = @file
|
|
645
|
+
assert original.save
|
|
646
|
+
|
|
647
|
+
copy = Dummy.new
|
|
648
|
+
copy.avatar = original.avatar
|
|
649
|
+
assert copy.save
|
|
650
|
+
|
|
651
|
+
assert copy.avatar.present?
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
it "succeeds when original attachment is empty" do
|
|
655
|
+
original = Dummy.create!
|
|
656
|
+
|
|
657
|
+
copy = Dummy.new
|
|
658
|
+
copy.avatar = @file
|
|
659
|
+
assert copy.save
|
|
660
|
+
assert copy.avatar.present?
|
|
661
|
+
|
|
662
|
+
copy.avatar = original.avatar
|
|
663
|
+
assert copy.save
|
|
664
|
+
assert !copy.avatar.present?
|
|
665
|
+
end
|
|
666
|
+
end
|
|
667
|
+
end
|