organicinternet-paperclip 2.3.8.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 (60) hide show
  1. data/LICENSE +26 -0
  2. data/README.md +217 -0
  3. data/Rakefile +80 -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/generators/paperclip/USAGE +8 -0
  9. data/lib/generators/paperclip/paperclip_generator.rb +31 -0
  10. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
  11. data/lib/paperclip.rb +376 -0
  12. data/lib/paperclip/attachment.rb +350 -0
  13. data/lib/paperclip/callback_compatability.rb +61 -0
  14. data/lib/paperclip/command_line.rb +80 -0
  15. data/lib/paperclip/geometry.rb +115 -0
  16. data/lib/paperclip/interpolations.rb +114 -0
  17. data/lib/paperclip/iostream.rb +45 -0
  18. data/lib/paperclip/matchers.rb +33 -0
  19. data/lib/paperclip/matchers/have_attached_file_matcher.rb +57 -0
  20. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +75 -0
  21. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +54 -0
  22. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +95 -0
  23. data/lib/paperclip/processor.rb +58 -0
  24. data/lib/paperclip/railtie.rb +24 -0
  25. data/lib/paperclip/storage.rb +2 -0
  26. data/lib/paperclip/storage/filesystem.rb +73 -0
  27. data/lib/paperclip/storage/s3.rb +192 -0
  28. data/lib/paperclip/style.rb +90 -0
  29. data/lib/paperclip/thumbnail.rb +79 -0
  30. data/lib/paperclip/upfile.rb +55 -0
  31. data/lib/paperclip/version.rb +3 -0
  32. data/lib/tasks/paperclip.rake +72 -0
  33. data/rails/init.rb +2 -0
  34. data/shoulda_macros/paperclip.rb +118 -0
  35. data/test/attachment_test.rb +804 -0
  36. data/test/command_line_test.rb +133 -0
  37. data/test/database.yml +4 -0
  38. data/test/fixtures/12k.png +0 -0
  39. data/test/fixtures/50x50.png +0 -0
  40. data/test/fixtures/5k.png +0 -0
  41. data/test/fixtures/bad.png +1 -0
  42. data/test/fixtures/s3.yml +8 -0
  43. data/test/fixtures/text.txt +0 -0
  44. data/test/fixtures/twopage.pdf +0 -0
  45. data/test/geometry_test.rb +177 -0
  46. data/test/helper.rb +146 -0
  47. data/test/integration_test.rb +498 -0
  48. data/test/interpolations_test.rb +127 -0
  49. data/test/iostream_test.rb +71 -0
  50. data/test/matchers/have_attached_file_matcher_test.rb +24 -0
  51. data/test/matchers/validate_attachment_content_type_matcher_test.rb +47 -0
  52. data/test/matchers/validate_attachment_presence_matcher_test.rb +26 -0
  53. data/test/matchers/validate_attachment_size_matcher_test.rb +51 -0
  54. data/test/paperclip_test.rb +271 -0
  55. data/test/processor_test.rb +10 -0
  56. data/test/storage_test.rb +386 -0
  57. data/test/style_test.rb +141 -0
  58. data/test/thumbnail_test.rb +227 -0
  59. data/test/upfile_test.rb +36 -0
  60. metadata +224 -0
