jmcnevin-paperclip 2.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/LICENSE +26 -0
  2. data/README.md +414 -0
  3. data/Rakefile +86 -0
  4. data/generators/paperclip/USAGE +5 -0
  5. data/generators/paperclip/paperclip_generator.rb +27 -0
  6. data/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
  7. data/init.rb +4 -0
  8. data/lib/generators/paperclip/USAGE +8 -0
  9. data/lib/generators/paperclip/paperclip_generator.rb +33 -0
  10. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
  11. data/lib/paperclip.rb +480 -0
  12. data/lib/paperclip/attachment.rb +520 -0
  13. data/lib/paperclip/callback_compatibility.rb +61 -0
  14. data/lib/paperclip/geometry.rb +155 -0
  15. data/lib/paperclip/interpolations.rb +171 -0
  16. data/lib/paperclip/iostream.rb +45 -0
  17. data/lib/paperclip/matchers.rb +33 -0
  18. data/lib/paperclip/matchers/have_attached_file_matcher.rb +57 -0
  19. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +81 -0
  20. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +54 -0
  21. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +95 -0
  22. data/lib/paperclip/missing_attachment_styles.rb +87 -0
  23. data/lib/paperclip/options.rb +78 -0
  24. data/lib/paperclip/processor.rb +58 -0
  25. data/lib/paperclip/railtie.rb +26 -0
  26. data/lib/paperclip/storage.rb +3 -0
  27. data/lib/paperclip/storage/filesystem.rb +81 -0
  28. data/lib/paperclip/storage/fog.rb +163 -0
  29. data/lib/paperclip/storage/s3.rb +270 -0
  30. data/lib/paperclip/style.rb +95 -0
  31. data/lib/paperclip/thumbnail.rb +105 -0
  32. data/lib/paperclip/upfile.rb +62 -0
  33. data/lib/paperclip/version.rb +3 -0
  34. data/lib/tasks/paperclip.rake +101 -0
  35. data/rails/init.rb +2 -0
  36. data/shoulda_macros/paperclip.rb +124 -0
  37. data/test/attachment_test.rb +1161 -0
  38. data/test/database.yml +4 -0
  39. data/test/fixtures/12k.png +0 -0
  40. data/test/fixtures/50x50.png +0 -0
  41. data/test/fixtures/5k.png +0 -0
  42. data/test/fixtures/animated.gif +0 -0
  43. data/test/fixtures/bad.png +1 -0
  44. data/test/fixtures/double spaces in name.png +0 -0
  45. data/test/fixtures/fog.yml +8 -0
  46. data/test/fixtures/s3.yml +8 -0
  47. data/test/fixtures/spaced file.png +0 -0
  48. data/test/fixtures/text.txt +1 -0
  49. data/test/fixtures/twopage.pdf +0 -0
  50. data/test/fixtures/uppercase.PNG +0 -0
  51. data/test/fog_test.rb +192 -0
  52. data/test/geometry_test.rb +206 -0
  53. data/test/helper.rb +158 -0
  54. data/test/integration_test.rb +781 -0
  55. data/test/interpolations_test.rb +202 -0
  56. data/test/iostream_test.rb +71 -0
  57. data/test/matchers/have_attached_file_matcher_test.rb +24 -0
  58. data/test/matchers/validate_attachment_content_type_matcher_test.rb +87 -0
  59. data/test/matchers/validate_attachment_presence_matcher_test.rb +26 -0
  60. data/test/matchers/validate_attachment_size_matcher_test.rb +51 -0
  61. data/test/options_test.rb +75 -0
  62. data/test/paperclip_missing_attachment_styles_test.rb +80 -0
  63. data/test/paperclip_test.rb +340 -0
  64. data/test/processor_test.rb +10 -0
  65. data/test/storage/filesystem_test.rb +56 -0
  66. data/test/storage/s3_live_test.rb +88 -0
  67. data/test/storage/s3_test.rb +689 -0
  68. data/test/style_test.rb +180 -0
  69. data/test/thumbnail_test.rb +383 -0
  70. data/test/upfile_test.rb +53 -0
  71. metadata +294 -0
