joshpuetz-paperclip 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/LICENSE +26 -0
  2. data/README.rdoc +174 -0
  3. data/Rakefile +99 -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 +1 -0
  8. data/lib/paperclip/attachment.rb +414 -0
  9. data/lib/paperclip/callback_compatability.rb +33 -0
  10. data/lib/paperclip/geometry.rb +115 -0
  11. data/lib/paperclip/interpolations.rb +105 -0
  12. data/lib/paperclip/iostream.rb +58 -0
  13. data/lib/paperclip/matchers/have_attached_file_matcher.rb +49 -0
  14. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +66 -0
  15. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +48 -0
  16. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +83 -0
  17. data/lib/paperclip/matchers.rb +4 -0
  18. data/lib/paperclip/processor.rb +49 -0
  19. data/lib/paperclip/storage.rb +236 -0
  20. data/lib/paperclip/thumbnail.rb +70 -0
  21. data/lib/paperclip/upfile.rb +48 -0
  22. data/lib/paperclip.rb +350 -0
  23. data/shoulda_macros/paperclip.rb +68 -0
  24. data/tasks/paperclip_tasks.rake +79 -0
  25. data/test/attachment_test.rb +767 -0
  26. data/test/database.yml +4 -0
  27. data/test/fixtures/12k.png +0 -0
  28. data/test/fixtures/50x50.png +0 -0
  29. data/test/fixtures/5k.png +0 -0
  30. data/test/fixtures/bad.png +1 -0
  31. data/test/fixtures/s3.yml +4 -0
  32. data/test/fixtures/text.txt +0 -0
  33. data/test/fixtures/twopage.pdf +0 -0
  34. data/test/geometry_test.rb +177 -0
  35. data/test/helper.rb +99 -0
  36. data/test/integration_test.rb +481 -0
  37. data/test/interpolations_test.rb +120 -0
  38. data/test/iostream_test.rb +71 -0
  39. data/test/matchers/have_attached_file_matcher_test.rb +21 -0
  40. data/test/matchers/validate_attachment_content_type_matcher_test.rb +30 -0
  41. data/test/matchers/validate_attachment_presence_matcher_test.rb +21 -0
  42. data/test/matchers/validate_attachment_size_matcher_test.rb +50 -0
  43. data/test/paperclip_test.rb +291 -0
  44. data/test/processor_test.rb +10 -0
  45. data/test/storage_test.rb +282 -0
  46. data/test/thumbnail_test.rb +177 -0
  47. metadata +125 -0
