cloudfuji_paperclip 2.4.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. data/.gitignore +22 -0
  2. data/.travis.yml +13 -0
  3. data/Appraisals +14 -0
  4. data/CONTRIBUTING.md +38 -0
  5. data/Gemfile +5 -0
  6. data/Gemfile.lock +137 -0
  7. data/LICENSE +26 -0
  8. data/README.md +444 -0
  9. data/Rakefile +41 -0
  10. data/cucumber/paperclip_steps.rb +6 -0
  11. data/features/basic_integration.feature +46 -0
  12. data/features/rake_tasks.feature +63 -0
  13. data/features/step_definitions/attachment_steps.rb +65 -0
  14. data/features/step_definitions/html_steps.rb +15 -0
  15. data/features/step_definitions/rails_steps.rb +182 -0
  16. data/features/step_definitions/s3_steps.rb +14 -0
  17. data/features/step_definitions/web_steps.rb +209 -0
  18. data/features/support/env.rb +8 -0
  19. data/features/support/fakeweb.rb +3 -0
  20. data/features/support/fixtures/.boot_config.rb.swo +0 -0
  21. data/features/support/fixtures/boot_config.txt +15 -0
  22. data/features/support/fixtures/gemfile.txt +5 -0
  23. data/features/support/fixtures/preinitializer.txt +20 -0
  24. data/features/support/paths.rb +28 -0
  25. data/features/support/rails.rb +46 -0
  26. data/features/support/selectors.rb +19 -0
  27. data/gemfiles/rails2.gemfile +9 -0
  28. data/gemfiles/rails3.gemfile +9 -0
  29. data/gemfiles/rails3_1.gemfile +9 -0
  30. data/generators/paperclip/USAGE +5 -0
  31. data/generators/paperclip/paperclip_generator.rb +27 -0
  32. data/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
  33. data/init.rb +4 -0
  34. data/lib/generators/paperclip/USAGE +8 -0
  35. data/lib/generators/paperclip/paperclip_generator.rb +33 -0
  36. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
  37. data/lib/paperclip/attachment.rb +468 -0
  38. data/lib/paperclip/callback_compatibility.rb +61 -0
  39. data/lib/paperclip/geometry.rb +120 -0
  40. data/lib/paperclip/interpolations.rb +174 -0
  41. data/lib/paperclip/iostream.rb +45 -0
  42. data/lib/paperclip/matchers/have_attached_file_matcher.rb +57 -0
  43. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +81 -0
  44. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +54 -0
  45. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +95 -0
  46. data/lib/paperclip/matchers.rb +64 -0
  47. data/lib/paperclip/missing_attachment_styles.rb +87 -0
  48. data/lib/paperclip/processor.rb +58 -0
  49. data/lib/paperclip/railtie.rb +31 -0
  50. data/lib/paperclip/schema.rb +39 -0
  51. data/lib/paperclip/storage/filesystem.rb +81 -0
  52. data/lib/paperclip/storage/fog.rb +174 -0
  53. data/lib/paperclip/storage/s3.rb +333 -0
  54. data/lib/paperclip/storage.rb +3 -0
  55. data/lib/paperclip/style.rb +103 -0
  56. data/lib/paperclip/thumbnail.rb +105 -0
  57. data/lib/paperclip/upfile.rb +62 -0
  58. data/lib/paperclip/url_generator.rb +64 -0
  59. data/lib/paperclip/version.rb +3 -0
  60. data/lib/paperclip.rb +487 -0
  61. data/lib/tasks/paperclip.rake +101 -0
  62. data/paperclip.gemspec +41 -0
  63. data/rails/init.rb +2 -0
  64. data/shoulda_macros/paperclip.rb +124 -0
  65. data/test/.gitignore +1 -0
  66. data/test/attachment_test.rb +1116 -0
  67. data/test/database.yml +4 -0
  68. data/test/fixtures/12k.png +0 -0
  69. data/test/fixtures/50x50.png +0 -0
  70. data/test/fixtures/5k.png +0 -0
  71. data/test/fixtures/animated.gif +0 -0
  72. data/test/fixtures/bad.png +1 -0
  73. data/test/fixtures/fog.yml +8 -0
  74. data/test/fixtures/question?mark.png +0 -0
  75. data/test/fixtures/s3.yml +8 -0
  76. data/test/fixtures/spaced file.png +0 -0
  77. data/test/fixtures/text.txt +1 -0
  78. data/test/fixtures/twopage.pdf +0 -0
  79. data/test/fixtures/uppercase.PNG +0 -0
  80. data/test/geometry_test.rb +206 -0
  81. data/test/helper.rb +177 -0
  82. data/test/integration_test.rb +654 -0
  83. data/test/interpolations_test.rb +216 -0
  84. data/test/iostream_test.rb +71 -0
  85. data/test/matchers/have_attached_file_matcher_test.rb +24 -0
  86. data/test/matchers/validate_attachment_content_type_matcher_test.rb +87 -0
  87. data/test/matchers/validate_attachment_presence_matcher_test.rb +26 -0
  88. data/test/matchers/validate_attachment_size_matcher_test.rb +51 -0
  89. data/test/paperclip_missing_attachment_styles_test.rb +80 -0
  90. data/test/paperclip_test.rb +390 -0
  91. data/test/processor_test.rb +10 -0
  92. data/test/schema_test.rb +98 -0
  93. data/test/storage/filesystem_test.rb +56 -0
  94. data/test/storage/fog_test.rb +219 -0
  95. data/test/storage/s3_live_test.rb +138 -0
  96. data/test/storage/s3_test.rb +943 -0
  97. data/test/style_test.rb +209 -0
  98. data/test/support/mock_attachment.rb +22 -0
  99. data/test/support/mock_interpolator.rb +24 -0
  100. data/test/support/mock_model.rb +2 -0
  101. data/test/support/mock_url_generator_builder.rb +27 -0
  102. data/test/thumbnail_test.rb +383 -0
  103. data/test/upfile_test.rb +53 -0
  104. data/test/url_generator_test.rb +187 -0
  105. metadata +404 -0
