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