paperclip 3.0.2 → 3.0.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of paperclip might be problematic. Click here for more details.

Files changed (47) hide show
  1. data/CONTRIBUTING.md +34 -2
  2. data/NEWS +33 -1
  3. data/README.md +20 -6
  4. data/RUNNING_TESTS.md +4 -0
  5. data/features/basic_integration.feature +4 -2
  6. data/features/support/fakeweb.rb +7 -0
  7. data/lib/paperclip.rb +2 -1
  8. data/lib/paperclip/attachment.rb +28 -16
  9. data/lib/paperclip/glue.rb +8 -0
  10. data/lib/paperclip/helpers.rb +4 -16
  11. data/lib/paperclip/instance_methods.rb +1 -1
  12. data/lib/paperclip/io_adapters/attachment_adapter.rb +13 -4
  13. data/lib/paperclip/io_adapters/file_adapter.rb +5 -3
  14. data/lib/paperclip/io_adapters/stringio_adapter.rb +4 -2
  15. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +3 -1
  16. data/lib/paperclip/missing_attachment_styles.rb +5 -8
  17. data/lib/paperclip/railtie.rb +4 -7
  18. data/lib/paperclip/storage/fog.rb +11 -0
  19. data/lib/paperclip/storage/s3.rb +26 -0
  20. data/lib/paperclip/tempfile.rb +7 -5
  21. data/lib/paperclip/validators.rb +1 -0
  22. data/lib/paperclip/validators/attachment_content_type_validator.rb +8 -1
  23. data/lib/paperclip/version.rb +1 -1
  24. data/lib/tasks/paperclip.rake +1 -1
  25. data/paperclip.gemspec +3 -3
  26. data/test/attachment_test.rb +77 -23
  27. data/test/geometry_test.rb +3 -3
  28. data/test/helper.rb +10 -10
  29. data/test/integration_test.rb +103 -56
  30. data/test/{attachment_adapter_test.rb → io_adapters/attachment_adapter_test.rb} +4 -1
  31. data/test/io_adapters/file_adapter_test.rb +88 -0
  32. data/test/{identity_adapter_test.rb → io_adapters/identity_adapter_test.rb} +0 -0
  33. data/test/{nil_adapter_test.rb → io_adapters/nil_adapter_test.rb} +0 -0
  34. data/test/{adapter_registry_test.rb → io_adapters/registry_test.rb} +0 -0
  35. data/test/{stringio_adapter_test.rb → io_adapters/stringio_adapter_test.rb} +0 -0
  36. data/test/{uploaded_file_adapter_test.rb → io_adapters/uploaded_file_adapter_test.rb} +0 -0
  37. data/test/paperclip_missing_attachment_styles_test.rb +10 -12
  38. data/test/paperclip_test.rb +4 -2
  39. data/test/storage/filesystem_test.rb +24 -16
  40. data/test/storage/fog_test.rb +14 -5
  41. data/test/storage/s3_live_test.rb +40 -12
  42. data/test/storage/s3_test.rb +13 -9
  43. data/test/style_test.rb +1 -1
  44. data/test/thumbnail_test.rb +7 -3
  45. data/test/validators/attachment_content_type_validator_test.rb +53 -1
  46. metadata +30 -28
  47. data/test/file_adapter_test.rb +0 -43
@@ -30,7 +30,10 @@ ROOT = Pathname(File.expand_path(File.join(File.dirname(__FILE__), '..')))
30
30
  class Test::Unit::TestCase
31
31
  def setup
32
32
  silence_warnings do
33
- Object.const_set(:Rails, stub('Rails', :root => ROOT, :env => 'test'))
33
+ Object.const_set(:Rails, stub('Rails'))
34
+ Rails.stubs(:root).returns(Pathname.new(ROOT).join('tmp'))
35
+ Rails.stubs(:env).returns('test')
36
+ Rails.stubs(:const_defined?).with(:Railtie).returns(false)
34
37
  end
35
38
  end
36
39
  end
@@ -65,6 +68,9 @@ def reset_class class_name
65
68
  include Paperclip::Glue
66
69
  end
67
70
 
71
+ klass.reset_column_information
72
+ klass.connection_pool.clear_table_cache!(klass.table_name) if klass.connection_pool.respond_to?(:clear_table_cache!)
73
+ klass.connection.schema_cache.clear_table_cache!(klass.table_name) if klass.connection.respond_to?(:schema_cache)
68
74
  klass
69
75
  end
70
76
 
