mender_paperclip 2.4.3

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