@@ -0,0 +1,781 @@
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 no dimension columns" do
335
+ setup do
336
+ rebuild_model
337
+ @dummy = Dummy.new
338
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"))
339
+ end
340
+
341
+ should "not break on image uploads" do
342
+ assert_nothing_raised do
343
+ assert @dummy.avatar = @file
344
+ assert @dummy.save
345
+ end
346
+ end
347
+
348
+ should "return nil when asked for the width/height" do
349
+ @dummy.avatar = @file
350
+ @dummy.save
351
+ assert_nil @dummy.avatar.width
352
+ assert_nil @dummy.avatar.height
353
+ end
354
+ end
355
+
356
+ context "A model with dimension columns" do
357
+ setup do
358
+ rebuild_model :with_dimensions => true
359
+ @dummy = Dummy.new
360
+ @image_file = File.new(File.join(FIXTURES_DIR, "50x50.png"))
361
+ @image_file2 = File.new(File.join(FIXTURES_DIR, "5k.png"))
362
+ @text_file = File.new(File.join(FIXTURES_DIR, "text.txt"))
363
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"))
364
+ end
365
+
366
+ should "return nil when asked for the width/height of a non-image upload" do
367
+ @dummy.avatar = @text_file
368
+ @dummy.save
369
+ assert_nil @dummy.avatar.width
370
+ assert_nil @dummy.avatar.height
371
+ end
372
+
373
+ should "assign dimensions for image uploads" do
374
+ assert @dummy.avatar = @image_file
375
+ assert @dummy.save
376
+ assert_equal 50, @dummy.avatar.width
377
+ assert_equal 50, @dummy.avatar.height
378
+ end
379
+
380
+ should "not break when a bad image is uploaded" do
381
+ assert_nothing_raised do
382
+ @dummy.avatar = @bad_file
383
+ @dummy.save
384
+ end
385
+ end
386
+
387
+ should "return nil width/height when a bad image is uploaded" do
388
+ assert_nothing_raised do
389
+ @dummy.avatar = @bad_file
390
+ @dummy.save
391
+ end
392
+ assert_nil @dummy.avatar.width
393
+ assert_nil @dummy.avatar.height
394
+ end
395
+
396
+ should "change dimensions if changing image upload" do
397
+ @dummy.avatar = @image_file
398
+ @dummy.save
399
+ old_size = `identify -format "%wx%h" #{@dummy.avatar.to_file(:original).path}`.chomp
400
+ assert_equal old_size, "#{@dummy.avatar.width}x#{@dummy.avatar.height}"
401
+ @dummy.avatar = @image_file2
402
+ @dummy.save
403
+ new_size = `identify -format "%wx%h" #{@dummy.avatar.to_file(:original).path}`.chomp
404
+ assert_equal new_size, "#{@dummy.avatar.width}x#{@dummy.avatar.height}"
405
+ assert_not_equal old_size, new_size # sanity check
406
+ end
407
+
408
+ should "unassign dimensions if changing image upload to non-image" do
409
+ @dummy.avatar = @image_file
410
+ @dummy.save
411
+ @dummy.avatar = @text_file
412
+ @dummy.save
413
+ assert_nil @dummy.avatar.width
414
+ assert_nil @dummy.avatar.height
415
+ end
416
+ end
417
+
418
+ context "A model with dimension columns and custom sizes" do
419
+ setup do
420
+ rebuild_model :with_dimensions => true,
421
+ :styles => { :large => "40x30>",
422
+ :medium => "20x20",
423
+ :thumb => ["5x5#", :gif] },
424
+ :default_style => :medium
425
+
426
+ @dummy = Dummy.new
427
+ @file = File.new(File.join(FIXTURES_DIR, "50x50.png"))
428
+ end
429
+
430
+ should "return the default style dimensions" do
431
+ assert @dummy.avatar = @file
432
+ assert @dummy.save
433
+ assert_equal 20, @dummy.avatar.width
434
+ assert_equal 20, @dummy.avatar.height
435
+ end
436
+
437
+ should "return other style dimensions when asked" do
438
+ assert @dummy.avatar = @file
439
+ assert @dummy.save
440
+
441
+ assert_equal 5, @dummy.avatar.width(:thumb)
442
+ assert_equal 5, @dummy.avatar.height(:thumb)
443
+
444
+ assert_equal 30, @dummy.avatar.width(:large)
445
+ assert_equal 30, @dummy.avatar.height(:large)
446
+ end
447
+ end
448
+
449
+ context "A model with a filesystem attachment" do
450
+ setup do
451
+ rebuild_model :styles => { :large => "300x300>",
452
+ :medium => "100x100",
453
+ :thumb => ["32x32#", :gif] },
454
+ :whiny_thumbnails => true,
455
+ :default_style => :medium,
456
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
457
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
458
+ @dummy = Dummy.new
459
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
460
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
461
+ @text_file = File.new(File.join(FIXTURES_DIR, "text.txt"), 'rb')
462
+
463
+ assert @dummy.avatar = @file
464
+ assert @dummy.valid?, @dummy.errors.full_messages.join(", ")
465
+ assert @dummy.save
466
+ end
467
+
468
+ should "write and delete its files" do
469
+ [["434x66", :original],
470
+ ["300x46", :large],
471
+ ["100x15", :medium],
472
+ ["32x32", :thumb]].each do |geo, style|
473
+ cmd = %Q[identify -format "%wx%h" "#{@dummy.avatar.path(style)}"]
474
+ assert_equal geo, `#{cmd}`.chomp, cmd
475
+ end
476
+
477
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
478
+
479
+ @d2 = Dummy.find(@dummy.id)
480
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path}"`.chomp
481
+ assert_equal "434x66", `identify -format "%wx%h" "#{@d2.avatar.path(:original)}"`.chomp
482
+ assert_equal "300x46", `identify -format "%wx%h" "#{@d2.avatar.path(:large)}"`.chomp
483
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path(:medium)}"`.chomp
484
+ assert_equal "32x32", `identify -format "%wx%h" "#{@d2.avatar.path(:thumb)}"`.chomp
485
+
486
+ @dummy.avatar = "not a valid file but not nil"
487
+ assert_equal File.basename(@file.path), @dummy.avatar_file_name
488
+ assert @dummy.valid?
489
+ assert @dummy.save
490
+
491
+ saved_paths.each do |p|
492
+ assert File.exists?(p)
493
+ end
494
+
495
+ @dummy.avatar.clear
496
+ assert_nil @dummy.avatar_file_name
497
+ assert @dummy.valid?
498
+ assert @dummy.save
499
+
500
+ saved_paths.each do |p|
501
+ assert ! File.exists?(p)
502
+ end
503
+
504
+ @d2 = Dummy.find(@dummy.id)
505
+ assert_nil @d2.avatar_file_name
506
+ end
507
+
508
+ should "work exactly the same when new as when reloaded" do
509
+ @d2 = Dummy.find(@dummy.id)
510
+
511
+ assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
512
+ [:thumb, :medium, :large, :original].each do |style|
513
+ assert_equal @dummy.avatar.path(style), @d2.avatar.path(style)
514
+ end
515
+
516
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
517
+
518
+ @d2.avatar.clear
519
+ assert @d2.save
520
+
521
+ saved_paths.each do |p|
522
+ assert ! File.exists?(p)
523
+ end
524
+ end
525
+
526
+ should "know the difference between good files, bad files, and not files" do
527
+ expected = @dummy.avatar.to_file
528
+ @dummy.avatar = "not a file"
529
+ assert @dummy.valid?
530
+ assert_equal expected.path, @dummy.avatar.path
531
+ expected.close
532
+
533
+ @dummy.avatar = @bad_file
534
+ assert ! @dummy.valid?
535
+ end
536
+
537
+ should "properly determine #image?" do
538
+ @dummy.avatar = @file
539
+ assert_equal true, @dummy.avatar.image?
540
+
541
+ @dummy.avatar = @text_file
542
+ assert_equal false, @dummy.avatar.image?
543
+
544
+ @dummy.avatar = "not a file"
545
+ assert_equal false, @dummy.avatar.image?
546
+ end
547
+
548
+ should "know the difference between good files, bad files, and not files when validating" do
549
+ Dummy.validates_attachment_presence :avatar
550
+ @d2 = Dummy.find(@dummy.id)
551
+ @d2.avatar = @file
552
+ assert @d2.valid?, @d2.errors.full_messages.inspect
553
+ @d2.avatar = @bad_file
554
+ assert ! @d2.valid?
555
+ end
556
+
557
+ should "be able to reload without saving and not have the file disappear" do
558
+ @dummy.avatar = @file
559
+ assert @dummy.save
560
+ @dummy.avatar.clear
561
+ assert_nil @dummy.avatar_file_name
562
+ @dummy.reload
563
+ assert_equal "5k.png", @dummy.avatar_file_name
564
+ end
565
+
566
+ [000,002,022].each do |umask|
567
+ context "when the umask is #{umask}" do
568
+ setup do
569
+ @umask = File.umask umask
570
+ end
571
+
572
+ teardown do
573
+ File.umask @umask
574
+ end
575
+
576
+ should "respect the current umask" do
577
+ @dummy.avatar = @file
578
+ @dummy.save
579
+ assert_equal 0666&~umask, 0666&File.stat(@dummy.avatar.path).mode
580
+ end
581
+ end
582
+ end
583
+
584
+ context "that is assigned its file from another Paperclip attachment" do
585
+ setup do
586
+ @dummy2 = Dummy.new
587
+ @file2 = File.new(File.join(FIXTURES_DIR, "12k.png"), 'rb')
588
+ assert @dummy2.avatar = @file2
589
+ @dummy2.save
590
+ end
591
+
592
+ should "work when assigned a file" do
593
+ assert_not_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
594
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
595
+
596
+ assert @dummy.avatar = @dummy2.avatar
597
+ @dummy.save
598
+ assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
599
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
600
+ assert_equal @dummy.avatar_file_name, @dummy2.avatar_file_name
601
+ end
602
+ end
603
+
604
+ end
605
+
606
+ context "A model with an attachments association and a Paperclip attachment" do
607
+ setup do
608
+ Dummy.class_eval do
609
+ has_many :attachments, :class_name => 'Dummy'
610
+ end
611
+
612
+ @dummy = Dummy.new
613
+ @dummy.avatar = File.new(File.join(File.dirname(__FILE__),
614
+ "fixtures",
615
+ "5k.png"), 'rb')
616
+ end
617
+
618
+ should "should not error when saving" do
619
+ assert_nothing_raised do
620
+ @dummy.save!
621
+ end
622
+ end
623
+ end
624
+
625
+ if ENV['S3_TEST_BUCKET']
626
+ def s3_files_for attachment
627
+ [:thumb, :medium, :large, :original].inject({}) do |files, style|
628
+ data = `curl "#{attachment.url(style)}" 2>/dev/null`.chomp
629
+ t = Tempfile.new("paperclip-test")
630
+ t.binmode
631
+ t.write(data)
632
+ t.rewind
633
+ files[style] = t
634
+ files
635
+ end
636
+ end
637
+
638
+ def s3_headers_for attachment, style
639
+ `curl --head "#{attachment.url(style)}" 2>/dev/null`.split("\n").inject({}) do |h,head|
640
+ split_head = head.chomp.split(/\s*:\s*/, 2)
641
+ h[split_head.first.downcase] = split_head.last unless split_head.empty?
642
+ h
643
+ end
644
+ end
645
+
646
+ context "A model with an S3 attachment" do
647
+ setup do
648
+ rebuild_model :styles => { :large => "300x300>",
649
+ :medium => "100x100",
650
+ :thumb => ["32x32#", :gif] },
651
+ :storage => :s3,
652
+ :whiny_thumbnails => true,
653
+ :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml")),
654
+ :default_style => :medium,
655
+ :bucket => ENV['S3_TEST_BUCKET'],
656
+ :path => ":class/:attachment/:id/:style/:basename.:extension"
657
+ @dummy = Dummy.new
658
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
659
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
660
+
661
+ assert @dummy.avatar = @file
662
+ assert @dummy.valid?
663
+ assert @dummy.save
664
+
665
+ @files_on_s3 = s3_files_for @dummy.avatar
666
+ end
667
+
668
+ context 'assigning itself to a new model' do
669
+ setup do
670
+ @d2 = Dummy.new
671
+ @d2.avatar = @dummy.avatar
672
+ @d2.save
673
+ end
674
+
675
+ should "have the same name as the old file" do
676
+ assert_equal @d2.avatar.original_filename, @dummy.avatar.original_filename
677
+ end
678
+ end
679
+
680
+ should "have the same contents as the original" do
681
+ @file.rewind
682
+ assert_equal @file.read, @files_on_s3[:original].read
683
+ end
684
+
685
+ should "write and delete its files" do
686
+ [["434x66", :original],
687
+ ["300x46", :large],
688
+ ["100x15", :medium],
689
+ ["32x32", :thumb]].each do |geo, style|
690
+ cmd = %Q[identify -format "%wx%h" "#{@files_on_s3[style].path}"]
691
+ assert_equal geo, `#{cmd}`.chomp, cmd
692
+ end
693
+
694
+ @d2 = Dummy.find(@dummy.id)
695
+ @d2_files = s3_files_for @d2.avatar
696
+ [["434x66", :original],
697
+ ["300x46", :large],
698
+ ["100x15", :medium],
699
+ ["32x32", :thumb]].each do |geo, style|
700
+ cmd = %Q[identify -format "%wx%h" "#{@d2_files[style].path}"]
701
+ assert_equal geo, `#{cmd}`.chomp, cmd
702
+ end
703
+
704
+ @dummy.avatar = "not a valid file but not nil"
705
+ assert_equal File.basename(@file.path), @dummy.avatar_file_name
706
+ assert @dummy.valid?
707
+ assert @dummy.save
708
+
709
+ [:thumb, :medium, :large, :original].each do |style|
710
+ assert @dummy.avatar.exists?(style)
711
+ end
712
+
713
+ @dummy.avatar.clear
714
+ assert_nil @dummy.avatar_file_name
715
+ assert @dummy.valid?
716
+ assert @dummy.save
717
+
718
+ [:thumb, :medium, :large, :original].each do |style|
719
+ assert ! @dummy.avatar.exists?(style)
720
+ end
721
+
722
+ @d2 = Dummy.find(@dummy.id)
723
+ assert_nil @d2.avatar_file_name
724
+ end
725
+
726
+ should "work exactly the same when new as when reloaded" do
727
+ @d2 = Dummy.find(@dummy.id)
728
+
729
+ assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
730
+ [:thumb, :medium, :large, :original].each do |style|
731
+ assert_equal @dummy.avatar.to_file(style).read, @d2.avatar.to_file(style).read
732
+ end
733
+
734
+ saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) }
735
+
736
+ @d2.avatar.clear
737
+ assert @d2.save
738
+
739
+ [:thumb, :medium, :large, :original].each do |style|
740
+ assert ! @dummy.avatar.exists?(style)
741
+ end
742
+ end
743
+
744
+ should "know the difference between good files, bad files, not files, and nil" do
745
+ expected = @dummy.avatar.to_file
746
+ @dummy.avatar = "not a file"
747
+ assert @dummy.valid?
748
+ assert_equal expected.read, @dummy.avatar.to_file.read
749
+
750
+ @dummy.avatar = @bad_file
751
+ assert ! @dummy.valid?
752
+ @dummy.avatar = nil
753
+ assert @dummy.valid?
754
+
755
+ Dummy.validates_attachment_presence :avatar
756
+ @d2 = Dummy.find(@dummy.id)
757
+ @d2.avatar = @file
758
+ assert @d2.valid?
759
+ @d2.avatar = @bad_file
760
+ assert ! @d2.valid?
761
+ @d2.avatar = nil
762
+ assert ! @d2.valid?
763
+ end
764
+
765
+ should "be able to reload without saving and not have the file disappear" do
766
+ @dummy.avatar = @file
767
+ assert @dummy.save
768
+ @dummy.avatar = nil
769
+ assert_nil @dummy.avatar_file_name
770
+ @dummy.reload
771
+ assert_equal "5k.png", @dummy.avatar_file_name
772
+ end
773
+
774
+ should "have the right content type" do
775
+ headers = s3_headers_for(@dummy.avatar, :original)
776
+ assert_equal 'image/png', headers['content-type']
777
+ end
778
+ end
779
+ end
780
+ end
781
+