@@ -0,0 +1,767 @@
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
+ context "Attachment default_options" do
18
+ setup do
19
+ rebuild_model
20
+ @old_default_options = Paperclip::Attachment.default_options.dup
21
+ @new_default_options = @old_default_options.merge({
22
+ :path => "argle/bargle",
23
+ :url => "fooferon",
24
+ :default_url => "not here.png"
25
+ })
26
+ end
27
+
28
+ teardown do
29
+ Paperclip::Attachment.default_options.merge! @old_default_options
30
+ end
31
+
32
+ should "be overrideable" do
33
+ Paperclip::Attachment.default_options.merge!(@new_default_options)
34
+ @new_default_options.keys.each do |key|
35
+ assert_equal @new_default_options[key],
36
+ Paperclip::Attachment.default_options[key]
37
+ end
38
+ end
39
+
40
+ context "without an Attachment" do
41
+ setup do
42
+ @dummy = Dummy.new
43
+ end
44
+
45
+ should "return false when asked exists?" do
46
+ assert !@dummy.avatar.exists?
47
+ end
48
+ end
49
+
50
+ context "on an Attachment" do
51
+ setup do
52
+ @dummy = Dummy.new
53
+ @attachment = @dummy.avatar
54
+ end
55
+
56
+ Paperclip::Attachment.default_options.keys.each do |key|
57
+ should "be the default_options for #{key}" do
58
+ assert_equal @old_default_options[key],
59
+ @attachment.instance_variable_get("@#{key}"),
60
+ key
61
+ end
62
+ end
63
+
64
+ context "when redefined" do
65
+ setup do
66
+ Paperclip::Attachment.default_options.merge!(@new_default_options)
67
+ @dummy = Dummy.new
68
+ @attachment = @dummy.avatar
69
+ end
70
+
71
+ Paperclip::Attachment.default_options.keys.each do |key|
72
+ should "be the new default_options for #{key}" do
73
+ assert_equal @new_default_options[key],
74
+ @attachment.instance_variable_get("@#{key}"),
75
+ key
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ context "An attachment with similarly named interpolations" do
83
+ setup do
84
+ rebuild_model :path => ":id.omg/:id-bbq/:idwhat/:id_partition.wtf"
85
+ @dummy = Dummy.new
86
+ @dummy.stubs(:id).returns(1024)
87
+ @file = File.new(File.join(File.dirname(__FILE__),
88
+ "fixtures",
89
+ "5k.png"), 'rb')
90
+ @dummy.avatar = @file
91
+ end
92
+
93
+ teardown { @file.close }
94
+
95
+ should "make sure that they are interpolated correctly" do
96
+ assert_equal "1024.omg/1024-bbq/1024what/000/001/024.wtf", @dummy.avatar.path
97
+ end
98
+ end
99
+
100
+ context "An attachment with a :rails_env interpolation" do
101
+ setup do
102
+ @rails_env = "blah"
103
+ @id = 1024
104
+ rebuild_model :path => ":rails_env/:id.png"
105
+ @dummy = Dummy.new
106
+ @dummy.stubs(:id).returns(@id)
107
+ @file = StringIO.new(".")
108
+ @dummy.avatar = @file
109
+ end
110
+
111
+ should "return the proper path" do
112
+ temporary_rails_env(@rails_env) {
113
+ assert_equal "#{@rails_env}/#{@id}.png", @dummy.avatar.path
114
+ }
115
+ end
116
+ end
117
+
118
+ context "An attachment with a default style and an extension interpolation" do
119
+ setup do
120
+ @attachment = attachment :path => ":basename.:extension",
121
+ :styles => { :default => ["100x100", :png] },
122
+ :default_style => :default
123
+ @file = StringIO.new("...")
124
+ @file.expects(:original_filename).returns("file.jpg")
125
+ end
126
+ should "return the right extension for the path" do
127
+ @attachment.assign(@file)
128
+ assert_equal "file.png", @attachment.path
129
+ end
130
+ end
131
+
132
+ context "An attachment with :convert_options" do
133
+ setup do
134
+ rebuild_model :styles => {
135
+ :thumb => "100x100",
136
+ :large => "400x400"
137
+ },
138
+ :convert_options => {
139
+ :all => "-do_stuff",
140
+ :thumb => "-thumbnailize"
141
+ }
142
+ @dummy = Dummy.new
143
+ @dummy.avatar
144
+ end
145
+
146
+ should "report the correct options when sent #extra_options_for(:thumb)" do
147
+ assert_equal "-thumbnailize -do_stuff", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
148
+ end
149
+
150
+ should "report the correct options when sent #extra_options_for(:large)" do
151
+ assert_equal "-do_stuff", @dummy.avatar.send(:extra_options_for, :large)
152
+ end
153
+
154
+ before_should "call extra_options_for(:thumb/:large)" do
155
+ Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb)
156
+ Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large)
157
+ end
158
+ end
159
+
160
+ context "An attachment with :convert_options that is a proc" do
161
+ setup do
162
+ rebuild_model :styles => {
163
+ :thumb => "100x100",
164
+ :large => "400x400"
165
+ },
166
+ :convert_options => {
167
+ :all => lambda{|i| i.all },
168
+ :thumb => lambda{|i| i.thumb }
169
+ }
170
+ Dummy.class_eval do
171
+ def all; "-all"; end
172
+ def thumb; "-thumb"; end
173
+ end
174
+ @dummy = Dummy.new
175
+ @dummy.avatar
176
+ end
177
+
178
+ should "report the correct options when sent #extra_options_for(:thumb)" do
179
+ assert_equal "-thumb -all", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
180
+ end
181
+
182
+ should "report the correct options when sent #extra_options_for(:large)" do
183
+ assert_equal "-all", @dummy.avatar.send(:extra_options_for, :large)
184
+ end
185
+
186
+ before_should "call extra_options_for(:thumb/:large)" do
187
+ Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb)
188
+ Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large)
189
+ end
190
+ end
191
+
192
+ context "An attachment with :path that is a proc" do
193
+ setup do
194
+ rebuild_model :path => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
195
+
196
+ @file = File.new(File.join(File.dirname(__FILE__),
197
+ "fixtures",
198
+ "5k.png"), 'rb')
199
+ @dummyA = Dummy.new(:other => 'a')
200
+ @dummyA.avatar = @file
201
+ @dummyB = Dummy.new(:other => 'b')
202
+ @dummyB.avatar = @file
203
+ end
204
+
205
+ teardown { @file.close }
206
+
207
+ should "return correct path" do
208
+ assert_equal "path/a.png", @dummyA.avatar.path
209
+ assert_equal "path/b.png", @dummyB.avatar.path
210
+ end
211
+ end
212
+
213
+ context "An attachment with :styles that is a proc" do
214
+ setup do
215
+ rebuild_model :styles => lambda{ |attachment| {:thumb => "50x50#", :large => "400x400"} }
216
+
217
+ @attachment = Dummy.new.avatar
218
+ end
219
+
220
+ should "have the correct geometry" do
221
+ assert_equal "50x50#", @attachment.styles[:thumb][:geometry]
222
+ end
223
+ end
224
+
225
+ context "An attachment with :url that is a proc" do
226
+ setup do
227
+ rebuild_model :url => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
228
+
229
+ @file = File.new(File.join(File.dirname(__FILE__),
230
+ "fixtures",
231
+ "5k.png"), 'rb')
232
+ @dummyA = Dummy.new(:other => 'a')
233
+ @dummyA.avatar = @file
234
+ @dummyB = Dummy.new(:other => 'b')
235
+ @dummyB.avatar = @file
236
+ end
237
+
238
+ teardown { @file.close }
239
+
240
+ should "return correct url" do
241
+ assert_equal "path/a.png", @dummyA.avatar.url(:original, false)
242
+ assert_equal "path/b.png", @dummyB.avatar.url(:original, false)
243
+ end
244
+ end
245
+
246
+ geometry_specs = [
247
+ [ lambda{|z| "50x50#" }, :png ],
248
+ lambda{|z| "50x50#" },
249
+ { :geometry => lambda{|z| "50x50#" } }
250
+ ]
251
+ geometry_specs.each do |geometry_spec|
252
+ context "An attachment geometry like #{geometry_spec}" do
253
+ setup do
254
+ rebuild_model :styles => { :normal => geometry_spec }
255
+ @attachment = Dummy.new.avatar
256
+ end
257
+
258
+ should "not run the procs immediately" do
259
+ assert_kind_of Proc, @attachment.styles[:normal][:geometry]
260
+ end
261
+
262
+ context "when assigned" do
263
+ setup do
264
+ @file = StringIO.new(".")
265
+ @attachment.assign(@file)
266
+ end
267
+
268
+ should "have the correct geometry" do
269
+ assert_equal "50x50#", @attachment.styles[:normal][:geometry]
270
+ end
271
+ end
272
+ end
273
+ end
274
+
275
+ context "An attachment with both 'normal' and hash-style styles" do
276
+ setup do
277
+ rebuild_model :styles => {
278
+ :normal => ["50x50#", :png],
279
+ :hash => { :geometry => "50x50#", :format => :png }
280
+ }
281
+ @dummy = Dummy.new
282
+ @attachment = @dummy.avatar
283
+ end
284
+
285
+ [:processors, :whiny, :convert_options, :geometry, :format].each do |field|
286
+ should "have the same #{field} field" do
287
+ assert_equal @attachment.styles[:normal][field], @attachment.styles[:hash][field]
288
+ end
289
+ end
290
+ end
291
+
292
+ context "An attachment with :processors that is a proc" do
293
+ setup do
294
+ rebuild_model :styles => { :normal => '' }, :processors => lambda { |a| [ :test ] }
295
+ @attachment = Dummy.new.avatar
296
+ end
297
+
298
+ should "not run the proc immediately" do
299
+ assert_kind_of Proc, @attachment.styles[:normal][:processors]
300
+ end
301
+
302
+ context "when assigned" do
303
+ setup do
304
+ @attachment.assign(StringIO.new("."))
305
+ end
306
+
307
+ should "have the correct processors" do
308
+ assert_equal [ :test ], @attachment.styles[:normal][:processors]
309
+ end
310
+ end
311
+ end
312
+
313
+ context "An attachment with erroring processor" do
314
+ setup do
315
+ rebuild_model :processor => [:thumbnail], :styles => { :small => '' }, :whiny_thumbnails => true
316
+ @dummy = Dummy.new
317
+ Paperclip::Thumbnail.expects(:make).raises(Paperclip::PaperclipError, "cannot be processed.")
318
+ @file = StringIO.new("...")
319
+ @file.stubs(:to_tempfile).returns(@file)
320
+ @dummy.avatar = @file
321
+ end
322
+
323
+ should "correctly forward processing error message to the instance" do
324
+ @dummy.valid?
325
+ assert_contains @dummy.errors.full_messages, "Avatar cannot be processed."
326
+ end
327
+ end
328
+
329
+ context "An attachment with multiple processors" do
330
+ setup do
331
+ class Paperclip::Test < Paperclip::Processor; end
332
+ @style_params = { :once => {:one => 1, :two => 2} }
333
+ rebuild_model :processors => [:thumbnail, :test], :styles => @style_params
334
+ @dummy = Dummy.new
335
+ @file = StringIO.new("...")
336
+ @file.stubs(:to_tempfile).returns(@file)
337
+ Paperclip::Test.stubs(:make).returns(@file)
338
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
339
+ end
340
+
341
+ context "when assigned" do
342
+ setup { @dummy.avatar = @file }
343
+
344
+ before_should "call #make on all specified processors" do
345
+ expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
346
+ Paperclip::Thumbnail.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
347
+ Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
348
+ end
349
+
350
+ before_should "call #make with attachment passed as third argument" do
351
+ expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
352
+ Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
353
+ end
354
+ end
355
+ end
356
+
357
+ context "An attachment with no processors defined" do
358
+ setup do
359
+ rebuild_model :processors => [], :styles => {:something => 1}
360
+ @dummy = Dummy.new
361
+ @file = StringIO.new("...")
362
+ end
363
+ should "raise when assigned to" do
364
+ assert_raises(RuntimeError){ @dummy.avatar = @file }
365
+ end
366
+ end
367
+
368
+ context "Assigning an attachment with post_process hooks" do
369
+ setup do
370
+ rebuild_model :styles => { :something => "100x100#" }
371
+ Dummy.class_eval do
372
+ before_avatar_post_process :do_before_avatar
373
+ after_avatar_post_process :do_after_avatar
374
+ before_post_process :do_before_all
375
+ after_post_process :do_after_all
376
+ def do_before_avatar; end
377
+ def do_after_avatar; end
378
+ def do_before_all; end
379
+ def do_after_all; end
380
+ end
381
+ @file = StringIO.new(".")
382
+ @file.stubs(:to_tempfile).returns(@file)
383
+ @dummy = Dummy.new
384
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
385
+ @attachment = @dummy.avatar
386
+ end
387
+
388
+ should "call the defined callbacks when assigned" do
389
+ @dummy.expects(:do_before_avatar).with()
390
+ @dummy.expects(:do_after_avatar).with()
391
+ @dummy.expects(:do_before_all).with()
392
+ @dummy.expects(:do_after_all).with()
393
+ Paperclip::Thumbnail.expects(:make).returns(@file)
394
+ @dummy.avatar = @file
395
+ end
396
+
397
+ should "not cancel the processing if a before_post_process returns nil" do
398
+ @dummy.expects(:do_before_avatar).with().returns(nil)
399
+ @dummy.expects(:do_after_avatar).with()
400
+ @dummy.expects(:do_before_all).with().returns(nil)
401
+ @dummy.expects(:do_after_all).with()
402
+ Paperclip::Thumbnail.expects(:make).returns(@file)
403
+ @dummy.avatar = @file
404
+ end
405
+
406
+ should "cancel the processing if a before_post_process returns false" do
407
+ @dummy.expects(:do_before_avatar).never
408
+ @dummy.expects(:do_after_avatar).never
409
+ @dummy.expects(:do_before_all).with().returns(false)
410
+ @dummy.expects(:do_after_all).never
411
+ Paperclip::Thumbnail.expects(:make).never
412
+ @dummy.avatar = @file
413
+ end
414
+
415
+ should "cancel the processing if a before_avatar_post_process returns false" do
416
+ @dummy.expects(:do_before_avatar).with().returns(false)
417
+ @dummy.expects(:do_after_avatar).never
418
+ @dummy.expects(:do_before_all).with().returns(true)
419
+ @dummy.expects(:do_after_all).never
420
+ Paperclip::Thumbnail.expects(:make).never
421
+ @dummy.avatar = @file
422
+ end
423
+ end
424
+
425
+ context "Assigning an attachment" do
426
+ setup do
427
+ rebuild_model :styles => { :something => "100x100#" }
428
+ @file = StringIO.new(".")
429
+ @file.expects(:original_filename).returns("5k.png\n\n")
430
+ @file.expects(:content_type).returns("image/png\n\n")
431
+ @file.stubs(:to_tempfile).returns(@file)
432
+ @dummy = Dummy.new
433
+ Paperclip::Thumbnail.expects(:make).returns(@file)
434
+ @dummy.expects(:run_callbacks).with(:before_avatar_post_process, {:original => @file})
435
+ @dummy.expects(:run_callbacks).with(:before_post_process, {:original => @file})
436
+ @dummy.expects(:run_callbacks).with(:after_avatar_post_process, {:original => @file, :something => @file})
437
+ @dummy.expects(:run_callbacks).with(:after_post_process, {:original => @file, :something => @file})
438
+ @attachment = @dummy.avatar
439
+ @dummy.avatar = @file
440
+ end
441
+
442
+ should "strip whitespace from original_filename field" do
443
+ assert_equal "5k.png", @dummy.avatar.original_filename
444
+ end
445
+
446
+ should "strip whitespace from content_type field" do
447
+ assert_equal "image/png", @dummy.avatar.instance.avatar_content_type
448
+ end
449
+ end
450
+
451
+ context "Attachment with strange letters" do
452
+ setup do
453
+ rebuild_model
454
+
455
+ @not_file = mock
456
+ @tempfile = mock
457
+ @not_file.stubs(:nil?).returns(false)
458
+ @not_file.expects(:size).returns(10)
459
+ @tempfile.expects(:size).returns(10)
460
+ @not_file.expects(:to_tempfile).returns(@tempfile)
461
+ @not_file.expects(:original_filename).returns("sheep_say_bæ.png\r\n")
462
+ @not_file.expects(:content_type).returns("image/png\r\n")
463
+
464
+ @dummy = Dummy.new
465
+ @attachment = @dummy.avatar
466
+ @attachment.expects(:valid_assignment?).with(@not_file).returns(true)
467
+ @attachment.expects(:queue_existing_for_delete)
468
+ @attachment.expects(:post_process)
469
+ @attachment.expects(:valid?).returns(true)
470
+ @attachment.expects(:validate)
471
+ @dummy.avatar = @not_file
472
+ end
473
+
474
+ should "remove strange letters and replace with underscore (_)" do
475
+ assert_equal "sheep_say_b_.png", @dummy.avatar.original_filename
476
+ end
477
+
478
+ end
479
+
480
+ context "An attachment" do
481
+ setup do
482
+ @old_defaults = Paperclip::Attachment.default_options.dup
483
+ Paperclip::Attachment.default_options.merge!({
484
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
485
+ })
486
+ FileUtils.rm_rf("tmp")
487
+ rebuild_model
488
+ @instance = Dummy.new
489
+ @attachment = Paperclip::Attachment.new(:avatar, @instance)
490
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
491
+ end
492
+
493
+ teardown do
494
+ @file.close
495
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
496
+ end
497
+
498
+ should "raise if there are not the correct columns when you try to assign" do
499
+ @other_attachment = Paperclip::Attachment.new(:not_here, @instance)
500
+ assert_raises(Paperclip::PaperclipError) do
501
+ @other_attachment.assign(@file)
502
+ end
503
+ end
504
+
505
+ should "return its default_url when no file assigned" do
506
+ assert @attachment.to_file.nil?
507
+ assert_equal "/avatars/original/missing.png", @attachment.url
508
+ assert_equal "/avatars/blah/missing.png", @attachment.url(:blah)
509
+ end
510
+
511
+ should "return nil as path when no file assigned" do
512
+ assert @attachment.to_file.nil?
513
+ assert_equal nil, @attachment.path
514
+ assert_equal nil, @attachment.path(:blah)
515
+ end
516
+
517
+ context "with a file assigned in the database" do
518
+ setup do
519
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
520
+ @attachment.stubs(:instance_read).with(:content_type).returns("image/png")
521
+ @attachment.stubs(:instance_read).with(:file_size).returns(12345)
522
+ now = Time.now
523
+ Time.stubs(:now).returns(now)
524
+ @attachment.stubs(:instance_read).with(:updated_at).returns(Time.now)
525
+ end
526
+
527
+ should "return a correct url even if the file does not exist" do
528
+ assert_nil @attachment.to_file
529
+ assert_match %r{^/system/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah)
530
+ end
531
+
532
+ should "make sure the updated_at mtime is in the url if it is defined" do
533
+ assert_match %r{#{Time.now.to_i}$}, @attachment.url(:blah)
534
+ end
535
+
536
+ should "make sure the updated_at mtime is NOT in the url if false is passed to the url method" do
537
+ assert_no_match %r{#{Time.now.to_i}$}, @attachment.url(:blah, false)
538
+ end
539
+
540
+ context "with the updated_at field removed" do
541
+ setup do
542
+ @attachment.stubs(:instance_read).with(:updated_at).returns(nil)
543
+ end
544
+
545
+ should "only return the url without the updated_at when sent #url" do
546
+ assert_match "/avatars/#{@instance.id}/blah/5k.png", @attachment.url(:blah)
547
+ end
548
+ end
549
+
550
+ should "return the proper path when filename has a single .'s" do
551
+ assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
552
+ end
553
+
554
+ should "return the proper path when filename has multiple .'s" do
555
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.old.png")
556
+ assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png"), File.expand_path(@attachment.path)
557
+ end
558
+
559
+ context "when expecting three styles" do
560
+ setup do
561
+ styles = {:styles => { :large => ["400x400", :png],
562
+ :medium => ["100x100", :gif],
563
+ :small => ["32x32#", :jpg]}}
564
+ @attachment = Paperclip::Attachment.new(:avatar,
565
+ @instance,
566
+ styles)
567
+ end
568
+
569
+ context "and assigned a file" do
570
+ setup do
571
+ now = Time.now
572
+ Time.stubs(:now).returns(now)
573
+ @attachment.assign(@file)
574
+ end
575
+
576
+ should "be dirty" do
577
+ assert @attachment.dirty?
578
+ end
579
+
580
+ context "and saved" do
581
+ setup do
582
+ @attachment.save
583
+ end
584
+
585
+ should "return the real url" do
586
+ file = @attachment.to_file
587
+ assert file
588
+ assert_match %r{^/system/avatars/#{@instance.id}/original/5k\.png}, @attachment.url
589
+ assert_match %r{^/system/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small)
590
+ file.close
591
+ end
592
+
593
+ should "commit the files to disk" do
594
+ [:large, :medium, :small].each do |style|
595
+ io = @attachment.to_io(style)
596
+ assert File.exists?(io)
597
+ assert ! io.is_a?(::Tempfile)
598
+ io.close
599
+ end
600
+ end
601
+
602
+ should "save the files as the right formats and sizes" do
603
+ [[:large, 400, 61, "PNG"],
604
+ [:medium, 100, 15, "GIF"],
605
+ [:small, 32, 32, "JPEG"]].each do |style|
606
+ cmd = %Q[identify -format "%w %h %b %m" "#{@attachment.path(style.first)}"]
607
+ out = `#{cmd}`
608
+ width, height, size, format = out.split(" ")
609
+ assert_equal style[1].to_s, width.to_s
610
+ assert_equal style[2].to_s, height.to_s
611
+ assert_equal style[3].to_s, format.to_s
612
+ end
613
+ end
614
+
615
+ should "still have its #file attribute not be nil" do
616
+ assert ! (file = @attachment.to_file).nil?
617
+ file.close
618
+ end
619
+
620
+ context "and trying to delete" do
621
+ setup do
622
+ @existing_names = @attachment.styles.keys.collect do |style|
623
+ @attachment.path(style)
624
+ end
625
+ end
626
+
627
+ should "delete the files after assigning nil" do
628
+ @attachment.expects(:instance_write).with(:file_name, nil)
629
+ @attachment.expects(:instance_write).with(:content_type, nil)
630
+ @attachment.expects(:instance_write).with(:file_size, nil)
631
+ @attachment.expects(:instance_write).with(:updated_at, nil)
632
+ @attachment.assign nil
633
+ @attachment.save
634
+ @existing_names.each{|f| assert ! File.exists?(f) }
635
+ end
636
+
637
+ should "delete the files when you call #clear and #save" do
638
+ @attachment.expects(:instance_write).with(:file_name, nil)
639
+ @attachment.expects(:instance_write).with(:content_type, nil)
640
+ @attachment.expects(:instance_write).with(:file_size, nil)
641
+ @attachment.expects(:instance_write).with(:updated_at, nil)
642
+ @attachment.clear
643
+ @attachment.save
644
+ @existing_names.each{|f| assert ! File.exists?(f) }
645
+ end
646
+
647
+ should "delete the files when you call #delete" do
648
+ @attachment.expects(:instance_write).with(:file_name, nil)
649
+ @attachment.expects(:instance_write).with(:content_type, nil)
650
+ @attachment.expects(:instance_write).with(:file_size, nil)
651
+ @attachment.expects(:instance_write).with(:updated_at, nil)
652
+ @attachment.destroy
653
+ @existing_names.each{|f| assert ! File.exists?(f) }
654
+ end
655
+ end
656
+ end
657
+ end
658
+ end
659
+
660
+ end
661
+
662
+ context "when trying a nonexistant storage type" do
663
+ setup do
664
+ rebuild_model :storage => :not_here
665
+ end
666
+
667
+ should "not be able to find the module" do
668
+ assert_raise(NameError){ Dummy.new.avatar }
669
+ end
670
+ end
671
+ end
672
+
673
+ context "An attachment with only a avatar_file_name column" do
674
+ setup do
675
+ ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
676
+ table.column :avatar_file_name, :string
677
+ end
678
+ rebuild_class
679
+ @dummy = Dummy.new
680
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
681
+ end
682
+
683
+ teardown { @file.close }
684
+
685
+ should "not error when assigned an attachment" do
686
+ assert_nothing_raised { @dummy.avatar = @file }
687
+ end
688
+
689
+ should "return the time when sent #avatar_updated_at" do
690
+ now = Time.now
691
+ Time.stubs(:now).returns(now)
692
+ @dummy.avatar = @file
693
+ assert now, @dummy.avatar.updated_at
694
+ end
695
+
696
+ should "return nil when reloaded and sent #avatar_updated_at" do
697
+ @dummy.save
698
+ @dummy.reload
699
+ assert_nil @dummy.avatar.updated_at
700
+ end
701
+
702
+ should "return the right value when sent #avatar_file_size" do
703
+ @dummy.avatar = @file
704
+ assert_equal @file.size, @dummy.avatar.size
705
+ end
706
+
707
+ context "and avatar_updated_at column" do
708
+ setup do
709
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_updated_at, :timestamp
710
+ rebuild_class
711
+ @dummy = Dummy.new
712
+ end
713
+
714
+ should "not error when assigned an attachment" do
715
+ assert_nothing_raised { @dummy.avatar = @file }
716
+ end
717
+
718
+ should "return the right value when sent #avatar_updated_at" do
719
+ now = Time.now
720
+ Time.stubs(:now).returns(now)
721
+ @dummy.avatar = @file
722
+ assert_equal now.to_i, @dummy.avatar.updated_at
723
+ end
724
+ end
725
+
726
+ context "and avatar_content_type column" do
727
+ setup do
728
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_content_type, :string
729
+ rebuild_class
730
+ @dummy = Dummy.new
731
+ end
732
+
733
+ should "not error when assigned an attachment" do
734
+ assert_nothing_raised { @dummy.avatar = @file }
735
+ end
736
+
737
+ should "return the right value when sent #avatar_content_type" do
738
+ @dummy.avatar = @file
739
+ assert_equal "image/png", @dummy.avatar.content_type
740
+ end
741
+ end
742
+
743
+ context "and avatar_file_size column" do
744
+ setup do
745
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_file_size, :integer
746
+ rebuild_class
747
+ @dummy = Dummy.new
748
+ end
749
+
750
+ should "not error when assigned an attachment" do
751
+ assert_nothing_raised { @dummy.avatar = @file }
752
+ end
753
+
754
+ should "return the right value when sent #avatar_file_size" do
755
+ @dummy.avatar = @file
756
+ assert_equal @file.size, @dummy.avatar.size
757
+ end
758
+
759
+ should "return the right value when saved, reloaded, and sent #avatar_file_size" do
760
+ @dummy.avatar = @file
761
+ @dummy.save
762
+ @dummy = Dummy.find(@dummy.id)
763
+ assert_equal @file.size, @dummy.avatar.size
764
+ end
765
+ end
766
+ end
767
+ end