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.
Files changed (197) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.hound.yml +1066 -0
  4. data/.rubocop.yml +1 -0
  5. data/.travis.yml +22 -0
  6. data/Appraisals +11 -0
  7. data/CONTRIBUTING.md +75 -0
  8. data/Gemfile +21 -0
  9. data/LICENSE +24 -0
  10. data/NEWS +420 -0
  11. data/README.md +979 -0
  12. data/RELEASING.md +17 -0
  13. data/Rakefile +44 -0
  14. data/UPGRADING +14 -0
  15. data/cucumber/paperclip_steps.rb +6 -0
  16. data/features/basic_integration.feature +80 -0
  17. data/features/migration.feature +94 -0
  18. data/features/rake_tasks.feature +62 -0
  19. data/features/step_definitions/attachment_steps.rb +110 -0
  20. data/features/step_definitions/html_steps.rb +15 -0
  21. data/features/step_definitions/rails_steps.rb +236 -0
  22. data/features/step_definitions/s3_steps.rb +14 -0
  23. data/features/step_definitions/web_steps.rb +107 -0
  24. data/features/support/env.rb +11 -0
  25. data/features/support/fakeweb.rb +13 -0
  26. data/features/support/file_helpers.rb +34 -0
  27. data/features/support/fixtures/boot_config.txt +15 -0
  28. data/features/support/fixtures/gemfile.txt +5 -0
  29. data/features/support/fixtures/preinitializer.txt +20 -0
  30. data/features/support/paths.rb +28 -0
  31. data/features/support/rails.rb +63 -0
  32. data/features/support/selectors.rb +19 -0
  33. data/gemfiles/3.2.gemfile +19 -0
  34. data/gemfiles/4.1.gemfile +19 -0
  35. data/gemfiles/4.2.gemfile +19 -0
  36. data/lib/generators/paperclip/USAGE +8 -0
  37. data/lib/generators/paperclip/paperclip_generator.rb +30 -0
  38. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +15 -0
  39. data/lib/paperclip/attachment.rb +608 -0
  40. data/lib/paperclip/attachment_registry.rb +59 -0
  41. data/lib/paperclip/callbacks.rb +40 -0
  42. data/lib/paperclip/content_type_detector.rb +79 -0
  43. data/lib/paperclip/deprecations.rb +42 -0
  44. data/lib/paperclip/errors.rb +32 -0
  45. data/lib/paperclip/file_command_content_type_detector.rb +30 -0
  46. data/lib/paperclip/filename_cleaner.rb +16 -0
  47. data/lib/paperclip/geometry.rb +158 -0
  48. data/lib/paperclip/geometry_detector_factory.rb +48 -0
  49. data/lib/paperclip/geometry_parser_factory.rb +31 -0
  50. data/lib/paperclip/glue.rb +17 -0
  51. data/lib/paperclip/has_attached_file.rb +109 -0
  52. data/lib/paperclip/helpers.rb +56 -0
  53. data/lib/paperclip/interpolations/plural_cache.rb +18 -0
  54. data/lib/paperclip/interpolations.rb +197 -0
  55. data/lib/paperclip/io_adapters/abstract_adapter.rb +47 -0
  56. data/lib/paperclip/io_adapters/attachment_adapter.rb +36 -0
  57. data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
  58. data/lib/paperclip/io_adapters/empty_string_adapter.rb +18 -0
  59. data/lib/paperclip/io_adapters/file_adapter.rb +22 -0
  60. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +15 -0
  61. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -0
  62. data/lib/paperclip/io_adapters/nil_adapter.rb +34 -0
  63. data/lib/paperclip/io_adapters/registry.rb +32 -0
  64. data/lib/paperclip/io_adapters/stringio_adapter.rb +33 -0
  65. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +42 -0
  66. data/lib/paperclip/io_adapters/uri_adapter.rb +63 -0
  67. data/lib/paperclip/locales/de.yml +18 -0
  68. data/lib/paperclip/locales/en.yml +18 -0
  69. data/lib/paperclip/locales/es.yml +18 -0
  70. data/lib/paperclip/locales/ja.yml +18 -0
  71. data/lib/paperclip/locales/pt-BR.yml +18 -0
  72. data/lib/paperclip/locales/zh-CN.yml +18 -0
  73. data/lib/paperclip/locales/zh-HK.yml +18 -0
  74. data/lib/paperclip/locales/zh-TW.yml +18 -0
  75. data/lib/paperclip/logger.rb +21 -0
  76. data/lib/paperclip/matchers/have_attached_file_matcher.rb +54 -0
  77. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +100 -0
  78. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +59 -0
  79. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +96 -0
  80. data/lib/paperclip/matchers.rb +64 -0
  81. data/lib/paperclip/media_type_spoof_detector.rb +89 -0
  82. data/lib/paperclip/missing_attachment_styles.rb +79 -0
  83. data/lib/paperclip/processor.rb +48 -0
  84. data/lib/paperclip/processor_helpers.rb +50 -0
  85. data/lib/paperclip/rails_environment.rb +25 -0
  86. data/lib/paperclip/railtie.rb +31 -0
  87. data/lib/paperclip/schema.rb +83 -0
  88. data/lib/paperclip/storage/filesystem.rb +90 -0
  89. data/lib/paperclip/storage/fog.rb +241 -0
  90. data/lib/paperclip/storage/s3.rb +440 -0
  91. data/lib/paperclip/storage.rb +3 -0
  92. data/lib/paperclip/style.rb +109 -0
  93. data/lib/paperclip/tempfile.rb +43 -0
  94. data/lib/paperclip/tempfile_factory.rb +23 -0
  95. data/lib/paperclip/thumbnail.rb +121 -0
  96. data/lib/paperclip/url_generator.rb +72 -0
  97. data/lib/paperclip/validators/attachment_content_type_validator.rb +88 -0
  98. data/lib/paperclip/validators/attachment_file_name_validator.rb +80 -0
  99. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +29 -0
  100. data/lib/paperclip/validators/attachment_presence_validator.rb +30 -0
  101. data/lib/paperclip/validators/attachment_size_validator.rb +115 -0
  102. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +27 -0
  103. data/lib/paperclip/validators.rb +74 -0
  104. data/lib/paperclip/version.rb +3 -0
  105. data/lib/paperclip.rb +213 -0
  106. data/lib/tasks/paperclip.rake +127 -0
  107. data/paperclip.gemspec +51 -0
  108. data/shoulda_macros/paperclip.rb +134 -0
  109. data/spec/database.yml +4 -0
  110. data/spec/paperclip/attachment_definitions_spec.rb +13 -0
  111. data/spec/paperclip/attachment_processing_spec.rb +82 -0
  112. data/spec/paperclip/attachment_registry_spec.rb +130 -0
  113. data/spec/paperclip/attachment_spec.rb +1494 -0
  114. data/spec/paperclip/content_type_detector_spec.rb +48 -0
  115. data/spec/paperclip/deprecations_spec.rb +65 -0
  116. data/spec/paperclip/file_command_content_type_detector_spec.rb +26 -0
  117. data/spec/paperclip/filename_cleaner_spec.rb +14 -0
  118. data/spec/paperclip/geometry_detector_spec.rb +39 -0
  119. data/spec/paperclip/geometry_parser_spec.rb +73 -0
  120. data/spec/paperclip/geometry_spec.rb +255 -0
  121. data/spec/paperclip/glue_spec.rb +44 -0
  122. data/spec/paperclip/has_attached_file_spec.rb +142 -0
  123. data/spec/paperclip/integration_spec.rb +667 -0
  124. data/spec/paperclip/interpolations_spec.rb +262 -0
  125. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +78 -0
  126. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +139 -0
  127. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +83 -0
  128. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
  129. data/spec/paperclip/io_adapters/file_adapter_spec.rb +131 -0
  130. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +104 -0
  131. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
  132. data/spec/paperclip/io_adapters/nil_adapter_spec.rb +25 -0
  133. data/spec/paperclip/io_adapters/registry_spec.rb +35 -0
  134. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +64 -0
  135. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +146 -0
  136. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +127 -0
  137. data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
  138. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +99 -0
  139. data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
  140. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
  141. data/spec/paperclip/media_type_spoof_detector_spec.rb +79 -0
  142. data/spec/paperclip/meta_class_spec.rb +30 -0
  143. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +84 -0
  144. data/spec/paperclip/paperclip_spec.rb +222 -0
  145. data/spec/paperclip/plural_cache_spec.rb +37 -0
  146. data/spec/paperclip/processor_helpers_spec.rb +57 -0
  147. data/spec/paperclip/processor_spec.rb +26 -0
  148. data/spec/paperclip/rails_environment_spec.rb +33 -0
  149. data/spec/paperclip/rake_spec.rb +103 -0
  150. data/spec/paperclip/schema_spec.rb +248 -0
  151. data/spec/paperclip/storage/filesystem_spec.rb +79 -0
  152. data/spec/paperclip/storage/fog_spec.rb +535 -0
  153. data/spec/paperclip/storage/s3_live_spec.rb +182 -0
  154. data/spec/paperclip/storage/s3_spec.rb +1526 -0
  155. data/spec/paperclip/style_spec.rb +255 -0
  156. data/spec/paperclip/tempfile_factory_spec.rb +33 -0
  157. data/spec/paperclip/thumbnail_spec.rb +500 -0
  158. data/spec/paperclip/url_generator_spec.rb +211 -0
  159. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
  160. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +160 -0
  161. data/spec/paperclip/validators/attachment_presence_validator_spec.rb +85 -0
  162. data/spec/paperclip/validators/attachment_size_validator_spec.rb +229 -0
  163. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +52 -0
  164. data/spec/paperclip/validators_spec.rb +164 -0
  165. data/spec/spec_helper.rb +43 -0
  166. data/spec/support/assertions.rb +71 -0
  167. data/spec/support/deprecations.rb +9 -0
  168. data/spec/support/fake_model.rb +25 -0
  169. data/spec/support/fake_rails.rb +12 -0
  170. data/spec/support/fixtures/12k.png +0 -0
  171. data/spec/support/fixtures/50x50.png +0 -0
  172. data/spec/support/fixtures/5k.png +0 -0
  173. data/spec/support/fixtures/animated +0 -0
  174. data/spec/support/fixtures/animated.gif +0 -0
  175. data/spec/support/fixtures/animated.unknown +0 -0
  176. data/spec/support/fixtures/bad.png +1 -0
  177. data/spec/support/fixtures/empty.html +1 -0
  178. data/spec/support/fixtures/empty.xlsx +0 -0
  179. data/spec/support/fixtures/fog.yml +8 -0
  180. data/spec/support/fixtures/rotated.jpg +0 -0
  181. data/spec/support/fixtures/s3.yml +8 -0
  182. data/spec/support/fixtures/spaced file.jpg +0 -0
  183. data/spec/support/fixtures/spaced file.png +0 -0
  184. data/spec/support/fixtures/text.txt +1 -0
  185. data/spec/support/fixtures/twopage.pdf +0 -0
  186. data/spec/support/fixtures/uppercase.PNG +0 -0
  187. data/spec/support/matchers/accept.rb +5 -0
  188. data/spec/support/matchers/exist.rb +5 -0
  189. data/spec/support/matchers/have_column.rb +23 -0
  190. data/spec/support/mock_attachment.rb +22 -0
  191. data/spec/support/mock_interpolator.rb +24 -0
  192. data/spec/support/mock_url_generator_builder.rb +27 -0
  193. data/spec/support/model_reconstruction.rb +60 -0
  194. data/spec/support/rails_helpers.rb +7 -0
  195. data/spec/support/test_data.rb +13 -0
  196. data/spec/support/version_helper.rb +9 -0
  197. 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