@@ -0,0 +1,654 @@
1
+ require './test/helper'
2
+
3
+ class IntegrationTest < Test::Unit::TestCase
4
+ context "Many models at once" do
5
+ setup do
6
+ rebuild_model
7
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
8
+ 300.times do |i|
9
+ Dummy.create! :avatar => @file
10
+ end
11
+ end
12
+
13
+ should "not exceed the open file limit" do
14
+ assert_nothing_raised do
15
+ dummies = Dummy.find(:all)
16
+ dummies.each { |dummy| dummy.avatar }
17
+ end
18
+ end
19
+ end
20
+
21
+ context "An attachment" do
22
+ setup do
23
+ rebuild_model :styles => { :thumb => "50x50#" }
24
+ @dummy = Dummy.new
25
+ @file = File.new(File.join(File.dirname(__FILE__),
26
+ "fixtures",
27
+ "5k.png"), 'rb')
28
+ @dummy.avatar = @file
29
+ assert @dummy.save
30
+ end
31
+
32
+ teardown { @file.close }
33
+
34
+ should "create its thumbnails properly" do
35
+ assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
36
+ end
37
+
38
+ context 'reprocessing with unreadable original' do
39
+ setup { File.chmod(0000, @dummy.avatar.path) }
40
+
41
+ should "not raise an error" do
42
+ assert_nothing_raised do
43
+ @dummy.avatar.reprocess!
44
+ end
45
+ end
46
+
47
+ should "return false" do
48
+ assert ! @dummy.avatar.reprocess!
49
+ end
50
+
51
+ teardown { File.chmod(0644, @dummy.avatar.path) }
52
+ end
53
+
54
+ context "redefining its attachment styles" do
55
+ setup do
56
+ Dummy.class_eval do
57
+ has_attached_file :avatar, :styles => { :thumb => "150x25#" }
58
+ has_attached_file :avatar, :styles => { :thumb => "150x25#", :dynamic => lambda { |a| '50x50#' } }
59
+ end
60
+ @d2 = Dummy.find(@dummy.id)
61
+ @original_timestamp = @d2.avatar_updated_at
62
+ @d2.avatar.reprocess!
63
+ @d2.save
64
+ end
65
+
66
+ should "create its thumbnails properly" do
67
+ assert_match /\b150x25\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
68
+ assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:dynamic)}"`
69
+ end
70
+
71
+ should "change the timestamp" do
72
+ assert_not_equal @original_timestamp, @d2.avatar_updated_at
73
+ end
74
+
75
+ should "clean up the old original if it is a tempfile" do
76
+ original = @d2.avatar.to_file(:original)
77
+ tf = Paperclip::Tempfile.new('original')
78
+ tf.binmode
79
+ original.binmode
80
+ tf.write(original.read)
81
+ original.close
82
+ tf.rewind
83
+
84
+ File.expects(:unlink).with(tf.instance_variable_get(:@tmpname))
85
+
86
+ @d2.avatar.expects(:to_file).with(:original).returns(tf)
87
+
88
+ @d2.avatar.reprocess!
89
+ end
90
+ end
91
+ end
92
+
93
+ context "Attachment" do
94
+ setup do
95
+ @thumb_path = "./test/../public/system/avatars/1/thumb/5k.png"
96
+ File.delete(@thumb_path) if File.exists?(@thumb_path)
97
+ rebuild_model :styles => { :thumb => "50x50#" }
98
+ @dummy = Dummy.new
99
+ @file = File.new(File.join(File.dirname(__FILE__),
100
+ "fixtures",
101
+ "5k.png"), 'rb')
102
+
103
+ end
104
+
105
+ teardown { @file.close }
106
+
107
+ should "not create the thumbnails upon saving when post-processing is disabled" do
108
+ @dummy.avatar.post_processing = false
109
+ @dummy.avatar = @file
110
+ assert @dummy.save
111
+ assert !File.exists?(@thumb_path)
112
+ end
113
+
114
+ should "create the thumbnails upon saving when post_processing is enabled" do
115
+ @dummy.avatar.post_processing = true
116
+ @dummy.avatar = @file
117
+ assert @dummy.save
118
+ assert File.exists?(@thumb_path)
119
+ end
120
+ end
121
+
122
+ context "Attachment with no generated thumbnails" do
123
+ setup do
124
+ @thumb_small_path = "./test/../public/system/avatars/1/thumb_small/5k.png"
125
+ @thumb_large_path = "./test/../public/system/avatars/1/thumb_large/5k.png"
126
+ File.delete(@thumb_small_path) if File.exists?(@thumb_small_path)
127
+ File.delete(@thumb_large_path) if File.exists?(@thumb_large_path)
128
+ rebuild_model :styles => { :thumb_small => "50x50#", :thumb_large => "60x60#" }
129
+ @dummy = Dummy.new
130
+ @file = File.new(File.join(File.dirname(__FILE__),
131
+ "fixtures",
132
+ "5k.png"), 'rb')
133
+
134
+ @dummy.avatar.post_processing = false
135
+ @dummy.avatar = @file
136
+ assert @dummy.save
137
+ @dummy.avatar.post_processing = true
138
+ end
139
+
140
+ teardown { @file.close }
141
+
142
+ should "allow us to create all thumbnails in one go" do
143
+ assert !File.exists?(@thumb_small_path)
144
+ assert !File.exists?(@thumb_large_path)
145
+
146
+ @dummy.avatar.reprocess!
147
+
148
+ assert File.exists?(@thumb_small_path)
149
+ assert File.exists?(@thumb_large_path)
150
+ end
151
+
152
+ should "allow us to selectively create each thumbnail" do
153
+ assert !File.exists?(@thumb_small_path)
154
+ assert !File.exists?(@thumb_large_path)
155
+
156
+ @dummy.avatar.reprocess! :thumb_small
157
+ assert File.exists?(@thumb_small_path)
158
+ assert !File.exists?(@thumb_large_path)
159
+
160
+ @dummy.avatar.reprocess! :thumb_large
161
+ assert File.exists?(@thumb_large_path)
162
+ end
163
+ end
164
+
165
+ context "A model that modifies its original" do
166
+ setup do
167
+ rebuild_model :styles => { :original => "2x2#" }
168
+ @dummy = Dummy.new
169
+ @file = File.new(File.join(File.dirname(__FILE__),
170
+ "fixtures",
171
+ "5k.png"), 'rb')
172
+ @dummy.avatar = @file
173
+ end
174
+
175
+ should "report the file size of the processed file and not the original" do
176
+ assert_not_equal @file.size, @dummy.avatar.size
177
+ end
178
+
179
+ teardown { @file.close }
180
+ end
181
+
182
+ context "A model with attachments scoped under an id" do
183
+ setup do
184
+ rebuild_model :styles => { :large => "100x100",
185
+ :medium => "50x50" },
186
+ :path => ":rails_root/tmp/:id/:attachments/:style.:extension"
187
+ @dummy = Dummy.new
188
+ @file = File.new(File.join(File.dirname(__FILE__),
189
+ "fixtures",
190
+ "5k.png"), 'rb')
191
+ @dummy.avatar = @file
192
+ end
193
+
194
+ teardown { @file.close }
195
+
196
+ context "when saved" do
197
+ setup do
198
+ @dummy.save
199
+ @saved_path = @dummy.avatar.path(:large)
200
+ end
201
+
202
+ should "have a large file in the right place" do
203
+ assert File.exists?(@dummy.avatar.path(:large))
204
+ end
205
+
206
+ context "and deleted" do
207
+ setup do
208
+ @dummy.avatar.clear
209
+ @dummy.save
210
+ end
211
+
212
+ should "not have a large file in the right place anymore" do
213
+ assert ! File.exists?(@saved_path)
214
+ end
215
+
216
+ should "not have its next two parent directories" do
217
+ assert ! File.exists?(File.dirname(@saved_path))
218
+ assert ! File.exists?(File.dirname(File.dirname(@saved_path)))
219
+ end
220
+
221
+ before_should "not die if an unexpected SystemCallError happens" do
222
+ FileUtils.stubs(:rmdir).raises(Errno::EPIPE)
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ context "A model with no attachment validation" do
229
+ setup do
230
+ rebuild_model :styles => { :large => "300x300>",
231
+ :medium => "100x100",
232
+ :thumb => ["32x32#", :gif] },
233
+ :default_style => :medium,
234
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
235
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
236
+ @dummy = Dummy.new
237
+ end
238
+
239
+ should "have its definition return false when asked about whiny_thumbnails" do
240
+ assert ! Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
241
+ end
242
+
243
+ context "when validates_attachment_thumbnails is called" do
244
+ setup do
245
+ Dummy.validates_attachment_thumbnails :avatar
246
+ end
247
+
248
+ should "have its definition return true when asked about whiny_thumbnails" do
249
+ assert_equal true, Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
250
+ end
251
+ end
252
+
253
+ context "redefined to have attachment validations" do
254
+ setup do
255
+ rebuild_model :styles => { :large => "300x300>",
256
+ :medium => "100x100",
257
+ :thumb => ["32x32#", :gif] },
258
+ :whiny_thumbnails => true,
259
+ :default_style => :medium,
260
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
261
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
262
+ end
263
+
264
+ should "have its definition return true when asked about whiny_thumbnails" do
265
+ assert_equal true, Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
266
+ end
267
+ end
268
+ end
269
+
270
+ context "A model with no convert_options setting" do
271
+ setup do
272
+ rebuild_model :styles => { :large => "300x300>",
273
+ :medium => "100x100",
274
+ :thumb => ["32x32#", :gif] },
275
+ :default_style => :medium,
276
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
277
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
278
+ @dummy = Dummy.new
279
+ end
280
+
281
+ should "have its definition return nil when asked about convert_options" do
282
+ assert ! Dummy.attachment_definitions[:avatar][:convert_options]
283
+ end
284
+
285
+ context "redefined to have convert_options setting" do
286
+ setup do
287
+ rebuild_model :styles => { :large => "300x300>",
288
+ :medium => "100x100",
289
+ :thumb => ["32x32#", :gif] },
290
+ :convert_options => "-strip -depth 8",
291
+ :default_style => :medium,
292
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
293
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
294
+ end
295
+
296
+ should "have its definition return convert_options value when asked about convert_options" do
297
+ assert_equal "-strip -depth 8", Dummy.attachment_definitions[:avatar][:convert_options]
298
+ end
299
+ end
300
+ end
301
+
302
+ context "A model with no source_file_options setting" do
303
+ setup do
304
+ rebuild_model :styles => { :large => "300x300>",
305
+ :medium => "100x100",
306
+ :thumb => ["32x32#", :gif] },
307
+ :default_style => :medium,
308
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
309
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
310
+ @dummy = Dummy.new
311
+ end
312
+
313
+ should "have its definition return nil when asked about source_file_options" do
314
+ assert ! Dummy.attachment_definitions[:avatar][:source_file_options]
315
+ end
316
+
317
+ context "redefined to have source_file_options setting" do
318
+ setup do
319
+ rebuild_model :styles => { :large => "300x300>",
320
+ :medium => "100x100",
321
+ :thumb => ["32x32#", :gif] },
322
+ :source_file_options => "-density 400",
323
+ :default_style => :medium,
324
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
325
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
326
+ end
327
+
328
+ should "have its definition return source_file_options value when asked about source_file_options" do
329
+ assert_equal "-density 400", Dummy.attachment_definitions[:avatar][:source_file_options]
330
+ end
331
+ end
332
+ end
333
+
334
+ context "A model with a filesystem attachment" do
335
+ setup do
336
+ rebuild_model :styles => { :large => "300x300>",
337
+ :medium => "100x100",
338
+ :thumb => ["32x32#", :gif] },
339
+ :whiny_thumbnails => true,
340
+ :default_style => :medium,
341
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
342
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
343
+ @dummy = Dummy.new
344
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
345
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
346
+
347
+ assert @dummy.avatar = @file
348
+ assert @dummy.valid?, @dummy.errors.full_messages.join(", ")
349
+ assert @dummy.save
350
+ end
351
+
352
+ should "write and delete its files" do
353
+ [["434x66", :original],
354
+ ["300x46", :large],
355
+ ["100x15", :medium],
356
+ ["32x32", :thumb]].each do |geo, style|
357
+ cmd = %Q[identify -format "%wx%h" "#{@dummy.avatar.path(style)}"]
358
+ assert_equal geo, `#{cmd}`.chomp, cmd
359
+ end
360
+
361
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
362
+
363
+ @d2 = Dummy.find(@dummy.id)
364
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path}"`.chomp
365
+ assert_equal "434x66", `identify -format "%wx%h" "#{@d2.avatar.path(:original)}"`.chomp
366
+ assert_equal "300x46", `identify -format "%wx%h" "#{@d2.avatar.path(:large)}"`.chomp
367
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path(:medium)}"`.chomp
368
+ assert_equal "32x32", `identify -format "%wx%h" "#{@d2.avatar.path(:thumb)}"`.chomp
369
+
370
+ @dummy.avatar = "not a valid file but not nil"
371
+ assert_equal File.basename(@file.path), @dummy.avatar_file_name
372
+ assert @dummy.valid?
373
+ assert @dummy.save
374
+
375
+ saved_paths.each do |p|
376
+ assert File.exists?(p)
377
+ end
378
+
379
+ @dummy.avatar.clear
380
+ assert_nil @dummy.avatar_file_name
381
+ assert @dummy.valid?
382
+ assert @dummy.save
383
+
384
+ saved_paths.each do |p|
385
+ assert ! File.exists?(p)
386
+ end
387
+
388
+ @d2 = Dummy.find(@dummy.id)
389
+ assert_nil @d2.avatar_file_name
390
+ end
391
+
392
+ should "work exactly the same when new as when reloaded" do
393
+ @d2 = Dummy.find(@dummy.id)
394
+
395
+ assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
396
+ [:thumb, :medium, :large, :original].each do |style|
397
+ assert_equal @dummy.avatar.path(style), @d2.avatar.path(style)
398
+ end
399
+
400
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
401
+
402
+ @d2.avatar.clear
403
+ assert @d2.save
404
+
405
+ saved_paths.each do |p|
406
+ assert ! File.exists?(p)
407
+ end
408
+ end
409
+
410
+ should "know the difference between good files, bad files, and not files" do
411
+ expected = @dummy.avatar.to_file
412
+ @dummy.avatar = "not a file"
413
+ assert @dummy.valid?
414
+ assert_equal expected.path, @dummy.avatar.path
415
+ expected.close
416
+
417
+ @dummy.avatar = @bad_file
418
+ assert ! @dummy.valid?
419
+ end
420
+
421
+ should "know the difference between good files, bad files, and not files when validating" do
422
+ Dummy.validates_attachment_presence :avatar
423
+ @d2 = Dummy.find(@dummy.id)
424
+ @d2.avatar = @file
425
+ assert @d2.valid?, @d2.errors.full_messages.inspect
426
+ @d2.avatar = @bad_file
427
+ assert ! @d2.valid?
428
+ end
429
+
430
+ should "be able to reload without saving and not have the file disappear" do
431
+ @dummy.avatar = @file
432
+ assert @dummy.save
433
+ @dummy.avatar.clear
434
+ assert_nil @dummy.avatar_file_name
435
+ @dummy.reload
436
+ assert_equal "5k.png", @dummy.avatar_file_name
437
+ end
438
+
439
+ [000,002,022].each do |umask|
440
+ context "when the umask is #{umask}" do
441
+ setup do
442
+ @umask = File.umask umask
443
+ end
444
+
445
+ teardown do
446
+ File.umask @umask
447
+ end
448
+
449
+ should "respect the current umask" do
450
+ @dummy.avatar = @file
451
+ @dummy.save
452
+ assert_equal 0666&~umask, 0666&File.stat(@dummy.avatar.path).mode
453
+ end
454
+ end
455
+ end
456
+
457
+ context "that is assigned its file from another Paperclip attachment" do
458
+ setup do
459
+ @dummy2 = Dummy.new
460
+ @file2 = File.new(File.join(FIXTURES_DIR, "12k.png"), 'rb')
461
+ assert @dummy2.avatar = @file2
462
+ @dummy2.save
463
+ end
464
+
465
+ should "work when assigned a file" do
466
+ assert_not_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
467
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
468
+
469
+ assert @dummy.avatar = @dummy2.avatar
470
+ @dummy.save
471
+ assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
472
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
473
+ assert_equal @dummy.avatar_file_name, @dummy2.avatar_file_name
474
+ end
475
+ end
476
+
477
+ end
478
+
479
+ context "A model with an attachments association and a Paperclip attachment" do
480
+ setup do
481
+ Dummy.class_eval do
482
+ has_many :attachments, :class_name => 'Dummy'
483
+ end
484
+
485
+ @dummy = Dummy.new
486
+ @dummy.avatar = File.new(File.join(File.dirname(__FILE__),
487
+ "fixtures",
488
+ "5k.png"), 'rb')
489
+ end
490
+
491
+ should "should not error when saving" do
492
+ assert_nothing_raised do
493
+ @dummy.save!
494
+ end
495
+ end
496
+ end
497
+
498
+ if ENV['S3_TEST_BUCKET']
499
+ def s3_files_for attachment
500
+ [:thumb, :medium, :large, :original].inject({}) do |files, style|
501
+ data = `curl "#{attachment.url(style)}" 2>/dev/null`.chomp
502
+ t = Tempfile.new("paperclip-test")
503
+ t.binmode
504
+ t.write(data)
505
+ t.rewind
506
+ files[style] = t
507
+ files
508
+ end
509
+ end
510
+
511
+ def s3_headers_for attachment, style
512
+ `curl --head "#{attachment.url(style)}" 2>/dev/null`.split("\n").inject({}) do |h,head|
513
+ split_head = head.chomp.split(/\s*:\s*/, 2)
514
+ h[split_head.first.downcase] = split_head.last unless split_head.empty?
515
+ h
516
+ end
517
+ end
518
+
519
+ context "A model with an S3 attachment" do
520
+ setup do
521
+ rebuild_model :styles => { :large => "300x300>",
522
+ :medium => "100x100",
523
+ :thumb => ["32x32#", :gif] },
524
+ :storage => :s3,
525
+ :whiny_thumbnails => true,
526
+ :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml")),
527
+ :default_style => :medium,
528
+ :bucket => ENV['S3_TEST_BUCKET'],
529
+ :path => ":class/:attachment/:id/:style/:basename.:extension"
530
+ @dummy = Dummy.new
531
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
532
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
533
+
534
+ assert @dummy.avatar = @file
535
+ assert @dummy.valid?
536
+ assert @dummy.save
537
+
538
+ @files_on_s3 = s3_files_for @dummy.avatar
539
+ end
540
+
541
+ context 'assigning itself to a new model' do
542
+ setup do
543
+ @d2 = Dummy.new
544
+ @d2.avatar = @dummy.avatar
545
+ @d2.save
546
+ end
547
+
548
+ should "have the same name as the old file" do
549
+ assert_equal @d2.avatar.original_filename, @dummy.avatar.original_filename
550
+ end
551
+ end
552
+
553
+ should "have the same contents as the original" do
554
+ @file.rewind
555
+ assert_equal @file.read, @files_on_s3[:original].read
556
+ end
557
+
558
+ should "write and delete its files" do
559
+ [["434x66", :original],
560
+ ["300x46", :large],
561
+ ["100x15", :medium],
562
+ ["32x32", :thumb]].each do |geo, style|
563
+ cmd = %Q[identify -format "%wx%h" "#{@files_on_s3[style].path}"]
564
+ assert_equal geo, `#{cmd}`.chomp, cmd
565
+ end
566
+
567
+ @d2 = Dummy.find(@dummy.id)
568
+ @d2_files = s3_files_for @d2.avatar
569
+ [["434x66", :original],
570
+ ["300x46", :large],
571
+ ["100x15", :medium],
572
+ ["32x32", :thumb]].each do |geo, style|
573
+ cmd = %Q[identify -format "%wx%h" "#{@d2_files[style].path}"]
574
+ assert_equal geo, `#{cmd}`.chomp, cmd
575
+ end
576
+
577
+ @dummy.avatar = "not a valid file but not nil"
578
+ assert_equal File.basename(@file.path), @dummy.avatar_file_name
579
+ assert @dummy.valid?
580
+ assert @dummy.save
581
+
582
+ [:thumb, :medium, :large, :original].each do |style|
583
+ assert @dummy.avatar.exists?(style)
584
+ end
585
+
586
+ @dummy.avatar.clear
587
+ assert_nil @dummy.avatar_file_name
588
+ assert @dummy.valid?
589
+ assert @dummy.save
590
+
591
+ [:thumb, :medium, :large, :original].each do |style|
592
+ assert ! @dummy.avatar.exists?(style)
593
+ end
594
+
595
+ @d2 = Dummy.find(@dummy.id)
596
+ assert_nil @d2.avatar_file_name
597
+ end
598
+
599
+ should "work exactly the same when new as when reloaded" do
600
+ @d2 = Dummy.find(@dummy.id)
601
+
602
+ assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
603
+ [:thumb, :medium, :large, :original].each do |style|
604
+ assert_equal @dummy.avatar.to_file(style).read, @d2.avatar.to_file(style).read
605
+ end
606
+
607
+ saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) }
608
+
609
+ @d2.avatar.clear
610
+ assert @d2.save
611
+
612
+ [:thumb, :medium, :large, :original].each do |style|
613
+ assert ! @dummy.avatar.exists?(style)
614
+ end
615
+ end
616
+
617
+ should "know the difference between good files, bad files, not files, and nil" do
618
+ expected = @dummy.avatar.to_file
619
+ @dummy.avatar = "not a file"
620
+ assert @dummy.valid?
621
+ assert_equal expected.read, @dummy.avatar.to_file.read
622
+
623
+ @dummy.avatar = @bad_file
624
+ assert ! @dummy.valid?
625
+ @dummy.avatar = nil
626
+ assert @dummy.valid?
627
+
628
+ Dummy.validates_attachment_presence :avatar
629
+ @d2 = Dummy.find(@dummy.id)
630
+ @d2.avatar = @file
631
+ assert @d2.valid?
632
+ @d2.avatar = @bad_file
633
+ assert ! @d2.valid?
634
+ @d2.avatar = nil
635
+ assert ! @d2.valid?
636
+ end
637
+
638
+ should "be able to reload without saving and not have the file disappear" do
639
+ @dummy.avatar = @file
640
+ assert @dummy.save
641
+ @dummy.avatar = nil
642
+ assert_nil @dummy.avatar_file_name
643
+ @dummy.reload
644
+ assert_equal "5k.png", @dummy.avatar_file_name
645
+ end
646
+
647
+ should "have the right content type" do
648
+ headers = s3_headers_for(@dummy.avatar, :original)
649
+ assert_equal 'image/png', headers['content-type']
650
+ end
651
+ end
652
+ end
653
+ end
654
+