@@ -91,15 +97,10 @@ def rebuild_model options = {}
91
97
  end
92
98
 
93
99
  def rebuild_class options = {}
94
- ActiveRecord::Base.send(:include, Paperclip::Glue)
95
- Object.send(:remove_const, "Dummy") rescue nil
96
- Object.const_set("Dummy", Class.new(ActiveRecord::Base))
97
- Paperclip.reset_duplicate_clash_check!
98
- Dummy.class_eval do
99
- include Paperclip::Glue
100
- has_attached_file :avatar, options
100
+ reset_class("Dummy").tap do |klass|
101
+ klass.has_attached_file :avatar, options
102
+ Paperclip.reset_duplicate_clash_check!
101
103
  end
102
- Dummy.reset_column_information
103
104
  end
104
105
 
105
106
  class FakeModel
@@ -116,7 +117,6 @@ class FakeModel
116
117
 
117
118
  def run_paperclip_callbacks name, *args
118
119
  end
119
-
120
120
  end
121
121
 
122
122
  def attachment(options={})
@@ -1,15 +1,20 @@
1
+ # encoding: utf-8
2
+
1
3
  require './test/helper'
4
+ require 'open-uri'
2
5
 
3
6
  class IntegrationTest < Test::Unit::TestCase
4
7
  context "Many models at once" do
5
8
  setup do
6
9
  rebuild_model
7
- @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
10
+ @file = File.new(fixture_file("5k.png"), 'rb')
8
11
  300.times do |i|
9
12
  Dummy.create! :avatar => @file
10
13
  end
11
14
  end
12
15
 
16
+ teardown { @file.close }
17
+
13
18
  should "not exceed the open file limit" do
14
19
  assert_nothing_raised do
15
20
  dummies = Dummy.find(:all)
@@ -22,9 +27,7 @@ class IntegrationTest < Test::Unit::TestCase
22
27
  setup do
23
28
  rebuild_model :styles => { :thumb => "50x50#" }
24
29
  @dummy = Dummy.new
25
- @file = File.new(File.join(File.dirname(__FILE__),
26
- "fixtures",
27
- "5k.png"), 'rb')
30
+ @file = File.new(fixture_file("5k.png"), 'rb')
28
31
  @dummy.avatar = @file
29
32
  assert @dummy.save
30
33
  end
@@ -79,13 +82,11 @@ class IntegrationTest < Test::Unit::TestCase
79
82
 
80
83
  context "Attachment" do
81
84
  setup do
82
- @thumb_path = "./test/../public/system/dummies/avatars/000/000/001/thumb/5k.png"
85
+ @thumb_path = "tmp/public/system/dummies/avatars/000/000/001/thumb/5k.png"
83
86
  File.delete(@thumb_path) if File.exists?(@thumb_path)
84
87
  rebuild_model :styles => { :thumb => "50x50#" }
85
88
  @dummy = Dummy.new
86
- @file = File.new(File.join(File.dirname(__FILE__),
87
- "fixtures",
88
- "5k.png"), 'rb')
89
+ @file = File.new(fixture_file("5k.png"), 'rb')
89
90
 
90
91
  end
91
92
 
@@ -108,15 +109,13 @@ class IntegrationTest < Test::Unit::TestCase
108
109
 
109
110
  context "Attachment with no generated thumbnails" do
110
111
  setup do
111
- @thumb_small_path = "./test/../public/system/dummies/avatars/000/000/001/thumb_small/5k.png"
112
- @thumb_large_path = "./test/../public/system/dummies/avatars/000/000/001/thumb_large/5k.png"
112
+ @thumb_small_path = "tmp/public/system/dummies/avatars/000/000/001/thumb_small/5k.png"
113
+ @thumb_large_path = "tmp/public/system/dummies/avatars/000/000/001/thumb_large/5k.png"
113
114
  File.delete(@thumb_small_path) if File.exists?(@thumb_small_path)
114
115
  File.delete(@thumb_large_path) if File.exists?(@thumb_large_path)
115
116
  rebuild_model :styles => { :thumb_small => "50x50#", :thumb_large => "60x60#" }
116
117
  @dummy = Dummy.new
117
- @file = File.new(File.join(File.dirname(__FILE__),
118
- "fixtures",
119
- "5k.png"), 'rb')
118
+ @file = File.new(fixture_file("5k.png"), 'rb')
120
119
 
121
120
  @dummy.avatar.post_processing = false
122
121
  @dummy.avatar = @file
