paperclip_with_versions 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 +356 -0
  9. data/lib/paperclip/attachment.rb +414 -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 +58 -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 +66 -0
  17. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +48 -0
  18. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +83 -0
  19. data/lib/paperclip/processor.rb +49 -0
  20. data/lib/paperclip/storage.rb +243 -0
  21. data/lib/paperclip/thumbnail.rb +73 -0
  22. data/lib/paperclip/upfile.rb +49 -0
  23. data/shoulda_macros/paperclip.rb +117 -0
  24. data/tasks/paperclip_tasks.rake +79 -0
  25. data/test/attachment_test.rb +815 -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 +71 -0
  39. data/test/matchers/have_attached_file_matcher_test.rb +21 -0
  40. data/test/matchers/validate_attachment_content_type_matcher_test.rb +30 -0
  41. data/test/matchers/validate_attachment_presence_matcher_test.rb +21 -0
  42. data/test/matchers/validate_attachment_size_matcher_test.rb +50 -0
  43. data/test/paperclip_test.rb +327 -0
  44. data/test/processor_test.rb +10 -0
  45. data/test/storage_test.rb +303 -0
  46. data/test/thumbnail_test.rb +227 -0
  47. data/test/upfile_test.rb +28 -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,815 @@
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.expects(: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
+
166
+ before_should "call extra_options_for(:thumb/:large)" do
167
+ Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb)
168
+ Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large)
169
+ end
170
+ end
171
+
172
+ context "An attachment with :convert_options that is a proc" do
173
+ setup do
174
+ rebuild_model :styles => {
175
+ :thumb => "100x100",
176
+ :large => "400x400"
177
+ },
178
+ :convert_options => {
179
+ :all => lambda{|i| i.all },
180
+ :thumb => lambda{|i| i.thumb }
181
+ }
182
+ Dummy.class_eval do
183
+ def all; "-all"; end
184
+ def thumb; "-thumb"; end
185
+ end
186
+ @dummy = Dummy.new
187
+ @dummy.avatar
188
+ end
189
+
190
+ should "report the correct options when sent #extra_options_for(:thumb)" do
191
+ assert_equal "-thumb -all", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
192
+ end
193
+
194
+ should "report the correct options when sent #extra_options_for(:large)" do
195
+ assert_equal "-all", @dummy.avatar.send(:extra_options_for, :large)
196
+ end
197
+
198
+ before_should "call extra_options_for(:thumb/:large)" do
199
+ Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb)
200
+ Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large)
201
+ end
202
+ end
203
+
204
+ context "An attachment with :path that is a proc" do
205
+ setup do
206
+ rebuild_model :path => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
207
+
208
+ @file = File.new(File.join(File.dirname(__FILE__),
209
+ "fixtures",
210
+ "5k.png"), 'rb')
211
+ @dummyA = Dummy.new(:other => 'a')
212
+ @dummyA.avatar = @file
213
+ @dummyB = Dummy.new(:other => 'b')
214
+ @dummyB.avatar = @file
215
+ end
216
+
217
+ teardown { @file.close }
218
+
219
+ should "return correct path" do
220
+ assert_equal "path/a.png", @dummyA.avatar.path
221
+ assert_equal "path/b.png", @dummyB.avatar.path
222
+ end
223
+ end
224
+
225
+ context "An attachment with :styles that is a proc" do
226
+ setup do
227
+ rebuild_model :styles => lambda{ |attachment| {:thumb => "50x50#", :large => "400x400"} }
228
+
229
+ @attachment = Dummy.new.avatar
230
+ end
231
+
232
+ should "have the correct geometry" do
233
+ assert_equal "50x50#", @attachment.styles[:thumb][:geometry]
234
+ end
235
+ end
236
+
237
+ context "An attachment with :url that is a proc" do
238
+ setup do
239
+ rebuild_model :url => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
240
+
241
+ @file = File.new(File.join(File.dirname(__FILE__),
242
+ "fixtures",
243
+ "5k.png"), 'rb')
244
+ @dummyA = Dummy.new(:other => 'a')
245
+ @dummyA.avatar = @file
246
+ @dummyB = Dummy.new(:other => 'b')
247
+ @dummyB.avatar = @file
248
+ end
249
+
250
+ teardown { @file.close }
251
+
252
+ should "return correct url" do
253
+ assert_equal "path/a.png", @dummyA.avatar.url(:original, false)
254
+ assert_equal "path/b.png", @dummyB.avatar.url(:original, false)
255
+ end
256
+ end
257
+
258
+ geometry_specs = [
259
+ [ lambda{|z| "50x50#" }, :png ],
260
+ lambda{|z| "50x50#" },
261
+ { :geometry => lambda{|z| "50x50#" } }
262
+ ]
263
+ geometry_specs.each do |geometry_spec|
264
+ context "An attachment geometry like #{geometry_spec}" do
265
+ setup do
266
+ rebuild_model :styles => { :normal => geometry_spec }
267
+ @attachment = Dummy.new.avatar
268
+ end
269
+
270
+ should "not run the procs immediately" do
271
+ assert_kind_of Proc, @attachment.styles[:normal][:geometry]
272
+ end
273
+
274
+ context "when assigned" do
275
+ setup do
276
+ @file = StringIO.new(".")
277
+ @attachment.assign(@file)
278
+ end
279
+
280
+ should "have the correct geometry" do
281
+ assert_equal "50x50#", @attachment.styles[:normal][:geometry]
282
+ end
283
+ end
284
+ end
285
+ end
286
+
287
+ context "An attachment with both 'normal' and hash-style styles" do
288
+ setup do
289
+ rebuild_model :styles => {
290
+ :normal => ["50x50#", :png],
291
+ :hash => { :geometry => "50x50#", :format => :png }
292
+ }
293
+ @dummy = Dummy.new
294
+ @attachment = @dummy.avatar
295
+ end
296
+
297
+ [:processors, :whiny, :convert_options, :geometry, :format].each do |field|
298
+ should "have the same #{field} field" do
299
+ assert_equal @attachment.styles[:normal][field], @attachment.styles[:hash][field]
300
+ end
301
+ end
302
+ end
303
+
304
+ context "An attachment with :processors that is a proc" do
305
+ setup do
306
+ rebuild_model :styles => { :normal => '' }, :processors => lambda { |a| [ :test ] }
307
+ @attachment = Dummy.new.avatar
308
+ end
309
+
310
+ should "not run the proc immediately" do
311
+ assert_kind_of Proc, @attachment.styles[:normal][:processors]
312
+ end
313
+
314
+ context "when assigned" do
315
+ setup do
316
+ @attachment.assign(StringIO.new("."))
317
+ end
318
+
319
+ should "have the correct processors" do
320
+ assert_equal [ :test ], @attachment.styles[:normal][:processors]
321
+ end
322
+ end
323
+ end
324
+
325
+ context "An attachment with erroring processor" do
326
+ setup do
327
+ rebuild_model :processor => [:thumbnail], :styles => { :small => '' }, :whiny_thumbnails => true
328
+ @dummy = Dummy.new
329
+ Paperclip::Thumbnail.expects(:make).raises(Paperclip::PaperclipError, "cannot be processed.")
330
+ @file = StringIO.new("...")
331
+ @file.stubs(:to_tempfile).returns(@file)
332
+ @dummy.avatar = @file
333
+ end
334
+
335
+ should "correctly forward processing error message to the instance" do
336
+ @dummy.valid?
337
+ assert_contains @dummy.errors.full_messages, "Avatar cannot be processed."
338
+ end
339
+ end
340
+
341
+ context "An attachment with multiple processors" do
342
+ setup do
343
+ class Paperclip::Test < Paperclip::Processor; end
344
+ @style_params = { :once => {:one => 1, :two => 2} }
345
+ rebuild_model :processors => [:thumbnail, :test], :styles => @style_params
346
+ @dummy = Dummy.new
347
+ @file = StringIO.new("...")
348
+ @file.stubs(:to_tempfile).returns(@file)
349
+ Paperclip::Test.stubs(:make).returns(@file)
350
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
351
+ end
352
+
353
+ context "when assigned" do
354
+ setup { @dummy.avatar = @file }
355
+
356
+ before_should "call #make on all specified processors" do
357
+ expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
358
+ Paperclip::Thumbnail.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
359
+ Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
360
+ end
361
+
362
+ before_should "call #make with attachment passed as third argument" do
363
+ expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
364
+ Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
365
+ end
366
+ end
367
+ end
368
+
369
+ context "An attachment with no processors defined" do
370
+ setup do
371
+ rebuild_model :processors => [], :styles => {:something => 1}
372
+ @dummy = Dummy.new
373
+ @file = StringIO.new("...")
374
+ end
375
+ should "raise when assigned to" do
376
+ assert_raises(RuntimeError){ @dummy.avatar = @file }
377
+ end
378
+ end
379
+
380
+ context "Assigning an attachment with post_process hooks" do
381
+ setup do
382
+ rebuild_model :styles => { :something => "100x100#" }
383
+ Dummy.class_eval do
384
+ before_avatar_post_process :do_before_avatar
385
+ after_avatar_post_process :do_after_avatar
386
+ before_post_process :do_before_all
387
+ after_post_process :do_after_all
388
+ def do_before_avatar; end
389
+ def do_after_avatar; end
390
+ def do_before_all; end
391
+ def do_after_all; end
392
+ end
393
+ @file = StringIO.new(".")
394
+ @file.stubs(:to_tempfile).returns(@file)
395
+ @dummy = Dummy.new
396
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
397
+ @attachment = @dummy.avatar
398
+ end
399
+
400
+ should "call the defined callbacks when assigned" do
401
+ @dummy.expects(:do_before_avatar).with()
402
+ @dummy.expects(:do_after_avatar).with()
403
+ @dummy.expects(:do_before_all).with()
404
+ @dummy.expects(:do_after_all).with()
405
+ Paperclip::Thumbnail.expects(:make).returns(@file)
406
+ @dummy.avatar = @file
407
+ end
408
+
409
+ should "not cancel the processing if a before_post_process returns nil" do
410
+ @dummy.expects(:do_before_avatar).with().returns(nil)
411
+ @dummy.expects(:do_after_avatar).with()
412
+ @dummy.expects(:do_before_all).with().returns(nil)
413
+ @dummy.expects(:do_after_all).with()
414
+ Paperclip::Thumbnail.expects(:make).returns(@file)
415
+ @dummy.avatar = @file
416
+ end
417
+
418
+ should "cancel the processing if a before_post_process returns false" do
419
+ @dummy.expects(:do_before_avatar).never
420
+ @dummy.expects(:do_after_avatar).never
421
+ @dummy.expects(:do_before_all).with().returns(false)
422
+ @dummy.expects(:do_after_all).never
423
+ Paperclip::Thumbnail.expects(:make).never
424
+ @dummy.avatar = @file
425
+ end
426
+
427
+ should "cancel the processing if a before_avatar_post_process returns false" do
428
+ @dummy.expects(:do_before_avatar).with().returns(false)
429
+ @dummy.expects(:do_after_avatar).never
430
+ @dummy.expects(:do_before_all).with().returns(true)
431
+ @dummy.expects(:do_after_all).never
432
+ Paperclip::Thumbnail.expects(:make).never
433
+ @dummy.avatar = @file
434
+ end
435
+ end
436
+
437
+ context "Assigning an attachment" do
438
+ setup do
439
+ rebuild_model :styles => { :something => "100x100#" }
440
+ @file = StringIO.new(".")
441
+ @file.expects(:original_filename).returns("5k.png\n\n")
442
+ @file.expects(:content_type).returns("image/png\n\n")
443
+ @file.stubs(:to_tempfile).returns(@file)
444
+ @dummy = Dummy.new
445
+ Paperclip::Thumbnail.expects(:make).returns(@file)
446
+ @dummy.expects(:run_callbacks).with(:before_avatar_post_process, {:original => @file})
447
+ @dummy.expects(:run_callbacks).with(:before_post_process, {:original => @file})
448
+ @dummy.expects(:run_callbacks).with(:after_avatar_post_process, {:original => @file, :something => @file})
449
+ @dummy.expects(:run_callbacks).with(:after_post_process, {:original => @file, :something => @file})
450
+ @attachment = @dummy.avatar
451
+ @dummy.avatar = @file
452
+ end
453
+
454
+ should "strip whitespace from original_filename field" do
455
+ assert_equal "5k.png", @dummy.avatar.original_filename
456
+ end
457
+
458
+ should "strip whitespace from content_type field" do
459
+ assert_equal "image/png", @dummy.avatar.instance.avatar_content_type
460
+ end
461
+ end
462
+
463
+ context "Attachment with strange letters" do
464
+ setup do
465
+ rebuild_model
466
+
467
+ @not_file = mock
468
+ @tempfile = mock
469
+ @not_file.stubs(:nil?).returns(false)
470
+ @not_file.expects(:size).returns(10)
471
+ @tempfile.expects(:size).returns(10)
472
+ @not_file.expects(:to_tempfile).returns(@tempfile)
473
+ @not_file.expects(:original_filename).returns("sheep_say_bæ.png\r\n")
474
+ @not_file.expects(:content_type).returns("image/png\r\n")
475
+
476
+ @dummy = Dummy.new
477
+ @attachment = @dummy.avatar
478
+ @attachment.expects(:valid_assignment?).with(@not_file).returns(true)
479
+ @attachment.expects(:queue_existing_for_delete)
480
+ @attachment.expects(:post_process)
481
+ @attachment.expects(:valid?).returns(true)
482
+ @attachment.expects(:validate)
483
+ @dummy.avatar = @not_file
484
+ end
485
+
486
+ should "remove strange letters and replace with underscore (_)" do
487
+ assert_equal "sheep_say_b_.png", @dummy.avatar.original_filename
488
+ end
489
+
490
+ end
491
+
492
+ context "An attachment" do
493
+ setup do
494
+ @old_defaults = Paperclip::Attachment.default_options.dup
495
+ Paperclip::Attachment.default_options.merge!({
496
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
497
+ })
498
+ FileUtils.rm_rf("tmp")
499
+ rebuild_model
500
+ @instance = Dummy.new
501
+ @attachment = Paperclip::Attachment.new(:avatar, @instance)
502
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
503
+ end
504
+
505
+ teardown do
506
+ @file.close
507
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
508
+ end
509
+
510
+ should "raise if there are not the correct columns when you try to assign" do
511
+ @other_attachment = Paperclip::Attachment.new(:not_here, @instance)
512
+ assert_raises(Paperclip::PaperclipError) do
513
+ @other_attachment.assign(@file)
514
+ end
515
+ end
516
+
517
+ should "return its default_url when no file assigned" do
518
+ assert @attachment.to_file.nil?
519
+ assert_equal "/avatars/original/missing.png", @attachment.url
520
+ assert_equal "/avatars/blah/missing.png", @attachment.url(:blah)
521
+ end
522
+
523
+ should "return nil as path when no file assigned" do
524
+ assert @attachment.to_file.nil?
525
+ assert_equal nil, @attachment.path
526
+ assert_equal nil, @attachment.path(:blah)
527
+ end
528
+
529
+ context "with a file assigned in the database" do
530
+ setup do
531
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
532
+ @attachment.stubs(:instance_read).with(:content_type).returns("image/png")
533
+ @attachment.stubs(:instance_read).with(:file_size).returns(12345)
534
+ dtnow = DateTime.now
535
+ @now = Time.now
536
+ Time.stubs(:now).returns(@now)
537
+ @attachment.stubs(:instance_read).with(:updated_at).returns(dtnow)
538
+ end
539
+
540
+ should "return a correct url even if the file does not exist" do
541
+ assert_nil @attachment.to_file
542
+ assert_match %r{^/system/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah)
543
+ end
544
+
545
+ should "make sure the updated_at mtime is in the url if it is defined" do
546
+ assert_match %r{#{@now.to_i}$}, @attachment.url(:blah)
547
+ end
548
+
549
+ should "make sure the updated_at mtime is NOT in the url if false is passed to the url method" do
550
+ assert_no_match %r{#{@now.to_i}$}, @attachment.url(:blah, false)
551
+ end
552
+
553
+ context "with the updated_at field removed" do
554
+ setup do
555
+ @attachment.stubs(:instance_read).with(:updated_at).returns(nil)
556
+ end
557
+
558
+ should "only return the url without the updated_at when sent #url" do
559
+ assert_match "/avatars/#{@instance.id}/blah/5k.png", @attachment.url(:blah)
560
+ end
561
+ end
562
+
563
+ should "return the proper path when filename has a single .'s" do
564
+ assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
565
+ end
566
+
567
+ should "return the proper path when filename has multiple .'s" do
568
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.old.png")
569
+ assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png"), File.expand_path(@attachment.path)
570
+ end
571
+
572
+ context "when expecting three styles" do
573
+ setup do
574
+ styles = {:styles => { :large => ["400x400", :png],
575
+ :medium => ["100x100", :gif],
576
+ :small => ["32x32#", :jpg]}}
577
+ @attachment = Paperclip::Attachment.new(:avatar,
578
+ @instance,
579
+ styles)
580
+ end
581
+
582
+ context "and assigned a file" do
583
+ setup do
584
+ now = Time.now
585
+ Time.stubs(:now).returns(now)
586
+ @attachment.assign(@file)
587
+ end
588
+
589
+ should "be dirty" do
590
+ assert @attachment.dirty?
591
+ end
592
+
593
+ context "and saved" do
594
+ setup do
595
+ @attachment.save
596
+ end
597
+
598
+ should "return the real url" do
599
+ file = @attachment.to_file
600
+ assert file
601
+ assert_match %r{^/system/avatars/#{@instance.id}/original/5k\.png}, @attachment.url
602
+ assert_match %r{^/system/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small)
603
+ file.close
604
+ end
605
+
606
+ should "commit the files to disk" do
607
+ [:large, :medium, :small].each do |style|
608
+ io = @attachment.to_file(style)
609
+ assert File.exists?(io)
610
+ assert ! io.is_a?(::Tempfile)
611
+ io.close
612
+ end
613
+ end
614
+
615
+ should "save the files as the right formats and sizes" do
616
+ [[:large, 400, 61, "PNG"],
617
+ [:medium, 100, 15, "GIF"],
618
+ [:small, 32, 32, "JPEG"]].each do |style|
619
+ cmd = %Q[identify -format "%w %h %b %m" "#{@attachment.path(style.first)}"]
620
+ out = `#{cmd}`
621
+ width, height, size, format = out.split(" ")
622
+ assert_equal style[1].to_s, width.to_s
623
+ assert_equal style[2].to_s, height.to_s
624
+ assert_equal style[3].to_s, format.to_s
625
+ end
626
+ end
627
+
628
+ should "still have its #file attribute not be nil" do
629
+ assert ! (file = @attachment.to_file).nil?
630
+ file.close
631
+ end
632
+
633
+ context "and trying to delete" do
634
+ setup do
635
+ @existing_names = @attachment.styles.keys.collect do |style|
636
+ @attachment.path(style)
637
+ end
638
+ end
639
+
640
+ should "delete the files after assigning nil" do
641
+ @attachment.expects(:instance_write).with(:file_name, nil)
642
+ @attachment.expects(:instance_write).with(:content_type, nil)
643
+ @attachment.expects(:instance_write).with(:file_size, nil)
644
+ @attachment.expects(:instance_write).with(:updated_at, nil)
645
+ @attachment.assign nil
646
+ @attachment.save
647
+ @existing_names.each{|f| assert ! File.exists?(f) }
648
+ end
649
+
650
+ should "delete the files when you call #clear and #save" do
651
+ @attachment.expects(:instance_write).with(:file_name, nil)
652
+ @attachment.expects(:instance_write).with(:content_type, nil)
653
+ @attachment.expects(:instance_write).with(:file_size, nil)
654
+ @attachment.expects(:instance_write).with(:updated_at, nil)
655
+ @attachment.clear
656
+ @attachment.save
657
+ @existing_names.each{|f| assert ! File.exists?(f) }
658
+ end
659
+
660
+ should "delete the files when you call #delete" do
661
+ @attachment.expects(:instance_write).with(:file_name, nil)
662
+ @attachment.expects(:instance_write).with(:content_type, nil)
663
+ @attachment.expects(:instance_write).with(:file_size, nil)
664
+ @attachment.expects(:instance_write).with(:updated_at, nil)
665
+ @attachment.destroy
666
+ @existing_names.each{|f| assert ! File.exists?(f) }
667
+ end
668
+
669
+ context "when keeping old files" do
670
+ setup do
671
+ @attachment.options[:keep_old_files] = true
672
+ end
673
+
674
+ should "keep the files after assigning nil" do
675
+ @attachment.expects(:instance_write).with(:file_name, nil)
676
+ @attachment.expects(:instance_write).with(:content_type, nil)
677
+ @attachment.expects(:instance_write).with(:file_size, nil)
678
+ @attachment.expects(:instance_write).with(:updated_at, nil)
679
+ @attachment.assign nil
680
+ @attachment.save
681
+ @existing_names.each{|f| assert File.exists?(f) }
682
+ end
683
+
684
+ should "keep the files when you call #clear and #save" do
685
+ @attachment.expects(:instance_write).with(:file_name, nil)
686
+ @attachment.expects(:instance_write).with(:content_type, nil)
687
+ @attachment.expects(:instance_write).with(:file_size, nil)
688
+ @attachment.expects(:instance_write).with(:updated_at, nil)
689
+ @attachment.clear
690
+ @attachment.save
691
+ @existing_names.each{|f| assert File.exists?(f) }
692
+ end
693
+
694
+ should "keep the files when you call #delete" do
695
+ @attachment.expects(:instance_write).with(:file_name, nil)
696
+ @attachment.expects(:instance_write).with(:content_type, nil)
697
+ @attachment.expects(:instance_write).with(:file_size, nil)
698
+ @attachment.expects(:instance_write).with(:updated_at, nil)
699
+ @attachment.destroy
700
+ @existing_names.each{|f| assert File.exists?(f) }
701
+ end
702
+ end
703
+ end
704
+ end
705
+ end
706
+ end
707
+
708
+ end
709
+
710
+ context "when trying a nonexistant storage type" do
711
+ setup do
712
+ rebuild_model :storage => :not_here
713
+ end
714
+
715
+ should "not be able to find the module" do
716
+ assert_raise(NameError){ Dummy.new.avatar }
717
+ end
718
+ end
719
+ end
720
+
721
+ context "An attachment with only a avatar_file_name column" do
722
+ setup do
723
+ ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
724
+ table.column :avatar_file_name, :string
725
+ end
726
+ rebuild_class
727
+ @dummy = Dummy.new
728
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
729
+ end
730
+
731
+ teardown { @file.close }
732
+
733
+ should "not error when assigned an attachment" do
734
+ assert_nothing_raised { @dummy.avatar = @file }
735
+ end
736
+
737
+ should "return the time when sent #avatar_updated_at" do
738
+ now = Time.now
739
+ Time.stubs(:now).returns(now)
740
+ @dummy.avatar = @file
741
+ assert now, @dummy.avatar.updated_at
742
+ end
743
+
744
+ should "return nil when reloaded and sent #avatar_updated_at" do
745
+ @dummy.save
746
+ @dummy.reload
747
+ assert_nil @dummy.avatar.updated_at
748
+ end
749
+
750
+ should "return the right value when sent #avatar_file_size" do
751
+ @dummy.avatar = @file
752
+ assert_equal @file.size, @dummy.avatar.size
753
+ end
754
+
755
+ context "and avatar_updated_at column" do
756
+ setup do
757
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_updated_at, :timestamp
758
+ rebuild_class
759
+ @dummy = Dummy.new
760
+ end
761
+
762
+ should "not error when assigned an attachment" do
763
+ assert_nothing_raised { @dummy.avatar = @file }
764
+ end
765
+
766
+ should "return the right value when sent #avatar_updated_at" do
767
+ now = Time.now
768
+ Time.stubs(:now).returns(now)
769
+ @dummy.avatar = @file
770
+ assert_equal now.to_i, @dummy.avatar.updated_at
771
+ end
772
+ end
773
+
774
+ context "and avatar_content_type column" do
775
+ setup do
776
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_content_type, :string
777
+ rebuild_class
778
+ @dummy = Dummy.new
779
+ end
780
+
781
+ should "not error when assigned an attachment" do
782
+ assert_nothing_raised { @dummy.avatar = @file }
783
+ end
784
+
785
+ should "return the right value when sent #avatar_content_type" do
786
+ @dummy.avatar = @file
787
+ assert_equal "image/png", @dummy.avatar.content_type
788
+ end
789
+ end
790
+
791
+ context "and avatar_file_size column" do
792
+ setup do
793
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_file_size, :integer
794
+ rebuild_class
795
+ @dummy = Dummy.new
796
+ end
797
+
798
+ should "not error when assigned an attachment" do
799
+ assert_nothing_raised { @dummy.avatar = @file }
800
+ end
801
+
802
+ should "return the right value when sent #avatar_file_size" do
803
+ @dummy.avatar = @file
804
+ assert_equal @file.size, @dummy.avatar.size
805
+ end
806
+
807
+ should "return the right value when saved, reloaded, and sent #avatar_file_size" do
808
+ @dummy.avatar = @file
809
+ @dummy.save
810
+ @dummy = Dummy.find(@dummy.id)
811
+ assert_equal @file.size, @dummy.avatar.size
812
+ end
813
+ end
814
+ end
815
+ end