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,1161 @@
1
+ # encoding: utf-8
2
+ require './test/helper'
3
+
4
+ class Dummy
5
+ # This is a dummy class
6
+ end
7
+
8
+ class AttachmentTest < Test::Unit::TestCase
9
+ should "return the path based on the url by default" do
10
+ @attachment = attachment :url => "/:class/:id/:basename"
11
+ @model = @attachment.instance
12
+ @model.id = 1234
13
+ @model.avatar_file_name = "fake.jpg"
14
+ assert_equal "#{Rails.root}/public/fake_models/1234/fake", @attachment.path
15
+ end
16
+
17
+ should "return the url by interpolating the default_url option when no file assigned" do
18
+ @attachment = attachment :default_url => ":class/blegga.png"
19
+ @model = @attachment.instance
20
+ assert_nil @model.avatar_file_name
21
+ assert_equal "fake_models/blegga.png", @attachment.url
22
+ end
23
+
24
+ should "return the url by executing and interpolating the default_url Proc when no file assigned" do
25
+ @attachment = attachment :default_url => lambda { |a| ":class/blegga.png" }
26
+ @model = @attachment.instance
27
+ assert_nil @model.avatar_file_name
28
+ assert_equal "fake_models/blegga.png", @attachment.url
29
+ end
30
+
31
+ should "return the url by executing and interpolating the default_url Proc with attachment arg when no file assigned" do
32
+ @attachment = attachment :default_url => lambda { |a| a.instance.some_method_to_determine_default_url }
33
+ @model = @attachment.instance
34
+ @model.stubs(:some_method_to_determine_default_url).returns(":class/blegga.png")
35
+ assert_nil @model.avatar_file_name
36
+ assert_equal "fake_models/blegga.png", @attachment.url
37
+ end
38
+
39
+ should "return the url by executing and interpolating the default_url when assigned with symbol as method in attachment model" do
40
+ @attachment = attachment :default_url => :some_method_to_determine_default_url
41
+ @model = @attachment.instance
42
+ @model.stubs(:some_method_to_determine_default_url).returns(":class/female_:style_blegga.png")
43
+ assert_equal "fake_models/female_foostyle_blegga.png", @attachment.url(:foostyle)
44
+ end
45
+
46
+ context "Attachment default_options" do
47
+ setup do
48
+ rebuild_model
49
+ @old_default_options = Paperclip::Attachment.default_options.dup
50
+ @new_default_options = @old_default_options.merge({
51
+ :path => "argle/bargle",
52
+ :url => "fooferon",
53
+ :default_url => "not here.png"
54
+ })
55
+ end
56
+
57
+ teardown do
58
+ Paperclip::Attachment.default_options.merge! @old_default_options
59
+ end
60
+
61
+ should "be overrideable" do
62
+ Paperclip::Attachment.default_options.merge!(@new_default_options)
63
+ @new_default_options.keys.each do |key|
64
+ assert_equal @new_default_options[key],
65
+ Paperclip::Attachment.default_options[key]
66
+ end
67
+ end
68
+
69
+ context "without an Attachment" do
70
+ setup do
71
+ @dummy = Dummy.new
72
+ end
73
+
74
+ should "return false when asked exists?" do
75
+ assert !@dummy.avatar.exists?
76
+ end
77
+ end
78
+
79
+ context "on an Attachment" do
80
+ setup do
81
+ @dummy = Dummy.new
82
+ @attachment = @dummy.avatar
83
+ end
84
+
85
+ Paperclip::Attachment.default_options.keys.each do |key|
86
+ should "be the default_options for #{key}" do
87
+ assert_equal @old_default_options[key],
88
+ @attachment.options.send(key),
89
+ key
90
+ end
91
+ end
92
+
93
+ context "when redefined" do
94
+ setup do
95
+ Paperclip::Attachment.default_options.merge!(@new_default_options)
96
+ @dummy = Dummy.new
97
+ @attachment = @dummy.avatar
98
+ end
99
+
100
+ Paperclip::Attachment.default_options.keys.each do |key|
101
+ should "be the new default_options for #{key}" do
102
+ assert_equal @new_default_options[key],
103
+ @attachment.options.send(key),
104
+ key
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ context "An attachment with similarly named interpolations" do
112
+ setup do
113
+ rebuild_model :path => ":id.omg/:id-bbq/:idwhat/:id_partition.wtf"
114
+ @dummy = Dummy.new
115
+ @dummy.stubs(:id).returns(1024)
116
+ @file = File.new(File.join(File.dirname(__FILE__),
117
+ "fixtures",
118
+ "5k.png"), 'rb')
119
+ @dummy.avatar = @file
120
+ end
121
+
122
+ teardown { @file.close }
123
+
124
+ should "make sure that they are interpolated correctly" do
125
+ assert_equal "1024.omg/1024-bbq/1024what/000/001/024.wtf", @dummy.avatar.path
126
+ end
127
+ end
128
+
129
+ context "An attachment with :timestamp interpolations" do
130
+ setup do
131
+ @file = StringIO.new("...")
132
+ @zone = 'UTC'
133
+ Time.stubs(:zone).returns(@zone)
134
+ @zone_default = 'Eastern Time (US & Canada)'
135
+ Time.stubs(:zone_default).returns(@zone_default)
136
+ end
137
+
138
+ context "using default time zone" do
139
+ setup do
140
+ rebuild_model :path => ":timestamp", :use_default_time_zone => true
141
+ @dummy = Dummy.new
142
+ @dummy.avatar = @file
143
+ end
144
+
145
+ should "return a time in the default zone" do
146
+ assert_equal @dummy.avatar_updated_at.in_time_zone(@zone_default).to_s, @dummy.avatar.path
147
+ end
148
+ end
149
+
150
+ context "using per-thread time zone" do
151
+ setup do
152
+ rebuild_model :path => ":timestamp", :use_default_time_zone => false
153
+ @dummy = Dummy.new
154
+ @dummy.avatar = @file
155
+ end
156
+
157
+ should "return a time in the per-thread zone" do
158
+ assert_equal @dummy.avatar_updated_at.in_time_zone(@zone).to_s, @dummy.avatar.path
159
+ end
160
+ end
161
+ end
162
+
163
+ context "An attachment" do
164
+ setup do
165
+ @file = StringIO.new("...")
166
+ end
167
+
168
+ context "using default time zone" do
169
+ setup do
170
+ rebuild_model :url => "X"
171
+ @dummy = Dummy.new
172
+ @dummy.avatar = @file
173
+ end
174
+
175
+ should "generate a url with a timestamp when passing true" do
176
+ assert_equal "X?#{@dummy.avatar_updated_at.to_i.to_s}", @dummy.avatar.url(:style, true)
177
+ end
178
+
179
+ should "not generate a url with a timestamp when passing false" do
180
+ assert_equal "X", @dummy.avatar.url(:style, false)
181
+ end
182
+
183
+ should "generate a url with a timestamp when setting a timestamp option" do
184
+ assert_equal "X?#{@dummy.avatar_updated_at.to_i.to_s}", @dummy.avatar.url(:style, :timestamp => true)
185
+ end
186
+
187
+ should "not generate a url with a timestamp when setting a timestamp option to false" do
188
+ assert_equal "X", @dummy.avatar.url(:style, :timestamp => false)
189
+ end
190
+ end
191
+ end
192
+
193
+ context "An attachment with :hash interpolations" do
194
+ setup do
195
+ @file = StringIO.new("...")
196
+ end
197
+
198
+ should "raise if no secret is provided" do
199
+ @attachment = attachment :path => ":hash"
200
+ @attachment.assign @file
201
+
202
+ assert_raise ArgumentError do
203
+ @attachment.path
204
+ end
205
+ end
206
+
207
+ context "when secret is set" do
208
+ setup do
209
+ @attachment = attachment :path => ":hash", :hash_secret => "w00t"
210
+ @attachment.stubs(:instance_read).with(:updated_at).returns(Time.at(1234567890))
211
+ @attachment.stubs(:instance_read).with(:file_name).returns("bla.txt")
212
+ @attachment.stubs(:instance_read).with(:content_type).returns("text/plain")
213
+ @attachment.instance.id = 1234
214
+ @attachment.assign @file
215
+ end
216
+
217
+ should "interpolate the hash data" do
218
+ @attachment.expects(:interpolate).with(@attachment.options.hash_data,anything).returns("interpolated_stuff")
219
+ @attachment.hash
220
+ end
221
+
222
+ should "result in the correct interpolation" do
223
+ assert_equal "fake_models/avatars/1234/original/1234567890", @attachment.send(:interpolate,@attachment.options.hash_data)
224
+ end
225
+
226
+ should "result in a correct hash" do
227
+ assert_equal "d22b617d1bf10016aa7d046d16427ae203f39fce", @attachment.path
228
+ end
229
+
230
+ should "generate a hash digest with the correct style" do
231
+ OpenSSL::HMAC.expects(:hexdigest).with(anything, anything, "fake_models/avatars/1234/medium/1234567890")
232
+ @attachment.path("medium")
233
+ end
234
+ end
235
+ end
236
+
237
+ context "An attachment with a :rails_env interpolation" do
238
+ setup do
239
+ @rails_env = "blah"
240
+ @id = 1024
241
+ rebuild_model :path => ":rails_env/:id.png"
242
+ @dummy = Dummy.new
243
+ @dummy.stubs(:id).returns(@id)
244
+ @file = StringIO.new(".")
245
+ @dummy.avatar = @file
246
+ Rails.stubs(:env).returns(@rails_env)
247
+ end
248
+
249
+ should "return the proper path" do
250
+ assert_equal "#{@rails_env}/#{@id}.png", @dummy.avatar.path
251
+ end
252
+ end
253
+
254
+ context "An attachment with a default style and an extension interpolation" do
255
+ setup do
256
+ @attachment = attachment :path => ":basename.:extension",
257
+ :styles => { :default => ["100x100", :png] },
258
+ :default_style => :default
259
+ @file = StringIO.new("...")
260
+ @file.stubs(:original_filename).returns("file.jpg")
261
+ end
262
+ should "return the right extension for the path" do
263
+ @attachment.assign(@file)
264
+ assert_equal "file.png", @attachment.path
265
+ end
266
+ end
267
+
268
+ context "An attachment with :convert_options" do
269
+ setup do
270
+ rebuild_model :styles => {
271
+ :thumb => "100x100",
272
+ :large => "400x400"
273
+ },
274
+ :convert_options => {
275
+ :all => "-do_stuff",
276
+ :thumb => "-thumbnailize"
277
+ }
278
+ @dummy = Dummy.new
279
+ @dummy.avatar
280
+ end
281
+
282
+ should "report the correct options when sent #extra_options_for(:thumb)" do
283
+ assert_equal "-thumbnailize -do_stuff", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
284
+ end
285
+
286
+ should "report the correct options when sent #extra_options_for(:large)" do
287
+ assert_equal "-do_stuff", @dummy.avatar.send(:extra_options_for, :large)
288
+ end
289
+ end
290
+
291
+ context "An attachment with :source_file_options" do
292
+ setup do
293
+ rebuild_model :styles => {
294
+ :thumb => "100x100",
295
+ :large => "400x400"
296
+ },
297
+ :source_file_options => {
298
+ :all => "-density 400",
299
+ :thumb => "-depth 8"
300
+ }
301
+ @dummy = Dummy.new
302
+ @dummy.avatar
303
+ end
304
+
305
+ should "report the correct options when sent #extra_source_file_options_for(:thumb)" do
306
+ assert_equal "-depth 8 -density 400", @dummy.avatar.send(:extra_source_file_options_for, :thumb), @dummy.avatar.options.source_file_options.inspect
307
+ end
308
+
309
+ should "report the correct options when sent #extra_source_file_options_for(:large)" do
310
+ assert_equal "-density 400", @dummy.avatar.send(:extra_source_file_options_for, :large)
311
+ end
312
+ end
313
+
314
+ context "An attachment with :only_process" do
315
+ setup do
316
+ rebuild_model :styles => {
317
+ :thumb => "100x100",
318
+ :large => "400x400"
319
+ },
320
+ :only_process => [:thumb]
321
+ @file = StringIO.new("...")
322
+ @attachment = Dummy.new.avatar
323
+ end
324
+
325
+ should "only process the provided style" do
326
+ @attachment.expects(:post_process).with(:thumb)
327
+ @attachment.expects(:post_process).with(:large).never
328
+ @attachment.assign(@file)
329
+ end
330
+ end
331
+
332
+ context "An attachment with :convert_options that is a proc" do
333
+ setup do
334
+ rebuild_model :styles => {
335
+ :thumb => "100x100",
336
+ :large => "400x400"
337
+ },
338
+ :convert_options => {
339
+ :all => lambda{|i| i.all },
340
+ :thumb => lambda{|i| i.thumb }
341
+ }
342
+ Dummy.class_eval do
343
+ def all; "-all"; end
344
+ def thumb; "-thumb"; end
345
+ end
346
+ @dummy = Dummy.new
347
+ @dummy.avatar
348
+ end
349
+
350
+ should "report the correct options when sent #extra_options_for(:thumb)" do
351
+ assert_equal "-thumb -all", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
352
+ end
353
+
354
+ should "report the correct options when sent #extra_options_for(:large)" do
355
+ assert_equal "-all", @dummy.avatar.send(:extra_options_for, :large)
356
+ end
357
+ end
358
+
359
+ context "An attachment with :path that is a proc" do
360
+ setup do
361
+ rebuild_model :path => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
362
+
363
+ @file = File.new(File.join(File.dirname(__FILE__),
364
+ "fixtures",
365
+ "5k.png"), 'rb')
366
+ @dummyA = Dummy.new(:other => 'a')
367
+ @dummyA.avatar = @file
368
+ @dummyB = Dummy.new(:other => 'b')
369
+ @dummyB.avatar = @file
370
+ end
371
+
372
+ teardown { @file.close }
373
+
374
+ should "return correct path" do
375
+ assert_equal "path/a.png", @dummyA.avatar.path
376
+ assert_equal "path/b.png", @dummyB.avatar.path
377
+ end
378
+ end
379
+
380
+ context "An attachment with :styles that is a proc" do
381
+ setup do
382
+ rebuild_model :styles => lambda{ |attachment| {:thumb => "50x50#", :large => "400x400"} }
383
+
384
+ @attachment = Dummy.new.avatar
385
+ end
386
+
387
+ should "have the correct geometry" do
388
+ assert_equal "50x50#", @attachment.options.styles[:thumb][:geometry]
389
+ end
390
+ end
391
+
392
+ context "An attachment with conditional :styles that is a proc" do
393
+ setup do
394
+ rebuild_model :styles => lambda{ |attachment| attachment.instance.other == 'a' ? {:thumb => "50x50#"} : {:large => "400x400"} }
395
+
396
+ @dummy = Dummy.new(:other => 'a')
397
+ end
398
+
399
+ should "have the correct styles for the assigned instance values" do
400
+ assert_equal "50x50#", @dummy.avatar.options.styles[:thumb][:geometry]
401
+ assert_nil @dummy.avatar.options.styles[:large]
402
+
403
+ @dummy.other = 'b'
404
+
405
+ assert_equal "400x400", @dummy.avatar.options.styles[:large][:geometry]
406
+ assert_nil @dummy.avatar.options.styles[:thumb]
407
+ end
408
+ end
409
+
410
+ context "An attachment with :url that is a proc" do
411
+ setup do
412
+ rebuild_model :url => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
413
+
414
+ @file = File.new(File.join(File.dirname(__FILE__),
415
+ "fixtures",
416
+ "5k.png"), 'rb')
417
+ @dummyA = Dummy.new(:other => 'a')
418
+ @dummyA.avatar = @file
419
+ @dummyB = Dummy.new(:other => 'b')
420
+ @dummyB.avatar = @file
421
+ end
422
+
423
+ teardown { @file.close }
424
+
425
+ should "return correct url" do
426
+ assert_equal "path/a.png", @dummyA.avatar.url(:original, false)
427
+ assert_equal "path/b.png", @dummyB.avatar.url(:original, false)
428
+ end
429
+ end
430
+
431
+ geometry_specs = [
432
+ [ lambda{|z| "50x50#" }, :png ],
433
+ lambda{|z| "50x50#" },
434
+ { :geometry => lambda{|z| "50x50#" } }
435
+ ]
436
+ geometry_specs.each do |geometry_spec|
437
+ context "An attachment geometry like #{geometry_spec}" do
438
+ setup do
439
+ rebuild_model :styles => { :normal => geometry_spec }
440
+ @attachment = Dummy.new.avatar
441
+ end
442
+
443
+ context "when assigned" do
444
+ setup do
445
+ @file = StringIO.new(".")
446
+ @attachment.assign(@file)
447
+ end
448
+
449
+ should "have the correct geometry" do
450
+ assert_equal "50x50#", @attachment.options.styles[:normal][:geometry]
451
+ end
452
+ end
453
+ end
454
+ end
455
+
456
+ context "An attachment with both 'normal' and hash-style styles" do
457
+ setup do
458
+ rebuild_model :styles => {
459
+ :normal => ["50x50#", :png],
460
+ :hash => { :geometry => "50x50#", :format => :png }
461
+ }
462
+ @dummy = Dummy.new
463
+ @attachment = @dummy.avatar
464
+ end
465
+
466
+ [:processors, :whiny, :convert_options, :geometry, :format].each do |field|
467
+ should "have the same #{field} field" do
468
+ assert_equal @attachment.options.styles[:normal][field], @attachment.options.styles[:hash][field]
469
+ end
470
+ end
471
+ end
472
+
473
+ context "An attachment with :processors that is a proc" do
474
+ setup do
475
+ class Paperclip::Test < Paperclip::Processor; end
476
+ @file = StringIO.new("...")
477
+ Paperclip::Test.stubs(:make).returns(@file)
478
+
479
+ rebuild_model :styles => { :normal => '' }, :processors => lambda { |a| [ :test ] }
480
+ @attachment = Dummy.new.avatar
481
+ end
482
+
483
+ context "when assigned" do
484
+ setup do
485
+ @attachment.assign(StringIO.new("."))
486
+ end
487
+
488
+ should "have the correct processors" do
489
+ assert_equal [ :test ], @attachment.options.styles[:normal][:processors]
490
+ end
491
+ end
492
+ end
493
+
494
+ context "An attachment with erroring processor" do
495
+ setup do
496
+ rebuild_model :processor => [:thumbnail], :styles => { :small => '' }, :whiny_thumbnails => true
497
+ @dummy = Dummy.new
498
+ Paperclip::Thumbnail.expects(:make).raises(Paperclip::PaperclipError, "cannot be processed.")
499
+ @file = StringIO.new("...")
500
+ @file.stubs(:to_tempfile).returns(@file)
501
+ @dummy.avatar = @file
502
+ end
503
+
504
+ should "correctly forward processing error message to the instance" do
505
+ @dummy.valid?
506
+ assert_contains @dummy.errors.full_messages, "Avatar cannot be processed."
507
+ end
508
+ end
509
+
510
+ context "An attachment with multiple processors" do
511
+ setup do
512
+ class Paperclip::Test < Paperclip::Processor; end
513
+ @style_params = { :once => {:one => 1, :two => 2} }
514
+ rebuild_model :processors => [:thumbnail, :test], :styles => @style_params
515
+ @dummy = Dummy.new
516
+ @file = StringIO.new("...")
517
+ @file.stubs(:to_tempfile).returns(@file)
518
+ Paperclip::Test.stubs(:make).returns(@file)
519
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
520
+ end
521
+
522
+ context "when assigned" do
523
+ setup { @dummy.avatar = @file }
524
+
525
+ before_should "call #make on all specified processors" do
526
+ Paperclip::Thumbnail.expects(:make).with(any_parameters).returns(@file)
527
+ Paperclip::Test.expects(:make).with(any_parameters).returns(@file)
528
+ end
529
+
530
+ before_should "call #make with the right parameters passed as second argument" do
531
+ expected_params = @style_params[:once].merge({
532
+ :processors => [:thumbnail, :test],
533
+ :whiny => true,
534
+ :convert_options => "",
535
+ :source_file_options => ""
536
+ })
537
+ Paperclip::Thumbnail.expects(:make).with(anything, expected_params, anything).returns(@file)
538
+ end
539
+
540
+ before_should "call #make with attachment passed as third argument" do
541
+ Paperclip::Test.expects(:make).with(anything, anything, @dummy.avatar).returns(@file)
542
+ end
543
+ end
544
+ end
545
+
546
+ should "include the filesystem module when loading the filesystem storage" do
547
+ rebuild_model :storage => :filesystem
548
+ @dummy = Dummy.new
549
+ assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
550
+ end
551
+
552
+ should "include the filesystem module even if capitalization is wrong" do
553
+ rebuild_model :storage => :FileSystem
554
+ @dummy = Dummy.new
555
+ assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
556
+
557
+ rebuild_model :storage => :Filesystem
558
+ @dummy = Dummy.new
559
+ assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
560
+ end
561
+
562
+ should "convert underscored storage name to camelcase" do
563
+ rebuild_model :storage => :not_here
564
+ @dummy = Dummy.new
565
+ exception = assert_raises(Paperclip::StorageMethodNotFound) do |e|
566
+ @dummy.avatar
567
+ end
568
+ assert exception.message.include?("NotHere")
569
+ end
570
+
571
+ should "raise an error if you try to include a storage module that doesn't exist" do
572
+ rebuild_model :storage => :not_here
573
+ @dummy = Dummy.new
574
+ assert_raises(Paperclip::StorageMethodNotFound) do
575
+ @dummy.avatar
576
+ end
577
+ end
578
+
579
+ context "An attachment with styles but no processors defined" do
580
+ setup do
581
+ rebuild_model :processors => [], :styles => {:something => '1'}
582
+ @dummy = Dummy.new
583
+ @file = StringIO.new("...")
584
+ end
585
+ should "raise when assigned to" do
586
+ assert_raises(RuntimeError){ @dummy.avatar = @file }
587
+ end
588
+ end
589
+
590
+ context "An attachment without styles and with no processors defined" do
591
+ setup do
592
+ rebuild_model :processors => [], :styles => {}
593
+ @dummy = Dummy.new
594
+ @file = StringIO.new("...")
595
+ end
596
+ should "not raise when assigned to" do
597
+ @dummy.avatar = @file
598
+ end
599
+ end
600
+
601
+ context "Assigning an attachment with post_process hooks" do
602
+ setup do
603
+ rebuild_class :styles => { :something => "100x100#" }
604
+ Dummy.class_eval do
605
+ before_avatar_post_process :do_before_avatar
606
+ after_avatar_post_process :do_after_avatar
607
+ before_post_process :do_before_all
608
+ after_post_process :do_after_all
609
+ def do_before_avatar; end
610
+ def do_after_avatar; end
611
+ def do_before_all; end
612
+ def do_after_all; end
613
+ end
614
+ @file = StringIO.new(".")
615
+ @file.stubs(:to_tempfile).returns(@file)
616
+ @dummy = Dummy.new
617
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
618
+ @attachment = @dummy.avatar
619
+ end
620
+
621
+ should "call the defined callbacks when assigned" do
622
+ @dummy.expects(:do_before_avatar).with()
623
+ @dummy.expects(:do_after_avatar).with()
624
+ @dummy.expects(:do_before_all).with()
625
+ @dummy.expects(:do_after_all).with()
626
+ Paperclip::Thumbnail.expects(:make).returns(@file)
627
+ @dummy.avatar = @file
628
+ end
629
+
630
+ should "not cancel the processing if a before_post_process returns nil" do
631
+ @dummy.expects(:do_before_avatar).with().returns(nil)
632
+ @dummy.expects(:do_after_avatar).with()
633
+ @dummy.expects(:do_before_all).with().returns(nil)
634
+ @dummy.expects(:do_after_all).with()
635
+ Paperclip::Thumbnail.expects(:make).returns(@file)
636
+ @dummy.avatar = @file
637
+ end
638
+
639
+ should "cancel the processing if a before_post_process returns false" do
640
+ @dummy.expects(:do_before_avatar).never
641
+ @dummy.expects(:do_after_avatar).never
642
+ @dummy.expects(:do_before_all).with().returns(false)
643
+ @dummy.expects(:do_after_all)
644
+ Paperclip::Thumbnail.expects(:make).never
645
+ @dummy.avatar = @file
646
+ end
647
+
648
+ should "cancel the processing if a before_avatar_post_process returns false" do
649
+ @dummy.expects(:do_before_avatar).with().returns(false)
650
+ @dummy.expects(:do_after_avatar)
651
+ @dummy.expects(:do_before_all).with().returns(true)
652
+ @dummy.expects(:do_after_all)
653
+ Paperclip::Thumbnail.expects(:make).never
654
+ @dummy.avatar = @file
655
+ end
656
+ end
657
+
658
+ context "Assigning an attachment" do
659
+ setup do
660
+ rebuild_model :styles => { :something => "100x100#" }
661
+ @file = StringIO.new(".")
662
+ @file.stubs(:original_filename).returns("5k.png\n\n")
663
+ @file.stubs(:content_type).returns("image/png\n\n")
664
+ @file.stubs(:to_tempfile).returns(@file)
665
+ @dummy = Dummy.new
666
+ Paperclip::Thumbnail.expects(:make).returns(@file)
667
+ @attachment = @dummy.avatar
668
+ @dummy.avatar = @file
669
+ end
670
+
671
+ should "strip whitespace from original_filename field" do
672
+ assert_equal "5k.png", @dummy.avatar.original_filename
673
+ end
674
+
675
+ should "strip whitespace from content_type field" do
676
+ assert_equal "image/png", @dummy.avatar.instance.avatar_content_type
677
+ end
678
+ end
679
+
680
+ context "Attachment with strange letters" do
681
+ setup do
682
+ rebuild_model
683
+
684
+ @not_file = mock("not_file")
685
+ @tempfile = mock("tempfile")
686
+ @not_file.stubs(:nil?).returns(false)
687
+ @not_file.expects(:size).returns(10)
688
+ @tempfile.expects(:size).returns(10)
689
+ @not_file.expects(:original_filename).returns("sheep_say_bæ.png\r\n")
690
+ @not_file.expects(:content_type).returns("image/png\r\n")
691
+
692
+ @dummy = Dummy.new
693
+ @attachment = @dummy.avatar
694
+ @attachment.expects(:valid_assignment?).with(@not_file).returns(true)
695
+ @attachment.expects(:queue_existing_for_delete)
696
+ @attachment.expects(:post_process)
697
+ @attachment.expects(:to_tempfile).returns(@tempfile)
698
+ @attachment.expects(:generate_fingerprint).with(@tempfile).returns("12345")
699
+ @attachment.expects(:generate_fingerprint).with(@not_file).returns("12345")
700
+ @dummy.avatar = @not_file
701
+ end
702
+
703
+ should "not remove strange letters" do
704
+ assert_equal "sheep_say_bæ.png", @dummy.avatar.original_filename
705
+ end
706
+ end
707
+
708
+ context "Attachment with uppercase extension and a default style" do
709
+ setup do
710
+ @old_defaults = Paperclip::Attachment.default_options.dup
711
+ Paperclip::Attachment.default_options.merge!({
712
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
713
+ })
714
+ FileUtils.rm_rf("tmp")
715
+ rebuild_model
716
+ @instance = Dummy.new
717
+ @instance.stubs(:id).returns 123
718
+
719
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "uppercase.PNG"), 'rb')
720
+
721
+ styles = {:styles => { :large => ["400x400", :jpg],
722
+ :medium => ["100x100", :jpg],
723
+ :small => ["32x32#", :jpg]},
724
+ :default_style => :small}
725
+ @attachment = Paperclip::Attachment.new(:avatar,
726
+ @instance,
727
+ styles)
728
+ now = Time.now
729
+ Time.stubs(:now).returns(now)
730
+ @attachment.assign(@file)
731
+ @attachment.save
732
+ end
733
+
734
+ teardown do
735
+ @file.close
736
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
737
+ end
738
+
739
+ should "should have matching to_s and url methods" do
740
+ file = @attachment.to_file
741
+ assert file
742
+ assert_match @attachment.to_s, @attachment.url
743
+ assert_match @attachment.to_s(:small), @attachment.url(:small)
744
+ file.close
745
+ end
746
+ end
747
+
748
+ context "An attachment" do
749
+ setup do
750
+ @old_defaults = Paperclip::Attachment.default_options.dup
751
+ Paperclip::Attachment.default_options.merge!({
752
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
753
+ })
754
+ FileUtils.rm_rf("tmp")
755
+ rebuild_model
756
+ @instance = Dummy.new
757
+ @instance.stubs(:id).returns 123
758
+ @attachment = Paperclip::Attachment.new(:avatar, @instance)
759
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
760
+ end
761
+
762
+ teardown do
763
+ @file.close
764
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
765
+ end
766
+
767
+ should "raise if there are not the correct columns when you try to assign" do
768
+ @other_attachment = Paperclip::Attachment.new(:not_here, @instance)
769
+ assert_raises(Paperclip::PaperclipError) do
770
+ @other_attachment.assign(@file)
771
+ end
772
+ end
773
+
774
+ should "return its default_url when no file assigned" do
775
+ assert @attachment.to_file.nil?
776
+ assert_equal "/avatars/original/missing.png", @attachment.url
777
+ assert_equal "/avatars/blah/missing.png", @attachment.url(:blah)
778
+ end
779
+
780
+ should "return nil as path when no file assigned" do
781
+ assert @attachment.to_file.nil?
782
+ assert_equal nil, @attachment.path
783
+ assert_equal nil, @attachment.path(:blah)
784
+ end
785
+
786
+ context "with a file assigned but not saved yet" do
787
+ should "clear out any attached files" do
788
+ @attachment.assign(@file)
789
+ assert !@attachment.queued_for_write.blank?
790
+ @attachment.clear
791
+ assert @attachment.queued_for_write.blank?
792
+ end
793
+ end
794
+
795
+ context "with a file assigned in the database" do
796
+ setup do
797
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
798
+ @attachment.stubs(:instance_read).with(:content_type).returns("image/png")
799
+ @attachment.stubs(:instance_read).with(:file_size).returns(12345)
800
+ dtnow = DateTime.now
801
+ @now = Time.now
802
+ Time.stubs(:now).returns(@now)
803
+ @attachment.stubs(:instance_read).with(:updated_at).returns(dtnow)
804
+ end
805
+
806
+ should "return a correct url even if the file does not exist" do
807
+ assert_nil @attachment.to_file
808
+ assert_match %r{^/system/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah)
809
+ end
810
+
811
+ should "make sure the updated_at mtime is in the url if it is defined" do
812
+ assert_match %r{#{@now.to_i}$}, @attachment.url(:blah)
813
+ end
814
+
815
+ should "make sure the updated_at mtime is NOT in the url if false is passed to the url method" do
816
+ assert_no_match %r{#{@now.to_i}$}, @attachment.url(:blah, false)
817
+ end
818
+
819
+ context "with the updated_at field removed" do
820
+ setup do
821
+ @attachment.stubs(:instance_read).with(:updated_at).returns(nil)
822
+ end
823
+
824
+ should "only return the url without the updated_at when sent #url" do
825
+ assert_match "/avatars/#{@instance.id}/blah/5k.png", @attachment.url(:blah)
826
+ end
827
+ end
828
+
829
+ should "return the proper path when filename has a single .'s" do
830
+ assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
831
+ end
832
+
833
+ should "return the proper path when filename has multiple .'s" do
834
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.old.png")
835
+ assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png"), File.expand_path(@attachment.path)
836
+ end
837
+
838
+ context "when expecting three styles" do
839
+ setup do
840
+ styles = {:styles => { :large => ["400x400", :png],
841
+ :medium => ["100x100", :gif],
842
+ :small => ["32x32#", :jpg]}}
843
+ @attachment = Paperclip::Attachment.new(:avatar,
844
+ @instance,
845
+ styles)
846
+ end
847
+
848
+ context "and assigned a file" do
849
+ setup do
850
+ now = Time.now
851
+ Time.stubs(:now).returns(now)
852
+ @attachment.assign(@file)
853
+ end
854
+
855
+ should "be dirty" do
856
+ assert @attachment.dirty?
857
+ end
858
+
859
+ should "set uploaded_file for access beyond the paperclip lifecycle" do
860
+ assert_equal @file, @attachment.uploaded_file
861
+ end
862
+
863
+ context "and saved" do
864
+ setup do
865
+ @attachment.save
866
+ end
867
+
868
+ should "return the real url" do
869
+ file = @attachment.to_file
870
+ assert file
871
+ assert_match %r{^/system/avatars/#{@instance.id}/original/5k\.png}, @attachment.url
872
+ assert_match %r{^/system/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small)
873
+ file.close
874
+ end
875
+
876
+ should "commit the files to disk" do
877
+ [:large, :medium, :small].each do |style|
878
+ io = @attachment.to_file(style)
879
+ # p "in commit to disk test, io is #{io.inspect} and @instance.id is #{@instance.id}"
880
+ assert File.exists?(io.path)
881
+ assert ! io.is_a?(::Tempfile)
882
+ io.close
883
+ end
884
+ end
885
+
886
+ should "save the files as the right formats and sizes" do
887
+ [[:large, 400, 61, "PNG"],
888
+ [:medium, 100, 15, "GIF"],
889
+ [:small, 32, 32, "JPEG"]].each do |style|
890
+ cmd = %Q[identify -format "%w %h %b %m" "#{@attachment.path(style.first)}"]
891
+ out = `#{cmd}`
892
+ width, height, size, format = out.split(" ")
893
+ assert_equal style[1].to_s, width.to_s
894
+ assert_equal style[2].to_s, height.to_s
895
+ assert_equal style[3].to_s, format.to_s
896
+ end
897
+ end
898
+
899
+ should "still have its #file attribute not be nil" do
900
+ assert ! (file = @attachment.to_file).nil?
901
+ file.close
902
+ end
903
+
904
+ context "and trying to delete" do
905
+ setup do
906
+ @existing_names = @attachment.options.styles.keys.collect do |style|
907
+ @attachment.path(style)
908
+ end
909
+ end
910
+
911
+ should "delete the files after assigning nil" do
912
+ @attachment.expects(:instance_write).with(:file_name, nil)
913
+ @attachment.expects(:instance_write).with(:content_type, nil)
914
+ @attachment.expects(:instance_write).with(:file_size, nil)
915
+ @attachment.expects(:instance_write).with(:updated_at, nil)
916
+ @attachment.expects(:instance_write).with(:width, nil)
917
+ @attachment.expects(:instance_write).with(:height, nil)
918
+ @attachment.assign nil
919
+ @attachment.save
920
+ @existing_names.each{|f| assert ! File.exists?(f) }
921
+ end
922
+
923
+ should "delete the files when you call #clear and #save" do
924
+ @attachment.expects(:instance_write).with(:file_name, nil)
925
+ @attachment.expects(:instance_write).with(:content_type, nil)
926
+ @attachment.expects(:instance_write).with(:file_size, nil)
927
+ @attachment.expects(:instance_write).with(:updated_at, nil)
928
+ @attachment.expects(:instance_write).with(:width, nil)
929
+ @attachment.expects(:instance_write).with(:height, nil)
930
+ @attachment.clear
931
+ @attachment.save
932
+ @existing_names.each{|f| assert ! File.exists?(f) }
933
+ end
934
+
935
+ should "delete the files when you call #delete" do
936
+ @attachment.expects(:instance_write).with(:file_name, nil)
937
+ @attachment.expects(:instance_write).with(:content_type, nil)
938
+ @attachment.expects(:instance_write).with(:file_size, nil)
939
+ @attachment.expects(:instance_write).with(:updated_at, nil)
940
+ @attachment.expects(:instance_write).with(:width, nil)
941
+ @attachment.expects(:instance_write).with(:height, nil)
942
+ @attachment.destroy
943
+ @existing_names.each{|f| assert ! File.exists?(f) }
944
+ end
945
+ end
946
+ end
947
+ end
948
+ end
949
+ end
950
+
951
+ context "with a file that has space in file name" do
952
+ setup do
953
+ @attachment.stubs(:instance_read).with(:file_name).returns("spaced file.png")
954
+ @attachment.stubs(:instance_read).with(:content_type).returns("image/png")
955
+ @attachment.stubs(:instance_read).with(:file_size).returns(12345)
956
+ dtnow = DateTime.now
957
+ @now = Time.now
958
+ Time.stubs(:now).returns(@now)
959
+ @attachment.stubs(:instance_read).with(:updated_at).returns(dtnow)
960
+ end
961
+
962
+ should "returns an escaped version of the URL" do
963
+ assert_match /\/spaced%20file\.png/, @attachment.url
964
+ end
965
+ end
966
+
967
+ context "when trying a nonexistant storage type" do
968
+ setup do
969
+ rebuild_model :storage => :not_here
970
+ end
971
+
972
+ should "not be able to find the module" do
973
+ assert_raise(Paperclip::StorageMethodNotFound){ Dummy.new.avatar }
974
+ end
975
+ end
976
+ end
977
+
978
+ context "An attachment with only a avatar_file_name column" do
979
+ setup do
980
+ ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
981
+ table.column :avatar_file_name, :string
982
+ end
983
+ rebuild_class
984
+ @dummy = Dummy.new
985
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
986
+ end
987
+
988
+ teardown { @file.close }
989
+
990
+ should "not error when assigned an attachment" do
991
+ assert_nothing_raised { @dummy.avatar = @file }
992
+ end
993
+
994
+ should "return the time when sent #avatar_updated_at" do
995
+ now = Time.now
996
+ Time.stubs(:now).returns(now)
997
+ @dummy.avatar = @file
998
+ assert_equal now.to_i, @dummy.avatar.updated_at.to_i
999
+ end
1000
+
1001
+ should "return nil when reloaded and sent #avatar_updated_at" do
1002
+ @dummy.save
1003
+ @dummy.reload
1004
+ assert_nil @dummy.avatar.updated_at
1005
+ end
1006
+
1007
+ should "return the right value when sent #avatar_file_size" do
1008
+ @dummy.avatar = @file
1009
+ assert_equal @file.size, @dummy.avatar.size
1010
+ end
1011
+
1012
+ context "and avatar_updated_at column" do
1013
+ setup do
1014
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_updated_at, :timestamp
1015
+ rebuild_class
1016
+ @dummy = Dummy.new
1017
+ end
1018
+
1019
+ should "not error when assigned an attachment" do
1020
+ assert_nothing_raised { @dummy.avatar = @file }
1021
+ end
1022
+
1023
+ should "return the right value when sent #avatar_updated_at" do
1024
+ now = Time.now
1025
+ Time.stubs(:now).returns(now)
1026
+ @dummy.avatar = @file
1027
+ assert_equal now.to_i, @dummy.avatar.updated_at
1028
+ end
1029
+ end
1030
+
1031
+ context "and avatar_content_type column" do
1032
+ setup do
1033
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_content_type, :string
1034
+ rebuild_class
1035
+ @dummy = Dummy.new
1036
+ end
1037
+
1038
+ should "not error when assigned an attachment" do
1039
+ assert_nothing_raised { @dummy.avatar = @file }
1040
+ end
1041
+
1042
+ should "return the right value when sent #avatar_content_type" do
1043
+ @dummy.avatar = @file
1044
+ assert_equal "image/png", @dummy.avatar.content_type
1045
+ end
1046
+ end
1047
+
1048
+ context "and avatar_file_size column" do
1049
+ setup do
1050
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_file_size, :integer
1051
+ rebuild_class
1052
+ @dummy = Dummy.new
1053
+ end
1054
+
1055
+ should "not error when assigned an attachment" do
1056
+ assert_nothing_raised { @dummy.avatar = @file }
1057
+ end
1058
+
1059
+ should "return the right value when sent #avatar_file_size" do
1060
+ @dummy.avatar = @file
1061
+ assert_equal @file.size, @dummy.avatar.size
1062
+ end
1063
+
1064
+ should "return the right value when saved, reloaded, and sent #avatar_file_size" do
1065
+ @dummy.avatar = @file
1066
+ @dummy.save
1067
+ @dummy = Dummy.find(@dummy.id)
1068
+ assert_equal @file.size, @dummy.avatar.size
1069
+ end
1070
+ end
1071
+
1072
+ context "and avatar_fingerprint column" do
1073
+ setup do
1074
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_fingerprint, :string
1075
+ rebuild_class
1076
+ @dummy = Dummy.new
1077
+ end
1078
+
1079
+ should "not error when assigned an attachment" do
1080
+ assert_nothing_raised { @dummy.avatar = @file }
1081
+ end
1082
+
1083
+ should "return the right value when sent #avatar_fingerprint" do
1084
+ @dummy.avatar = @file
1085
+ assert_equal 'aec488126c3b33c08a10c3fa303acf27', @dummy.avatar_fingerprint
1086
+ end
1087
+
1088
+ should "return the right value when saved, reloaded, and sent #avatar_fingerprint" do
1089
+ @dummy.avatar = @file
1090
+ @dummy.save
1091
+ @dummy = Dummy.find(@dummy.id)
1092
+ assert_equal 'aec488126c3b33c08a10c3fa303acf27', @dummy.avatar_fingerprint
1093
+ end
1094
+ end
1095
+ end
1096
+
1097
+ context "an attachment with delete_file option set to false" do
1098
+ setup do
1099
+ rebuild_model :preserve_files => true
1100
+ @dummy = Dummy.new
1101
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
1102
+ @dummy.avatar = @file
1103
+ @dummy.save!
1104
+ @attachment = @dummy.avatar
1105
+ @path = @attachment.path
1106
+ end
1107
+
1108
+ should "not delete the files from storage when attachment is destroyed" do
1109
+ @attachment.destroy
1110
+ assert File.exists?(@path)
1111
+ end
1112
+
1113
+ should "not delete the file when model is destroyed" do
1114
+ @dummy.destroy
1115
+ assert File.exists?(@path)
1116
+ end
1117
+ end
1118
+
1119
+ context "setting an interpolation class" do
1120
+ should "produce the URL with the given interpolations" do
1121
+ Interpolator = Class.new do
1122
+ def self.interpolate(pattern, attachment, style_name)
1123
+ "hello"
1124
+ end
1125
+ end
1126
+
1127
+ instance = Dummy.new
1128
+ attachment = Paperclip::Attachment.new(:avatar, instance, :interpolator => Interpolator)
1129
+
1130
+ assert_equal "hello", attachment.url
1131
+ end
1132
+ end
1133
+
1134
+ context "An attached file" do
1135
+ setup do
1136
+ rebuild_model
1137
+ @dummy = Dummy.new
1138
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
1139
+ @dummy.avatar = @file
1140
+ @dummy.save!
1141
+ @attachment = @dummy.avatar
1142
+ @path = @attachment.path
1143
+ end
1144
+
1145
+ should "not be deleted when the model fails to destroy" do
1146
+ @dummy.stubs(:destroy).raises(Exception)
1147
+
1148
+ assert_raise Exception do
1149
+ @dummy.destroy
1150
+ end
1151
+
1152
+ assert File.exists?(@path), "#{@path} does not exist."
1153
+ end
1154
+
1155
+ should "be deleted when the model is destroyed" do
1156
+ @dummy.destroy
1157
+ assert ! File.exists?(@path), "#{@path} does not exist."
1158
+ end
1159
+ end
1160
+
1161
+ end