@@ -153,9 +152,7 @@ class IntegrationTest < Test::Unit::TestCase
153
152
  setup do
154
153
  rebuild_model :styles => { :original => "2x2#" }
155
154
  @dummy = Dummy.new
156
- @file = File.new(File.join(File.dirname(__FILE__),
157
- "fixtures",
158
- "5k.png"), 'rb')
155
+ @file = File.new(fixture_file("5k.png"), 'rb')
159
156
  @dummy.avatar = @file
160
157
  end
161
158
 
@@ -172,9 +169,7 @@ class IntegrationTest < Test::Unit::TestCase
172
169
  :medium => "50x50" },
173
170
  :path => ":rails_root/tmp/:id/:attachments/:style.:extension"
174
171
  @dummy = Dummy.new
175
- @file = File.new(File.join(File.dirname(__FILE__),
176
- "fixtures",
177
- "5k.png"), 'rb')
172
+ @file = File.new(fixture_file("5k.png"), 'rb')
178
173
  @dummy.avatar = @file
179
174
  end
180
175
 
@@ -281,12 +276,13 @@ class IntegrationTest < Test::Unit::TestCase
281
276
  setup do
282
277
  rebuild_model
283
278
  @dummy = Dummy.new
284
- @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
279
+ @file = File.new(fixture_file("5k.png"), 'rb')
285
280
  @umask = File.umask(umask)
286
281
  end
287
282
 
288
283
  teardown do
289
284
  File.umask @umask
285
+ @file.close
290
286
  end
291
287
 
292
288
  should "respect the current umask" do
@@ -306,14 +302,16 @@ class IntegrationTest < Test::Unit::TestCase
306
302
  :url => "/:attachment/:class/:style/:id/:basename.:extension",
307
303
  :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
308
304
  @dummy = Dummy.new
309
- @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
310
- @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
305
+ @file = File.new(fixture_file("5k.png"), 'rb')
306
+ @bad_file = File.new(fixture_file("bad.png"), 'rb')
311
307
 
312
308
  assert @dummy.avatar = @file
313
309
  assert @dummy.valid?, @dummy.errors.full_messages.join(", ")
314
310
  assert @dummy.save
315
311
  end
316
312
 
313
+ teardown { [@file, @bad_file].each(&:close) }
314
+
317
315
  should "write and delete its files" do