@@ -0,0 +1,498 @@
1
+ require './test/helper'
2
+
3
+ class IntegrationTest < Test::Unit::TestCase
4
+ context "Many models at once" do
5
+ setup do
6
+ rebuild_model
7
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
8
+ 300.times do |i|
9
+ Dummy.create! :avatar => @file
10
+ end
11
+ end
12
+
13
+ should "not exceed the open file limit" do
14
+ assert_nothing_raised do
15
+ dummies = Dummy.find(:all)
16
+ dummies.each { |dummy| dummy.avatar }
17
+ end
18
+ end
19
+ end
20
+
21
+ context "An attachment" do
22
+ setup do
23
+ rebuild_model :styles => { :thumb => "50x50#" }
24
+ @dummy = Dummy.new
25
+ @file = File.new(File.join(File.dirname(__FILE__),
26
+ "fixtures",
27
+ "5k.png"), 'rb')
28
+ @dummy.avatar = @file
29
+ assert @dummy.save
30
+ end
31
+
32
+ teardown { @file.close }
33
+
34
+ should "create its thumbnails properly" do
35
+ assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
36
+ end
37
+
38
+ context 'reprocessing with unreadable original' do
39
+ setup { File.chmod(0000, @dummy.avatar.path) }
40
+
41
+ should "not raise an error" do
42
+ assert_nothing_raised do
43
+ @dummy.avatar.reprocess!
44
+ end
45
+ end
46
+
47
+ should "return false" do
48
+ assert ! @dummy.avatar.reprocess!
49
+ end
50
+
51
+ teardown { File.chmod(0644, @dummy.avatar.path) }
52
+ end
53
+
54
+ context "redefining its attachment styles" do
55
+ setup do
56
+ Dummy.class_eval do
57
+ has_attached_file :avatar, :styles => { :thumb => "150x25#" }
58
+ has_attached_file :avatar, :styles => { :thumb => "150x25#", :dynamic => lambda { |a| '50x50#' } }
59
+ end
60
+ @d2 = Dummy.find(@dummy.id)
61
+ @d2.avatar.reprocess!
62
+ @d2.save
63
+ end
64
+
65
+ should "create its thumbnails properly" do
66
+ assert_match /\b150x25\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
67
+ assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:dynamic)}"`
68
+ end
69
+ end
70
+ end
71
+
72
+ context "A model that modifies its original" do
73
+ setup do
74
+ rebuild_model :styles => { :original => "2x2#" }
75
+ @dummy = Dummy.new
76
+ @file = File.new(File.join(File.dirname(__FILE__),
77
+ "fixtures",
78
+ "5k.png"), 'rb')
79
+ @dummy.avatar = @file
80
+ end
81
+
82
+ should "report the file size of the processed file and not the original" do
83
+ assert_not_equal @file.size, @dummy.avatar.size
84
+ end
85
+
86
+ teardown { @file.close }
87
+ end
88
+
89
+ context "A model with attachments scoped under an id" do
90
+ setup do
91
+ rebuild_model :styles => { :large => "100x100",
92
+ :medium => "50x50" },
93
+ :path => ":rails_root/tmp/:id/:attachments/:style.:extension"
94
+ @dummy = Dummy.new
95
+ @file = File.new(File.join(File.dirname(__FILE__),
96
+ "fixtures",
97
+ "5k.png"), 'rb')
98
+ @dummy.avatar = @file
99
+ end
100
+
101
+ teardown { @file.close }
102
+
103
+ context "when saved" do
104
+ setup do
105
+ @dummy.save
106
+ @saved_path = @dummy.avatar.path(:large)
107
+ end
108
+
109
+ should "have a large file in the right place" do
110
+ assert File.exists?(@dummy.avatar.path(:large))
111
+ end
112
+
113
+ context "and deleted" do
114
+ setup do
115
+ @dummy.avatar.clear
116
+ @dummy.save
117
+ end
118
+
119
+ should "not have a large file in the right place anymore" do
120
+ assert ! File.exists?(@saved_path)
121
+ end
122
+
123
+ should "not have its next two parent directories" do
124
+ assert ! File.exists?(File.dirname(@saved_path))
125
+ assert ! File.exists?(File.dirname(File.dirname(@saved_path)))
126
+ end
127
+
128
+ before_should "not die if an unexpected SystemCallError happens" do
129
+ FileUtils.stubs(:rmdir).raises(Errno::EPIPE)
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ context "A model with no attachment validation" do
136
+ setup do
137
+ rebuild_model :styles => { :large => "300x300>",
138
+ :medium => "100x100",
139
+ :thumb => ["32x32#", :gif] },
140
+ :default_style => :medium,
141
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
142
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
143
+ @dummy = Dummy.new
144
+ end
145
+
146
+ should "have its definition return false when asked about whiny_thumbnails" do
147
+ assert ! Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
148
+ end
149
+
150
+ context "when validates_attachment_thumbnails is called" do
151
+ setup do
152
+ Dummy.validates_attachment_thumbnails :avatar
153
+ end
154
+
155
+ should "have its definition return true when asked about whiny_thumbnails" do
156
+ assert_equal true, Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
157
+ end
158
+ end
159
+
160
+ context "redefined to have attachment validations" do
161
+ setup do
162
+ rebuild_model :styles => { :large => "300x300>",
163
+ :medium => "100x100",
164
+ :thumb => ["32x32#", :gif] },
165
+ :whiny_thumbnails => true,
166
+ :default_style => :medium,
167
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
168
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
169
+ end
170
+
171
+ should "have its definition return true when asked about whiny_thumbnails" do
172
+ assert_equal true, Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
173
+ end
174
+ end
175
+ end
176
+
177
+ context "A model with no convert_options setting" do
178
+ setup do
179
+ rebuild_model :styles => { :large => "300x300>",
180
+ :medium => "100x100",
181
+ :thumb => ["32x32#", :gif] },
182
+ :default_style => :medium,
183
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
184
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
185
+ @dummy = Dummy.new
186
+ end
187
+
188
+ should "have its definition return nil when asked about convert_options" do
189
+ assert ! Dummy.attachment_definitions[:avatar][:convert_options]
190
+ end
191
+
192
+ context "redefined to have convert_options setting" do
193
+ setup do
194
+ rebuild_model :styles => { :large => "300x300>",
195
+ :medium => "100x100",
196
+ :thumb => ["32x32#", :gif] },
197
+ :convert_options => "-strip -depth 8",
198
+ :default_style => :medium,
199
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
200
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
201
+ end
202
+
203
+ should "have its definition return convert_options value when asked about convert_options" do
204
+ assert_equal "-strip -depth 8", Dummy.attachment_definitions[:avatar][:convert_options]
205
+ end
206
+ end
207
+ end
208
+
209
+ context "A model with a filesystem attachment" do
210
+ setup do
211
+ rebuild_model :styles => { :large => "300x300>",
212
+ :medium => "100x100",
213
+ :thumb => ["32x32#", :gif] },
214
+ :whiny_thumbnails => true,
215
+ :default_style => :medium,
216
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
217
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
218
+ @dummy = Dummy.new
219
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
220
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
221
+
222
+ assert @dummy.avatar = @file
223
+ assert @dummy.valid?, @dummy.errors.full_messages.join(", ")
224
+ assert @dummy.save
225
+ end
226
+
227
+ should "write and delete its files" do
228
+ [["434x66", :original],
229
+ ["300x46", :large],
230
+ ["100x15", :medium],
231
+ ["32x32", :thumb]].each do |geo, style|
232
+ cmd = %Q[identify -format "%wx%h" "#{@dummy.avatar.path(style)}"]
233
+ assert_equal geo, `#{cmd}`.chomp, cmd
234
+ end
235
+
236
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
237
+
238
+ @d2 = Dummy.find(@dummy.id)
239
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path}"`.chomp
240
+ assert_equal "434x66", `identify -format "%wx%h" "#{@d2.avatar.path(:original)}"`.chomp
241
+ assert_equal "300x46", `identify -format "%wx%h" "#{@d2.avatar.path(:large)}"`.chomp
242
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path(:medium)}"`.chomp
243
+ assert_equal "32x32", `identify -format "%wx%h" "#{@d2.avatar.path(:thumb)}"`.chomp
244
+
245
+ @dummy.avatar = "not a valid file but not nil"
246
+ assert_equal File.basename(@file.path), @dummy.avatar_file_name
247
+ assert @dummy.valid?
248
+ assert @dummy.save
249
+
250
+ saved_paths.each do |p|
251
+ assert File.exists?(p)
252
+ end
253
+
254
+ @dummy.avatar.clear
255
+ assert_nil @dummy.avatar_file_name
256
+ assert @dummy.valid?
257
+ assert @dummy.save
258
+
259
+ saved_paths.each do |p|
260
+ assert ! File.exists?(p)
261
+ end
262
+
263
+ @d2 = Dummy.find(@dummy.id)
264
+ assert_nil @d2.avatar_file_name
265
+ end
266
+
267
+ should "work exactly the same when new as when reloaded" do
268
+ @d2 = Dummy.find(@dummy.id)
269
+
270
+ assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
271
+ [:thumb, :medium, :large, :original].each do |style|
272
+ assert_equal @dummy.avatar.path(style), @d2.avatar.path(style)
273
+ end
274
+
275
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
276
+
277
+ @d2.avatar.clear
278
+ assert @d2.save
279
+
280
+ saved_paths.each do |p|
281
+ assert ! File.exists?(p)
282
+ end
283
+ end
284
+
285
+ should "know the difference between good files, bad files, and not files" do
286
+ expected = @dummy.avatar.to_file
287
+ @dummy.avatar = "not a file"
288
+ assert @dummy.valid?
289
+ assert_equal expected.path, @dummy.avatar.path
290
+ expected.close
291
+
292
+ @dummy.avatar = @bad_file
293
+ assert ! @dummy.valid?
294
+ end
295
+
296
+ should "know the difference between good files, bad files, and not files when validating" do
297
+ Dummy.validates_attachment_presence :avatar
298
+ @d2 = Dummy.find(@dummy.id)
299
+ @d2.avatar = @file
300
+ assert @d2.valid?, @d2.errors.full_messages.inspect
301
+ @d2.avatar = @bad_file
302
+ assert ! @d2.valid?
303
+ end
304
+
305
+ should "be able to reload without saving and not have the file disappear" do
306
+ @dummy.avatar = @file
307
+ assert @dummy.save
308
+ @dummy.avatar.clear
309
+ assert_nil @dummy.avatar_file_name
310
+ @dummy.reload
311
+ assert_equal "5k.png", @dummy.avatar_file_name
312
+ end
313
+
314
+ context "that is assigned its file from another Paperclip attachment" do
315
+ setup do
316
+ @dummy2 = Dummy.new
317
+ @file2 = File.new(File.join(FIXTURES_DIR, "12k.png"), 'rb')
318
+ assert @dummy2.avatar = @file2
319
+ @dummy2.save
320
+ end
321
+
322
+ should "work when assigned a file" do
323
+ assert_not_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
324
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
325
+
326
+ assert @dummy.avatar = @dummy2.avatar
327
+ @dummy.save
328
+ assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
329
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
330
+ end
331
+ end
332
+
333
+ end
334
+
335
+ context "A model with an attachments association and a Paperclip attachment" do
336
+ setup do
337
+ Dummy.class_eval do
338
+ has_many :attachments, :class_name => 'Dummy'
339
+ end
340
+
341
+ @dummy = Dummy.new
342
+ @dummy.avatar = File.new(File.join(File.dirname(__FILE__),
343
+ "fixtures",
344
+ "5k.png"), 'rb')
345
+ end
346
+
347
+ should "should not error when saving" do
348
+ assert_nothing_raised do
349
+ @dummy.save!
350
+ end
351
+ end
352
+ end
353
+
354
+ if ENV['S3_TEST_BUCKET']
355
+ def s3_files_for attachment
356
+ [:thumb, :medium, :large, :original].inject({}) do |files, style|
357
+ data = `curl "#{attachment.url(style)}" 2>/dev/null`.chomp
358
+ t = Tempfile.new("paperclip-test")
359
+ t.binmode
360
+ t.write(data)
361
+ t.rewind
362
+ files[style] = t
363
+ files
364
+ end
365
+ end
366
+
367
+ def s3_headers_for attachment, style
368
+ `curl --head "#{attachment.url(style)}" 2>/dev/null`.split("\n").inject({}) do |h,head|
369
+ split_head = head.chomp.split(/\s*:\s*/, 2)
370
+ h[split_head.first.downcase] = split_head.last unless split_head.empty?
371
+ h
372
+ end
373
+ end
374
+
375
+ context "A model with an S3 attachment" do
376
+ setup do
377
+ rebuild_model :styles => { :large => "300x300>",
378
+ :medium => "100x100",
379
+ :thumb => ["32x32#", :gif] },
380
+ :storage => :s3,
381
+ :whiny_thumbnails => true,
382
+ :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml")),
383
+ :default_style => :medium,
384
+ :bucket => ENV['S3_TEST_BUCKET'],
385
+ :path => ":class/:attachment/:id/:style/:basename.:extension"
386
+ @dummy = Dummy.new
387
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
388
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
389
+
390
+ assert @dummy.avatar = @file
391
+ assert @dummy.valid?
392
+ assert @dummy.save
393
+
394
+ @files_on_s3 = s3_files_for @dummy.avatar
395
+ end
396
+
397
+ should "have the same contents as the original" do
398
+ @file.rewind
399
+ assert_equal @file.read, @files_on_s3[:original].read
400
+ end
401
+
402
+ should "write and delete its files" do
403
+ [["434x66", :original],
404
+ ["300x46", :large],
405
+ ["100x15", :medium],
406
+ ["32x32", :thumb]].each do |geo, style|
407
+ cmd = %Q[identify -format "%wx%h" "#{@files_on_s3[style].path}"]
408
+ assert_equal geo, `#{cmd}`.chomp, cmd
409
+ end
410
+
411
+ @d2 = Dummy.find(@dummy.id)
412
+ @d2_files = s3_files_for @d2.avatar
413
+ [["434x66", :original],
414
+ ["300x46", :large],
415
+ ["100x15", :medium],
416
+ ["32x32", :thumb]].each do |geo, style|
417
+ cmd = %Q[identify -format "%wx%h" "#{@d2_files[style].path}"]
418
+ assert_equal geo, `#{cmd}`.chomp, cmd
419
+ end
420
+
421
+ @dummy.avatar = "not a valid file but not nil"
422
+ assert_equal File.basename(@file.path), @dummy.avatar_file_name
423
+ assert @dummy.valid?
424
+ assert @dummy.save
425
+
426
+ [:thumb, :medium, :large, :original].each do |style|
427
+ assert @dummy.avatar.exists?(style)
428
+ end
429
+
430
+ @dummy.avatar.clear
431
+ assert_nil @dummy.avatar_file_name
432
+ assert @dummy.valid?
433
+ assert @dummy.save
434
+
435
+ [:thumb, :medium, :large, :original].each do |style|
436
+ assert ! @dummy.avatar.exists?(style)
437
+ end
438
+
439
+ @d2 = Dummy.find(@dummy.id)
440
+ assert_nil @d2.avatar_file_name
441
+ end
442
+
443
+ should "work exactly the same when new as when reloaded" do
444
+ @d2 = Dummy.find(@dummy.id)
445
+
446
+ assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
447
+ [:thumb, :medium, :large, :original].each do |style|
448
+ assert_equal @dummy.avatar.to_file(style).read, @d2.avatar.to_file(style).read
449
+ end
450
+
451
+ saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) }
452
+
453
+ @d2.avatar.clear
454
+ assert @d2.save
455
+
456
+ [:thumb, :medium, :large, :original].each do |style|
457
+ assert ! @dummy.avatar.exists?(style)
458
+ end
459
+ end
460
+
461
+ should "know the difference between good files, bad files, not files, and nil" do
462
+ expected = @dummy.avatar.to_file
463
+ @dummy.avatar = "not a file"
464
+ assert @dummy.valid?
465
+ assert_equal expected.read, @dummy.avatar.to_file.read
466
+
467
+ @dummy.avatar = @bad_file
468
+ assert ! @dummy.valid?
469
+ @dummy.avatar = nil
470
+ assert @dummy.valid?
471
+
472
+ Dummy.validates_attachment_presence :avatar
473
+ @d2 = Dummy.find(@dummy.id)
474
+ @d2.avatar = @file
475
+ assert @d2.valid?
476
+ @d2.avatar = @bad_file
477
+ assert ! @d2.valid?
478
+ @d2.avatar = nil
479
+ assert ! @d2.valid?
480
+ end
481
+
482
+ should "be able to reload without saving and not have the file disappear" do
483
+ @dummy.avatar = @file
484
+ assert @dummy.save
485
+ @dummy.avatar = nil
486
+ assert_nil @dummy.avatar_file_name
487
+ @dummy.reload
488
+ assert_equal "5k.png", @dummy.avatar_file_name
489
+ end
490
+
491
+ should "have the right content type" do
492
+ headers = s3_headers_for(@dummy.avatar, :original)
493
+ assert_equal 'image/png', headers['content-type']
494
+ end
495
+ end
496
+ end
497
+ end
498
+