phildarnowsky-paperclip 2.2.10

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 (50) 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 +461 -0
  9. data/lib/paperclip/callback_compatability.rb +33 -0
  10. data/lib/paperclip/content_type.rb +21 -0
  11. data/lib/paperclip/geometry.rb +115 -0
  12. data/lib/paperclip/interpolations.rb +105 -0
  13. data/lib/paperclip/iostream.rb +58 -0
  14. data/lib/paperclip/matchers/have_attached_file_matcher.rb +49 -0
  15. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +66 -0
  16. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +48 -0
  17. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +83 -0
  18. data/lib/paperclip/matchers.rb +4 -0
  19. data/lib/paperclip/processor.rb +49 -0
  20. data/lib/paperclip/storage.rb +236 -0
  21. data/lib/paperclip/thumbnail.rb +70 -0
  22. data/lib/paperclip/upfile.rb +40 -0
  23. data/lib/paperclip.rb +360 -0
  24. data/paperclip.gemspec +37 -0
  25. data/shoulda_macros/paperclip.rb +68 -0
  26. data/tasks/paperclip_tasks.rake +79 -0
  27. data/test/attachment_test.rb +859 -0
  28. data/test/content_type_test.rb +46 -0
  29. data/test/database.yml +4 -0
  30. data/test/fixtures/12k.png +0 -0
  31. data/test/fixtures/50x50.png +0 -0
  32. data/test/fixtures/5k.png +0 -0
  33. data/test/fixtures/bad.png +1 -0
  34. data/test/fixtures/s3.yml +4 -0
  35. data/test/fixtures/text.txt +0 -0
  36. data/test/fixtures/twopage.pdf +0 -0
  37. data/test/geometry_test.rb +177 -0
  38. data/test/helper.rb +100 -0
  39. data/test/integration_test.rb +484 -0
  40. data/test/interpolations_test.rb +120 -0
  41. data/test/iostream_test.rb +71 -0
  42. data/test/matchers/have_attached_file_matcher_test.rb +21 -0
  43. data/test/matchers/validate_attachment_content_type_matcher_test.rb +30 -0
  44. data/test/matchers/validate_attachment_presence_matcher_test.rb +21 -0
  45. data/test/matchers/validate_attachment_size_matcher_test.rb +50 -0
  46. data/test/paperclip_test.rb +291 -0
  47. data/test/processor_test.rb +10 -0
  48. data/test/storage_test.rb +371 -0
  49. data/test/thumbnail_test.rb +177 -0
  50. metadata +127 -0