318
316
  [["434x66", :original],
319
317
  ["300x46", :large],
@@ -402,11 +400,13 @@ class IntegrationTest < Test::Unit::TestCase
402
400
  context "that is assigned its file from another Paperclip attachment" do
403
401
  setup do
404
402
  @dummy2 = Dummy.new
405
- @file2 = File.new(File.join(FIXTURES_DIR, "12k.png"), 'rb')
403
+ @file2 = File.new(fixture_file("12k.png"), 'rb')
406
404
  assert @dummy2.avatar = @file2
407
405
  @dummy2.save
408
406
  end
409
407
 
408
+ teardown { @file2.close }
409
+
410
410
  should "work when assigned a file" do
411
411
  assert_not_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
412
412
  `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
@@ -427,20 +427,56 @@ class IntegrationTest < Test::Unit::TestCase
427
427
  has_many :attachments, :class_name => 'Dummy'
428
428
  end
429
429
 
430
+ @file = File.new(fixture_file("5k.png"), 'rb')
430
431
  @dummy = Dummy.new
431
- @dummy.avatar = File.new(File.join(File.dirname(__FILE__),
432
- "fixtures",
433
- "5k.png"), 'rb')
432
+ @dummy.avatar = @file
434
433
  end
435
434
 
435
+ teardown { @file.close }
436
+
436
437
  should "should not error when saving" do
437
- assert_nothing_raised do
438
- @dummy.save!
438
+ @dummy.save!
439
+ end
440
+ end
441
+
442
+ context "A model with an attachment with hash in file name" do
443
+ setup do
444
+ @settings = { :styles => { :thumb => "50x50#" },
445
+ :path => ":rails_root/public/system/:attachment/:id_partition/:style/:hash.:extension",
446
+ :url => "/system/:attachment/:id_partition/:style/:hash.:extension",
447
+ :hash_secret => "somesecret" }
448
+
449
+ rebuild_model @settings
450
+
451
+ @file = File.new(fixture_file("5k.png"), 'rb')
452
+ @dummy = Dummy.create! :avatar => @file
453
+ end
454
+
455
+ teardown do
456
+ @file.close
457
+ end
458
+
459
+ should "be accessible" do
460
+ assert File.exists?(@dummy.avatar.path(:original))
461
+ assert File.exists?(@dummy.avatar.path(:thumb))
462
+ end
463
+
464
+ context "when new style is added" do
465
+ setup do
466
+ @dummy.avatar.options[:styles][:mini] = "25x25#"
467
+ @dummy.avatar.instance_variable_set :@normalized_styles, nil
468
+ @dummy.avatar.reprocess! 'mini'
469
+ end
470
+
471
+ should "make all the styles accessible" do
472
+ assert File.exists?(@dummy.avatar.path(:original))
473
+ assert File.exists?(@dummy.avatar.path(:thumb))
474
+ assert File.exists?(@dummy.avatar.path(:mini))
439
475
  end
440
476
  end
441
477
  end
442
478
 
443
- if ENV['S3_TEST_BUCKET']
479
+ if ENV['S3_BUCKET']
444
480
  def s3_files_for attachment
445
481
  [:thumb, :medium, :large, :original].inject({}) do |files, style|
446
482
  data = `curl "#{attachment.url(style)}" 2>/dev/null`.chomp
@@ -467,19 +503,27 @@ class IntegrationTest < Test::Unit::TestCase
467
503
  :medium => "100x100",
468
504
  :thumb => ["32x32#", :gif] },
469
505
  :storage => :s3,
470
- :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml")),
506
+ :s3_credentials => File.new(fixture_file('s3.yml')),
507
+ :s3_options => { :logger => Paperclip.logger },
471
508
  :default_style => :medium,
472
- :bucket => ENV['S3_TEST_BUCKET'],
509
+ :bucket => ENV['S3_BUCKET'],
473
510
  :path => ":class/:attachment/:id/:style/:basename.:extension"
511
+
474
512
  @dummy = Dummy.new
475
- @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
476
- @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
513
+ @file = File.new(fixture_file('5k.png'), 'rb')
514
+ @bad_file = File.new(fixture_file('bad.png'), 'rb')
477
515
 
478
- assert @dummy.avatar = @file
479
- assert @dummy.valid?
480
- assert @dummy.save
516
+ @dummy.avatar = @file
517
+ @dummy.valid?
518
+ @dummy.save!
481
519
 
482
- @files_on_s3 = s3_files_for @dummy.avatar
520
+ @files_on_s3 = s3_files_for(@dummy.avatar)
521
+ end
522
+
523
+ teardown do
524
+ @file.close
525
+ @bad_file.close
526
+ @files_on_s3.values.each(&:close) if @files_on_s3
483
527
  end
484
528
 
485
529
  context 'assigning itself to a new model' do
@@ -517,15 +561,6 @@ class IntegrationTest < Test::Unit::TestCase
517
561
  assert_equal geo, `#{cmd}`.chomp, cmd
518
562
  end
519
563
 
520
- @dummy.avatar = "not a valid file but not nil"
521
- assert_equal File.basename(@file.path), @dummy.avatar_file_name
522
- assert @dummy.valid?
523
- assert @dummy.save
524
-
525
- [:thumb, :medium, :large, :original].each do |style|
526
- assert @dummy.avatar.exists?(style)
527
- end
528
-
529
564
  @dummy.avatar.clear
530
565
  assert_nil @dummy.avatar_file_name
531
566
  assert @dummy.valid?
@@ -543,12 +578,18 @@ class IntegrationTest < Test::Unit::TestCase
543
578
  @d2 = Dummy.find(@dummy.id)
544
579
 
545
580
  assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
581
+
546
582
  [:thumb, :medium, :large, :original].each do |style|
547
- assert_equal @dummy.avatar.to_file(style).read, @d2.avatar.to_file(style).read
583
+ begin
584
+ first_file = open(@dummy.avatar.url(style))
585
+ second_file = open(@dummy.avatar.url(style))
586
+ assert_equal first_file.read, second_file.read
587
+ ensure
588
+ first_file.close if first_file
589
+ second_file.close if second_file
590
+ end
548
591
  end
549
592
 
550
- saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) }
551
-
552
593
  @d2.avatar.clear
553
594
  assert @d2.save
554
595
 
@@ -557,12 +598,7 @@ class IntegrationTest < Test::Unit::TestCase
557
598
  end
558
599
  end
559
600
 
