monde-paperclip 2.3.1.1

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