@@ -0,0 +1,859 @@
1
+ require 'ruby-debug'
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 "Assigning an attachment with the generic content-type" do
452
+ teardown do
453
+ rebuild_model :content_type_strategy => :believe_client
454
+ end
455
+
456
+ %w(application/octet application/octet-stream).each do |generic_content_type|
457
+ context generic_content_type do
458
+ setup do
459
+ @file = StringIO.new('.')
460
+ @file.stubs(:content_type).returns(generic_content_type)
461
+ end
462
+
463
+ context "when the content type strategy is :believe_client" do
464
+ setup do
465
+ rebuild_model :content_type_strategy => :believe_client
466
+ @dummy = Dummy.new
467
+ @dummy.avatar = @file
468
+ end
469
+
470
+ should "return that generic content type" do
471
+ assert_equal generic_content_type, @dummy.avatar_content_type
472
+ end
473
+ end
474
+
475
+ [:from_extension, :from_extension_when_generic].each do |inferring_strategy|
476
+ context "when the content type strategy is #{inferring_strategy}" do
477
+ setup do
478
+ rebuild_model :content_type_strategy => inferring_strategy
479
+ @file.stubs(:original_filename).returns("foobar.png \n")
480
+ @dummy = Dummy.new
481
+ @dummy.avatar = @file
482
+ end
483
+
484
+ should "infer from the extension" do
485
+ assert_equal "image/png", @dummy.avatar_content_type
486
+ end
487
+ end
488
+ end
489
+
490
+ end
491
+ end
492
+ end
493
+
494
+ context "An attachment with a non-generic content-type" do
495
+ setup do
496
+ @file = StringIO.new('.')
497
+ @file.stubs(:content_type).returns("image/xcf")
498
+ end
499
+
500
+ [:believe_client, :from_extension_when_generic].each do |believing_content_type|
501
+ context "and a content type strategy of #{believing_content_type}" do
502
+ setup do
503
+ rebuild_model :content_type_strategy => believing_content_type
504
+ @dummy = Dummy.new
505
+ @dummy.avatar = @file
506
+ end
507
+
508
+ should "use the specified content-type" do
509
+ assert_equal "image/xcf", @dummy.avatar.content_type
510
+ end
511
+ end
512
+ end
513
+
514
+ context "when the content type strategy is :from_extension and the extension disagrees with the nominal content-type" do
515
+ setup do
516
+ @file.stubs(:original_filename).returns("can_of_haggis.png")
517
+
518
+ rebuild_model :content_type_strategy => :from_extension
519
+ @dummy = Dummy.new
520
+ @dummy.avatar = @file
521
+ end
522
+
523
+ should "let the extension overrule the nominal content-type" do
524
+ assert_equal "image/png", @dummy.avatar.content_type
525
+ end
526
+ end
527
+ end
528
+
529
+ context "Assigning an attachment when the content type strategy has an invalid value" do
530
+ setup do
531
+ rebuild_model :content_type_strategy => :zomg_wtf
532
+ @dummy = Dummy.new
533
+ @file = StringIO.new('.')
534
+ end
535
+
536
+ should "raise a NotImplemented error" do
537
+ assert_raise(NotImplementedError) {@dummy.avatar = @file}
538
+ end
539
+ end
540
+
541
+ context "Attachment with strange letters" do
542
+ setup do
543
+ rebuild_model
544
+
545
+ @not_file = mock
546
+ @tempfile = mock
547
+ @not_file.stubs(:nil?).returns(false)
548
+ @not_file.expects(:size).returns(10)
549
+ @tempfile.expects(:size).returns(10)
550
+ @not_file.expects(:to_tempfile).returns(@tempfile)
551
+ @not_file.expects(:original_filename).returns("sheep_say_bæ.png\r\n")
552
+ @not_file.expects(:content_type).returns("image/png\r\n")
553
+
554
+ @dummy = Dummy.new
555
+ @attachment = @dummy.avatar
556
+ @attachment.expects(:valid_assignment?).with(@not_file).returns(true)
557
+ @attachment.expects(:queue_existing_for_delete)
558
+ @attachment.expects(:post_process)
559
+ @attachment.expects(:valid?).returns(true)
560
+ @attachment.expects(:validate)
561
+ @dummy.avatar = @not_file
562
+ end
563
+
564
+ should "remove strange letters and replace with underscore (_)" do
565
+ assert_equal "sheep_say_b_.png", @dummy.avatar.original_filename
566
+ end
567
+
568
+ end
569
+
570
+ context "An attachment" do
571
+ setup do
572
+ @old_defaults = Paperclip::Attachment.default_options.dup
573
+ Paperclip::Attachment.default_options.merge!({
574
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
575
+ })
576
+ FileUtils.rm_rf("tmp")
577
+ rebuild_model
578
+ @instance = Dummy.new
579
+ @attachment = Paperclip::Attachment.new(:avatar, @instance)
580
+ @file = File.new(File.join(File.dirname(__FILE__),
581
+ "fixtures",
582
+ "5k.png"), 'rb')
583
+ end
584
+
585
+ teardown do
586
+ @file.close
587
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
588
+ end
589
+
590
+ should "raise if there are not the correct columns when you try to assign" do
591
+ @other_attachment = Paperclip::Attachment.new(:not_here, @instance)
592
+ assert_raises(Paperclip::PaperclipError) do
593
+ @other_attachment.assign(@file)
594
+ end
595
+ end
596
+
597
+ should "return its default_url when no file assigned" do
598
+ assert @attachment.to_file.nil?
599
+ assert_equal "/avatars/original/missing.png", @attachment.url
600
+ assert_equal "/avatars/blah/missing.png", @attachment.url(:blah)
601
+ end
602
+
603
+ should "return nil as path when no file assigned" do
604
+ assert @attachment.to_file.nil?
605
+ assert_equal nil, @attachment.path
606
+ assert_equal nil, @attachment.path(:blah)
607
+ end
608
+
609
+ context "with a file assigned in the database" do
610
+ setup do
611
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
612
+ @attachment.stubs(:instance_read).with(:content_type).returns("image/png")
613
+ @attachment.stubs(:instance_read).with(:file_size).returns(12345)
614
+ now = Time.now
615
+ Time.stubs(:now).returns(now)
616
+ @attachment.stubs(:instance_read).with(:updated_at).returns(Time.now)
617
+ end
618
+
619
+ should "return a correct url even if the file does not exist" do
620
+ assert_nil @attachment.to_file
621
+ assert_match %r{^/system/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah)
622
+ end
623
+
624
+ should "make sure the updated_at mtime is in the url if it is defined" do
625
+ assert_match %r{#{Time.now.to_i}$}, @attachment.url(:blah)
626
+ end
627
+
628
+ should "make sure the updated_at mtime is NOT in the url if false is passed to the url method" do
629
+ assert_no_match %r{#{Time.now.to_i}$}, @attachment.url(:blah, false)
630
+ end
631
+
632
+ context "with the updated_at field removed" do
633
+ setup do
634
+ @attachment.stubs(:instance_read).with(:updated_at).returns(nil)
635
+ end
636
+
637
+ should "only return the url without the updated_at when sent #url" do
638
+ assert_match "/avatars/#{@instance.id}/blah/5k.png", @attachment.url(:blah)
639
+ end
640
+ end
641
+
642
+ should "return the proper path when filename has a single .'s" do
643
+ assert_equal "./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png", @attachment.path
644
+ end
645
+
646
+ should "return the proper path when filename has multiple .'s" do
647
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.old.png")
648
+ assert_equal "./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png", @attachment.path
649
+ end
650
+
651
+ context "when expecting three styles" do
652
+ setup do
653
+ styles = {:styles => { :large => ["400x400", :png],
654
+ :medium => ["100x100", :gif],
655
+ :small => ["32x32#", :jpg]}}
656
+ @attachment = Paperclip::Attachment.new(:avatar,
657
+ @instance,
658
+ styles)
659
+ end
660
+
661
+ context "and assigned a file" do
662
+ setup do
663
+ now = Time.now
664
+ Time.stubs(:now).returns(now)
665
+ @attachment.assign(@file)
666
+ end
667
+
668
+ should "be dirty" do
669
+ assert @attachment.dirty?
670
+ end
671
+
672
+ context "and saved" do
673
+ setup do
674
+ @attachment.save
675
+ end
676
+
677
+ should "return the real url" do
678
+ file = @attachment.to_file
679
+ assert file
680
+ assert_match %r{^/system/avatars/#{@instance.id}/original/5k\.png}, @attachment.url
681
+ assert_match %r{^/system/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small)
682
+ file.close
683
+ end
684
+
685
+ should "commit the files to disk" do
686
+ [:large, :medium, :small].each do |style|
687
+ io = @attachment.to_io(style)
688
+ assert File.exists?(io)
689
+ assert ! io.is_a?(::Tempfile)
690
+ io.close
691
+ end
692
+ end
693
+
694
+ should "save the files as the right formats and sizes" do
695
+ [[:large, 400, 61, "PNG"],
696
+ [:medium, 100, 15, "GIF"],
697
+ [:small, 32, 32, "JPEG"]].each do |style|
698
+ cmd = %Q[identify -format "%w %h %b %m" "#{@attachment.path(style.first)}"]
699
+ out = `#{cmd}`
700
+ width, height, size, format = out.split(" ")
701
+ assert_equal style[1].to_s, width.to_s
702
+ assert_equal style[2].to_s, height.to_s
703
+ assert_equal style[3].to_s, format.to_s
704
+ end
705
+ end
706
+
707
+ should "still have its #file attribute not be nil" do
708
+ assert ! (file = @attachment.to_file).nil?
709
+ file.close
710
+ end
711
+
712
+ context "and trying to delete" do
713
+ setup do
714
+ @existing_names = @attachment.styles.keys.collect do |style|
715
+ @attachment.path(style)
716
+ end
717
+ end
718
+
719
+ should "delete the files after assigning nil" do
720
+ @attachment.expects(:instance_write).with(:file_name, nil)
721
+ @attachment.expects(:instance_write).with(:content_type, nil)
722
+ @attachment.expects(:instance_write).with(:file_size, nil)
723
+ @attachment.expects(:instance_write).with(:updated_at, nil)
724
+ @attachment.assign nil
725
+ @attachment.save
726
+ @existing_names.each{|f| assert ! File.exists?(f) }
727
+ end
728
+
729
+ should "delete the files when you call #clear and #save" do
730
+ @attachment.expects(:instance_write).with(:file_name, nil)
731
+ @attachment.expects(:instance_write).with(:content_type, nil)
732
+ @attachment.expects(:instance_write).with(:file_size, nil)
733
+ @attachment.expects(:instance_write).with(:updated_at, nil)
734
+ @attachment.clear
735
+ @attachment.save
736
+ @existing_names.each{|f| assert ! File.exists?(f) }
737
+ end
738
+
739
+ should "delete the files when you call #delete" do
740
+ @attachment.expects(:instance_write).with(:file_name, nil)
741
+ @attachment.expects(:instance_write).with(:content_type, nil)
742
+ @attachment.expects(:instance_write).with(:file_size, nil)
743
+ @attachment.expects(:instance_write).with(:updated_at, nil)
744
+ @attachment.destroy
745
+ @existing_names.each{|f| assert ! File.exists?(f) }
746
+ end
747
+ end
748
+ end
749
+ end
750
+ end
751
+
752
+ end
753
+
754
+ context "when trying a nonexistant storage type" do
755
+ setup do
756
+ rebuild_model :storage => :not_here
757
+ end
758
+
759
+ should "not be able to find the module" do
760
+ assert_raise(NameError){ Dummy.new.avatar }
761
+ end
762
+ end
763
+ end
764
+
765
+ context "An attachment with only a avatar_file_name column" do
766
+ setup do
767
+ ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
768
+ table.column :avatar_file_name, :string
769
+ end
770
+ rebuild_class
771
+ @dummy = Dummy.new
772
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
773
+ end
774
+
775
+ teardown { @file.close }
776
+
777
+ should "not error when assigned an attachment" do
778
+ assert_nothing_raised { @dummy.avatar = @file }
779
+ end
780
+
781
+ should "return the time when sent #avatar_updated_at" do
782
+ now = Time.now
783
+ Time.stubs(:now).returns(now)
784
+ @dummy.avatar = @file
785
+ assert now, @dummy.avatar.updated_at
786
+ end
787
+
788
+ should "return nil when reloaded and sent #avatar_updated_at" do
789
+ @dummy.save
790
+ @dummy.reload
791
+ assert_nil @dummy.avatar.updated_at
792
+ end
793
+
794
+ should "return the right value when sent #avatar_file_size" do
795
+ @dummy.avatar = @file
796
+ assert_equal @file.size, @dummy.avatar.size
797
+ end
798
+
799
+ context "and avatar_updated_at column" do
800
+ setup do
801
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_updated_at, :timestamp
802
+ rebuild_class
803
+ @dummy = Dummy.new
804
+ end
805
+
806
+ should "not error when assigned an attachment" do
807
+ assert_nothing_raised { @dummy.avatar = @file }
808
+ end
809
+
810
+ should "return the right value when sent #avatar_updated_at" do
811
+ now = Time.now
812
+ Time.stubs(:now).returns(now)
813
+ @dummy.avatar = @file
814
+ assert_equal now.to_i, @dummy.avatar.updated_at
815
+ end
816
+ end
817
+
818
+ context "and avatar_content_type column" do
819
+ setup do
820
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_content_type, :string
821
+ rebuild_class
822
+ @dummy = Dummy.new
823
+ end
824
+
825
+ should "not error when assigned an attachment" do
826
+ assert_nothing_raised { @dummy.avatar = @file }
827
+ end
828
+
829
+ should "return the right value when sent #avatar_content_type" do
830
+ @dummy.avatar = @file
831
+ assert_equal "image/png", @dummy.avatar.content_type
832
+ end
833
+ end
834
+
835
+ context "and avatar_file_size column" do
836
+ setup do
837
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_file_size, :integer
838
+ rebuild_class
839
+ @dummy = Dummy.new
840
+ end
841
+
842
+ should "not error when assigned an attachment" do
843
+ assert_nothing_raised { @dummy.avatar = @file }
844
+ end
845
+
846
+ should "return the right value when sent #avatar_file_size" do
847
+ @dummy.avatar = @file
848
+ assert_equal @file.size, @dummy.avatar.size
849
+ end
850
+
851
+ should "return the right value when saved, reloaded, and sent #avatar_file_size" do
852
+ @dummy.avatar = @file
853
+ @dummy.save
854
+ @dummy = Dummy.find(@dummy.id)
855
+ assert_equal @file.size, @dummy.avatar.size
856
+ end
857
+ end
858
+ end
859
+ end