mender_paperclip 2.4.3

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 (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