560
- should "know the difference between good files, bad files, not files, and nil" do
561
- expected = @dummy.avatar.to_file
562
- @dummy.avatar = "not a file"
563
- assert @dummy.valid?
564
- assert_equal expected.read, @dummy.avatar.to_file.read
565
-
601
+ should "know the difference between good files, bad files, and nil" do
566
602
  @dummy.avatar = @bad_file
567
603
  assert ! @dummy.valid?
568
604
  @dummy.avatar = nil
@@ -591,7 +627,18 @@ class IntegrationTest < Test::Unit::TestCase
591
627
  headers = s3_headers_for(@dummy.avatar, :original)
592
628
  assert_equal 'image/png', headers['content-type']
593
629
  end
630
+
631
+ context "with non-english character in the file name" do
632
+ setup do
633
+
634
+ @file.stubs(:original_filename).returns("クリップ.png")
635
+ @dummy.avatar = @file
636
+ end
637
+
638
+ should "not raise any error" do
639
+ @dummy.save!
640
+ end
641
+ end
594
642
  end
595
643
  end
596
644
  end
597
-
@@ -12,6 +12,10 @@ class AttachmentAdapterTest < Test::Unit::TestCase
12
12
  @subject = Paperclip.io_adapters.for(@attachment)
13
13
  end
14
14
 
15
+ def teardown
16
+ @file.close
17
+ end
18
+
15
19
  should "get the right filename" do
16
20
  assert_equal "5k.png", @subject.original_filename
17
21
  end
@@ -44,5 +48,4 @@ class AttachmentAdapterTest < Test::Unit::TestCase
44
48
  assert_equal expected.length, actual.length
45
49
  assert_equal expected, actual
46
50
  end
47
-
48
51
  end
@@ -0,0 +1,88 @@
1
+ require './test/helper'
2
+
3
+ class FileAdapterTest < Test::Unit::TestCase
4
+ context "a new instance" do
5
+ context "with normal file" do
6
+ setup do
7
+ @file = File.new(fixture_file("5k.png"))
8
+ @file.binmode
9
+ @subject = Paperclip.io_adapters.for(@file)
10
+ end
11
+
12
+ teardown { @file.close }
13
+
14
+ should "get the right filename" do
15
+ assert_equal "5k.png", @subject.original_filename
16
+ end
17
+
18
+ should "force binmode on tempfile" do
19
+ assert @subject.instance_variable_get("@tempfile").binmode?
20
+ end
21
+
22
+ should "get the content type" do
23
+ assert_equal "image/png", @subject.content_type
24
+ end
25
+
26
+ should "return content type as a string" do
27
+ assert_kind_of String, @subject.content_type
28
+ end
29
+
30
+ should "get the file's size" do
31
+ assert_equal 4456, @subject.size
32
+ end
33
+
34
+ should "return false for a call to nil?" do
35
+ assert ! @subject.nil?
36
+ end
37
+
38
+ should "generate a MD5 hash of the contents" do
39
+ expected = Digest::MD5.file(@file.path).to_s
40
+ assert_equal expected, @subject.fingerprint
41
+ end
42
+
43
+ should "read the contents of the file" do
44
+ expected = @file.read
45
+ assert expected.length > 0
46
+ assert_equal expected, @subject.read
47
+ end
48
+
49
+ context "file with multiple possible content type" do
50
+ setup do
51
+ MIME::Types.stubs(:type_for).returns([MIME::Type.new('image/x-png'), MIME::Type.new('image/png')])
52
+ end
53
+
54
+ should "prefer officially registered mime type" do
55
+ assert_equal "image/png", @subject.content_type
56
+ end
57
+
58
+ should "return content type as a string" do
59
+ assert_kind_of String, @subject.content_type
60
+ end
61
+ end
62
+
63
+ context "file with content type derived from file command on *nix" do
64
+ setup do
65
+ MIME::Types.stubs(:type_for).returns([])
66
+ Paperclip.stubs(:run).returns("application/vnd.ms-office\n")
67
+ end
68
+
69
+ should "return content type without newline character" do
70
+ assert_equal "application/vnd.ms-office", @subject.content_type
71
+ end
72
+ end
73
+ end
74
+
75
+ context "empty file" do
76
+ setup do
77
+ @file = Tempfile.new("file_adapter_test")
78
+ @subject = Paperclip.io_adapters.for(@file)
79
+ end
80
+
81
+ teardown { @file.close }
82
+
83
+ should "provide correct mime-type" do
84
+ assert_equal "application/x-empty", @subject.content_type
85
+ end
86
+ end
87
+ end
88
+ end