monde-paperclip 2.3.1.1

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