paperclip 3.0.3 → 3.5.1

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 (121) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +2 -1
  3. data/.travis.yml +3 -0
  4. data/Appraisals +8 -3
  5. data/Gemfile +1 -1
  6. data/LICENSE +1 -1
  7. data/NEWS +198 -35
  8. data/README.md +332 -113
  9. data/features/basic_integration.feature +24 -12
  10. data/features/migration.feature +94 -0
  11. data/features/rake_tasks.feature +2 -3
  12. data/features/step_definitions/attachment_steps.rb +28 -0
  13. data/features/step_definitions/rails_steps.rb +94 -8
  14. data/features/step_definitions/s3_steps.rb +1 -1
  15. data/features/step_definitions/web_steps.rb +3 -3
  16. data/features/support/fakeweb.rb +4 -1
  17. data/features/support/file_helpers.rb +10 -0
  18. data/features/support/rails.rb +18 -2
  19. data/gemfiles/3.0.gemfile +2 -2
  20. data/gemfiles/3.1.gemfile +2 -2
  21. data/gemfiles/3.2.gemfile +2 -2
  22. data/gemfiles/4.0.gemfile +11 -0
  23. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +4 -8
  24. data/lib/paperclip/attachment.rb +96 -43
  25. data/lib/paperclip/attachment_registry.rb +57 -0
  26. data/lib/paperclip/callbacks.rb +2 -2
  27. data/lib/paperclip/content_type_detector.rb +78 -0
  28. data/lib/paperclip/file_command_content_type_detector.rb +32 -0
  29. data/lib/paperclip/filename_cleaner.rb +16 -0
  30. data/lib/paperclip/geometry.rb +66 -30
  31. data/lib/paperclip/geometry_detector_factory.rb +41 -0
  32. data/lib/paperclip/geometry_parser_factory.rb +31 -0
  33. data/lib/paperclip/glue.rb +2 -8
  34. data/lib/paperclip/has_attached_file.rb +99 -0
  35. data/lib/paperclip/helpers.rb +12 -15
  36. data/lib/paperclip/interpolations/plural_cache.rb +17 -0
  37. data/lib/paperclip/interpolations.rb +15 -5
  38. data/lib/paperclip/io_adapters/abstract_adapter.rb +45 -0
  39. data/lib/paperclip/io_adapters/attachment_adapter.rb +14 -49
  40. data/lib/paperclip/io_adapters/data_uri_adapter.rb +27 -0
  41. data/lib/paperclip/io_adapters/empty_string_adapter.rb +18 -0
  42. data/lib/paperclip/io_adapters/file_adapter.rb +8 -69
  43. data/lib/paperclip/io_adapters/identity_adapter.rb +1 -1
  44. data/lib/paperclip/io_adapters/nil_adapter.rb +2 -2
  45. data/lib/paperclip/io_adapters/stringio_adapter.rb +16 -45
  46. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +17 -40
  47. data/lib/paperclip/io_adapters/uri_adapter.rb +44 -0
  48. data/lib/paperclip/matchers/have_attached_file_matcher.rb +1 -5
  49. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +36 -17
  50. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +5 -1
  51. data/lib/paperclip/matchers.rb +3 -3
  52. data/lib/paperclip/missing_attachment_styles.rb +11 -16
  53. data/lib/paperclip/processor.rb +12 -0
  54. data/lib/paperclip/railtie.rb +5 -1
  55. data/lib/paperclip/schema.rb +59 -23
  56. data/lib/paperclip/storage/filesystem.rb +23 -5
  57. data/lib/paperclip/storage/fog.rb +64 -25
  58. data/lib/paperclip/storage/s3.rb +93 -52
  59. data/lib/paperclip/style.rb +2 -2
  60. data/lib/paperclip/tempfile_factory.rb +21 -0
  61. data/lib/paperclip/thumbnail.rb +18 -3
  62. data/lib/paperclip/validators/attachment_content_type_validator.rb +38 -10
  63. data/lib/paperclip/validators/attachment_presence_validator.rb +8 -8
  64. data/lib/paperclip/validators/attachment_size_validator.rb +12 -7
  65. data/lib/paperclip/validators.rb +21 -2
  66. data/lib/paperclip/version.rb +1 -1
  67. data/lib/paperclip.rb +15 -44
  68. data/lib/tasks/paperclip.rake +26 -7
  69. data/paperclip.gemspec +11 -7
  70. data/test/attachment_definitions_test.rb +12 -0
  71. data/test/attachment_processing_test.rb +83 -0
  72. data/test/attachment_registry_test.rb +77 -0
  73. data/test/attachment_test.rb +253 -44
  74. data/test/content_type_detector_test.rb +50 -0
  75. data/test/file_command_content_type_detector_test.rb +25 -0
  76. data/test/filename_cleaner_test.rb +14 -0
  77. data/test/fixtures/animated +0 -0
  78. data/test/fixtures/animated.unknown +0 -0
  79. data/test/fixtures/rotated.jpg +0 -0
  80. data/test/generator_test.rb +26 -24
  81. data/test/geometry_detector_test.rb +24 -0
  82. data/test/geometry_parser_test.rb +73 -0
  83. data/test/geometry_test.rb +55 -4
  84. data/test/has_attached_file_test.rb +125 -0
  85. data/test/helper.rb +38 -7
  86. data/test/integration_test.rb +105 -89
  87. data/test/interpolations_test.rb +12 -0
  88. data/test/io_adapters/abstract_adapter_test.rb +58 -0
  89. data/test/io_adapters/attachment_adapter_test.rb +120 -33
  90. data/test/io_adapters/data_uri_adapter_test.rb +60 -0
  91. data/test/io_adapters/empty_string_adapter_test.rb +17 -0
  92. data/test/io_adapters/file_adapter_test.rb +32 -1
  93. data/test/io_adapters/stringio_adapter_test.rb +29 -10
  94. data/test/io_adapters/uploaded_file_adapter_test.rb +53 -5
  95. data/test/io_adapters/uri_adapter_test.rb +102 -0
  96. data/test/matchers/validate_attachment_presence_matcher_test.rb +22 -0
  97. data/test/meta_class_test.rb +32 -0
  98. data/test/paperclip_missing_attachment_styles_test.rb +4 -8
  99. data/test/paperclip_test.rb +27 -51
  100. data/test/plural_cache_test.rb +36 -0
  101. data/test/processor_test.rb +16 -0
  102. data/test/rake_test.rb +103 -0
  103. data/test/schema_test.rb +179 -77
  104. data/test/storage/filesystem_test.rb +26 -3
  105. data/test/storage/fog_test.rb +181 -3
  106. data/test/storage/s3_test.rb +239 -4
  107. data/test/style_test.rb +18 -14
  108. data/test/tempfile_factory_test.rb +13 -0
  109. data/test/thumbnail_test.rb +96 -16
  110. data/test/validators/attachment_content_type_validator_test.rb +181 -55
  111. data/test/validators/attachment_size_validator_test.rb +10 -0
  112. data/test/validators_test.rb +8 -1
  113. metadata +126 -92
  114. data/Gemfile.lock +0 -157
  115. data/features/support/fixtures/.boot_config.rb.swo +0 -0
  116. data/images.rake +0 -21
  117. data/lib/.DS_Store +0 -0
  118. data/lib/paperclip/.DS_Store +0 -0
  119. data/lib/paperclip/attachment_options.rb +0 -9
  120. data/lib/paperclip/instance_methods.rb +0 -35
  121. data/test/attachment_options_test.rb +0 -27
@@ -125,7 +125,70 @@ class S3Test < Test::Unit::TestCase
125
125
  should "use the correct key" do
126
126
  assert_equal "avatars/stringio.txt", @dummy.avatar.s3_object.key
127
127
  end
128
+ end
129
+
130
+ context "s3_protocol" do
131
+ ["http", :http, ""].each do |protocol|
132
+ context "as #{protocol.inspect}" do
133
+ setup do
134
+ rebuild_model :storage => :s3, :s3_protocol => protocol
135
+
136
+ @dummy = Dummy.new
137
+ end
138
+
139
+ should "return the s3_protocol in string" do
140
+ assert_equal protocol.to_s, @dummy.avatar.s3_protocol
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ context ":s3_protocol => 'https'" do
147
+ setup do
148
+ rebuild_model :storage => :s3,
149
+ :s3_credentials => {},
150
+ :s3_protocol => 'https',
151
+ :bucket => "bucket",
152
+ :path => ":attachment/:basename.:extension"
153
+ @dummy = Dummy.new
154
+ @dummy.avatar = StringIO.new(".")
155
+ end
156
+
157
+ should "return a url based on an S3 path" do
158
+ assert_match %r{^https://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url
159
+ end
160
+ end
161
+
162
+ context ":s3_protocol => :https" do
163
+ setup do
164
+ rebuild_model :storage => :s3,
165
+ :s3_credentials => {},
166
+ :s3_protocol => :https,
167
+ :bucket => "bucket",
168
+ :path => ":attachment/:basename.:extension"
169
+ @dummy = Dummy.new
170
+ @dummy.avatar = StringIO.new(".")
171
+ end
128
172
 
173
+ should "return a url based on an S3 path" do
174
+ assert_match %r{^https://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url
175
+ end
176
+ end
177
+
178
+ context ":s3_protocol => ''" do
179
+ setup do
180
+ rebuild_model :storage => :s3,
181
+ :s3_credentials => {},
182
+ :s3_protocol => '',
183
+ :bucket => "bucket",
184
+ :path => ":attachment/:basename.:extension"
185
+ @dummy = Dummy.new
186
+ @dummy.avatar = StringIO.new(".")
187
+ end
188
+
189
+ should "return a url based on an S3 path" do
190
+ assert_match %r{^//s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url
191
+ end
129
192
  end
130
193
 
131
194
  context "An attachment that uses S3 for storage and has the style in the path" do
@@ -175,6 +238,26 @@ class S3Test < Test::Unit::TestCase
175
238
  end
176
239
  end
177
240
 
241
+ context "dynamic s3_host_name" do
242
+ setup do
243
+ rebuild_model :storage => :s3,
244
+ :s3_credentials => {},
245
+ :bucket => "bucket",
246
+ :path => ":attachment/:basename.:extension",
247
+ :s3_host_name => lambda {|a| a.instance.value }
248
+ @dummy = Dummy.new
249
+ class << @dummy
250
+ attr_accessor :value
251
+ end
252
+ @dummy.avatar = StringIO.new(".")
253
+ end
254
+
255
+ should "use s3_host_name as a proc if available" do
256
+ @dummy.value = "s3.something.com"
257
+ assert_equal "http://s3.something.com/bucket/avatars/stringio.txt", @dummy.avatar.url(:original, :timestamp => false)
258
+ end
259
+ end
260
+
178
261
  context "An attachment that uses S3 for storage and has styles that return different file types" do
179
262
  setup do
180
263
  rebuild_model :styles => { :large => ['500x500#', :jpg] },
@@ -209,6 +292,45 @@ class S3Test < Test::Unit::TestCase
209
292
  end
210
293
  end
211
294
 
295
+ context "An attachment that uses S3 for storage and has a proc for styles" do
296
+ setup do
297
+ rebuild_model :styles => lambda { |attachment| attachment.instance.counter; {:thumbnail => { :geometry => "50x50#", :s3_headers => {'Cache-Control' => 'max-age=31557600'}} }},
298
+ :storage => :s3,
299
+ :bucket => "bucket",
300
+ :path => ":attachment/:style/:basename.:extension",
301
+ :s3_credentials => {
302
+ 'access_key_id' => "12345",
303
+ 'secret_access_key' => "54321"
304
+ }
305
+
306
+ @file = File.new(fixture_file('5k.png'), 'rb')
307
+
308
+ Dummy.class_eval do
309
+ def counter
310
+ @counter ||= 0
311
+ @counter += 1
312
+ @counter
313
+ end
314
+ end
315
+
316
+ @dummy = Dummy.new
317
+ @dummy.avatar = @file
318
+
319
+ object = stub
320
+ @dummy.avatar.stubs(:s3_object).with(:original).returns(object)
321
+ @dummy.avatar.stubs(:s3_object).with(:thumbnail).returns(object)
322
+ object.expects(:write).with(anything, :content_type => 'image/png', :acl => :public_read)
323
+ object.expects(:write).with(anything, :content_type => 'image/png', :acl => :public_read, :cache_control => 'max-age=31557600')
324
+ @dummy.save
325
+ end
326
+
327
+ teardown { @file.close }
328
+
329
+ should "succeed" do
330
+ assert_equal @dummy.counter, 7
331
+ end
332
+ end
333
+
212
334
  context "An attachment that uses S3 for storage and has spaces in file name" do
213
335
  setup do
214
336
  rebuild_model :styles => { :large => ['500x500#', :jpg] },
@@ -244,8 +366,13 @@ class S3Test < Test::Unit::TestCase
244
366
  'secret_access_key' => "54321"
245
367
  }
246
368
 
247
- file = Paperclip.io_adapters.for(StringIO.new("."))
248
- file.original_filename = "question?mark.png"
369
+ stringio = StringIO.new(".")
370
+ class << stringio
371
+ def original_filename
372
+ "question?mark.png"
373
+ end
374
+ end
375
+ file = Paperclip.io_adapters.for(stringio)
249
376
  @dummy = Dummy.new
250
377
  @dummy.avatar = file
251
378
  @dummy.save
@@ -412,6 +539,18 @@ class S3Test < Test::Unit::TestCase
412
539
  end
413
540
  end
414
541
 
542
+ context "#expiring_url" do
543
+ setup { @dummy = Dummy.new }
544
+
545
+ context "with no attachment" do
546
+ setup { assert(!@dummy.avatar.exists?) }
547
+
548
+ should "return the default URL" do
549
+ assert_equal(@dummy.avatar.url, @dummy.avatar.expiring_url)
550
+ end
551
+ end
552
+ end
553
+
415
554
  context "Generating a url with an expiration for each style" do
416
555
  setup do
417
556
  rebuild_model :storage => :s3,
@@ -532,6 +671,21 @@ class S3Test < Test::Unit::TestCase
532
671
  assert_match %r{^http://s3\.amazonaws\.com/testing/avatars/original/5k\.png}, @dummy.avatar.url
533
672
  end
534
673
 
674
+ should "be rewinded after flush_writes" do
675
+ @dummy.avatar.instance_eval "def after_flush_writes; end"
676
+
677
+ files = @dummy.avatar.queued_for_write.values.each(&:read)
678
+ @dummy.save
679
+ assert files.none?(&:eof?), "Expect all the files to be rewinded."
680
+ end
681
+
682
+ should "be removed after after_flush_writes" do
683
+ paths = @dummy.avatar.queued_for_write.values.map(&:path)
684
+ @dummy.save
685
+ assert paths.none?{ |path| File.exists?(path) },
686
+ "Expect all the files to be deleted."
687
+ end
688
+
535
689
  context "and saved" do
536
690
  setup do
537
691
  object = stub
@@ -567,7 +721,7 @@ class S3Test < Test::Unit::TestCase
567
721
  setup do
568
722
  AWS::S3::S3Object.any_instance.stubs(:exists?).returns(true)
569
723
  AWS::S3::S3Object.any_instance.stubs(:delete)
570
- @dummy.destroy_attached_files
724
+ @dummy.destroy
571
725
  end
572
726
 
573
727
  should "succeed" do
@@ -617,6 +771,47 @@ class S3Test < Test::Unit::TestCase
617
771
  end
618
772
  end
619
773
 
774
+ context "An attachment with S3 storage and S3 credentials with a :credential_provider" do
775
+ setup do
776
+ class DummyCredentialProvider; end
777
+
778
+ rebuild_model :storage => :s3,
779
+ :bucket => "testing",
780
+ :s3_credentials => {
781
+ :credential_provider => DummyCredentialProvider.new
782
+ }
783
+ @dummy = Dummy.new
784
+ end
785
+
786
+ should "set the credential-provider" do
787
+ assert_kind_of DummyCredentialProvider, @dummy.avatar.s3_bucket.config.credential_provider
788
+ end
789
+ end
790
+
791
+ context "An attachment with S3 storage and S3 credentials in an unsupported manor" do
792
+ setup do
793
+ rebuild_model :storage => :s3, :bucket => "testing", :s3_credentials => ["unsupported"]
794
+ @dummy = Dummy.new
795
+ end
796
+
797
+ should "not accept the credentials" do
798
+ assert_raise(ArgumentError) do
799
+ @dummy.avatar.s3_credentials
800
+ end
801
+ end
802
+ end
803
+
804
+ context "An attachment with S3 storage and S3 credentials not supplied" do
805
+ setup do
806
+ rebuild_model :storage => :s3, :bucket => "testing"
807
+ @dummy = Dummy.new
808
+ end
809
+
810
+ should "not parse any credentials" do
811
+ assert_equal({}, @dummy.avatar.s3_credentials)
812
+ end
813
+ end
814
+
620
815
  context "An attachment with S3 storage and specific s3 headers set" do
621
816
  setup do
622
817
  rebuild_model :storage => :s3,
@@ -773,6 +968,46 @@ class S3Test < Test::Unit::TestCase
773
968
  end
774
969
  end
775
970
 
971
+ context "Can disable AES256 encryption multiple ways" do
972
+ [nil, false, ''].each do |tech|
973
+ setup do
974
+ rebuild_model(
975
+ :storage => :s3,
976
+ :bucket => "testing",
977
+ :path => ":attachment/:style/:basename.:extension",
978
+ :s3_credentials => {
979
+ 'access_key_id' => "12345",
980
+ 'secret_access_key' => "54321"},
981
+ :s3_server_side_encryption => tech)
982
+ end
983
+
984
+ context "when assigned" do
985
+ setup do
986
+ @file = File.new(fixture_file('5k.png'), 'rb')
987
+ @dummy = Dummy.new
988
+ @dummy.avatar = @file
989
+ end
990
+
991
+ teardown { @file.close }
992
+
993
+ context "and saved" do
994
+ setup do
995
+ object = stub
996
+ @dummy.avatar.stubs(:s3_object).returns(object)
997
+ object.expects(:write).with(anything,
998
+ :content_type => "image/png",
999
+ :acl => :public_read)
1000
+ @dummy.save
1001
+ end
1002
+
1003
+ should "succeed" do
1004
+ assert true
1005
+ end
1006
+ end
1007
+ end
1008
+ end
1009
+ end
1010
+
776
1011
  context "An attachment with S3 storage and using AES256 encryption" do
777
1012
  setup do
778
1013
  rebuild_model :storage => :s3,
@@ -801,7 +1036,7 @@ class S3Test < Test::Unit::TestCase
801
1036
  object.expects(:write).with(anything,
802
1037
  :content_type => "image/png",
803
1038
  :acl => :public_read,
804
- :server_side_encryption => :aes256)
1039
+ :server_side_encryption => 'AES256')
805
1040
  @dummy.save
806
1041
  end
807
1042
 
data/test/style_test.rb CHANGED
@@ -96,40 +96,44 @@ class StyleTest < Test::Unit::TestCase
96
96
  end
97
97
 
98
98
  context "An attachment with :convert_options" do
99
- setup do
99
+ should "not have called extra_options_for(:thumb/:large) on initialization" do
100
100
  @attachment = attachment :path => ":basename.:extension",
101
101
  :styles => {:thumb => "100x100", :large => "400x400"},
102
102
  :convert_options => {:all => "-do_stuff", :thumb => "-thumbnailize"}
103
- @style = @attachment.styles[:thumb]
104
- @file = StringIO.new("...")
105
- @file.stubs(:original_filename).returns("file.jpg")
106
- end
107
-
108
- before_should "not have called extra_options_for(:thumb/:large) on initialization" do
109
103
  @attachment.expects(:extra_options_for).never
104
+ @style = @attachment.styles[:thumb]
110
105
  end
111
106
 
112
107
  should "call extra_options_for(:thumb/:large) when convert options are requested" do
108
+ @attachment = attachment :path => ":basename.:extension",
109
+ :styles => {:thumb => "100x100", :large => "400x400"},
110
+ :convert_options => {:all => "-do_stuff", :thumb => "-thumbnailize"}
111
+ @style = @attachment.styles[:thumb]
112
+ @file = StringIO.new("...")
113
+ @file.stubs(:original_filename).returns("file.jpg")
114
+
113
115
  @attachment.expects(:extra_options_for).with(:thumb)
114
116
  @attachment.styles[:thumb].convert_options
115
117
  end
116
118
  end
117
119
 
118
120
  context "An attachment with :source_file_options" do
119
- setup do
121
+ should "not have called extra_source_file_options_for(:thumb/:large) on initialization" do
120
122
  @attachment = attachment :path => ":basename.:extension",
121
123
  :styles => {:thumb => "100x100", :large => "400x400"},
122
124
  :source_file_options => {:all => "-density 400", :thumb => "-depth 8"}
123
- @style = @attachment.styles[:thumb]
124
- @file = StringIO.new("...")
125
- @file.stubs(:original_filename).returns("file.jpg")
126
- end
127
-
128
- before_should "not have called extra_source_file_options_for(:thumb/:large) on initialization" do
129
125
  @attachment.expects(:extra_source_file_options_for).never
126
+ @style = @attachment.styles[:thumb]
130
127
  end
131
128
 
132
129
  should "call extra_options_for(:thumb/:large) when convert options are requested" do
130
+ @attachment = attachment :path => ":basename.:extension",
131
+ :styles => {:thumb => "100x100", :large => "400x400"},
132
+ :source_file_options => {:all => "-density 400", :thumb => "-depth 8"}
133
+ @style = @attachment.styles[:thumb]
134
+ @file = StringIO.new("...")
135
+ @file.stubs(:original_filename).returns("file.jpg")
136
+
133
137
  @attachment.expects(:extra_source_file_options_for).with(:thumb)
134
138
  @attachment.styles[:thumb].source_file_options
135
139
  end
@@ -0,0 +1,13 @@
1
+ require './test/helper'
2
+
3
+ class Paperclip::TempfileFactoryTest < Test::Unit::TestCase
4
+ should "be able to generate a tempfile with the right name" do
5
+ file = subject.generate("omg.png")
6
+ end
7
+ should "be able to generate a tempfile with the right name with a tilde at the beginning" do
8
+ file = subject.generate("~omg.png")
9
+ end
10
+ should "be able to generate a tempfile with the right name with a tilde at the end" do
11
+ file = subject.generate("omg.png~")
12
+ end
13
+ end
@@ -80,6 +80,8 @@ class ThumbnailTest < Test::Unit::TestCase
80
80
  should "let us know when a command isn't found versus a processing error" do
81
81
  old_path = ENV['PATH']
82
82
  begin
83
+ Cocaine::CommandLine.path = ''
84
+ Paperclip.options[:command_path] = ''
83
85
  ENV['PATH'] = ''
84
86
  assert_raises(Paperclip::Errors::CommandNotFoundError) do
85
87
  silence_stream(STDERR) do
@@ -113,10 +115,9 @@ class ThumbnailTest < Test::Unit::TestCase
113
115
  end
114
116
 
115
117
  should "send the right command to convert when sent #make" do
116
- Paperclip.expects(:run).with do |*arg|
117
- arg[0] == 'convert' &&
118
- arg[1] == ':source -resize "x50" -crop "100x50+114+0" +repage :dest' &&
119
- arg[2][:source] == "#{File.expand_path(@thumb.file.path)}[0]"
118
+ @thumb.expects(:convert).with do |*arg|
119
+ arg[0] == ':source -auto-orient -resize "x50" -crop "100x50+114+0" +repage :dest' &&
120
+ arg[1][:source] == "#{File.expand_path(@thumb.file.path)}[0]"
120
121
  end
121
122
  @thumb.make
122
123
  end
@@ -127,6 +128,16 @@ class ThumbnailTest < Test::Unit::TestCase
127
128
  end
128
129
  end
129
130
 
131
+ should 'properly crop a EXIF-rotated image' do
132
+ file = File.new(fixture_file('rotated.jpg'))
133
+ thumb = Paperclip::Thumbnail.new(file, :geometry => "50x50#")
134
+
135
+ output_file = thumb.make
136
+
137
+ command = Cocaine::CommandLine.new("identify", "-format %wx%h :file")
138
+ assert_equal "50x50", command.run(:file => output_file.path).strip
139
+ end
140
+
130
141
  context "being thumbnailed with source file options set" do
131
142
  setup do
132
143
  @thumb = Paperclip::Thumbnail.new(@file,
@@ -139,10 +150,9 @@ class ThumbnailTest < Test::Unit::TestCase
139
150
  end
140
151
 
141
152
  should "send the right command to convert when sent #make" do
142
- Paperclip.expects(:run).with do |*arg|
143
- arg[0] == 'convert' &&
144
- arg[1] == '-strip :source -resize "x50" -crop "100x50+114+0" +repage :dest' &&
145
- arg[2][:source] == "#{File.expand_path(@thumb.file.path)}[0]"
153
+ @thumb.expects(:convert).with do |*arg|
154
+ arg[0] == '-strip :source -auto-orient -resize "x50" -crop "100x50+114+0" +repage :dest' &&
155
+ arg[1][:source] == "#{File.expand_path(@thumb.file.path)}[0]"
146
156
  end
147
157
  @thumb.make
148
158
  end
@@ -181,10 +191,9 @@ class ThumbnailTest < Test::Unit::TestCase
181
191
  end
182
192
 
183
193
  should "send the right command to convert when sent #make" do
184
- Paperclip.expects(:run).with do |*arg|
185
- arg[0] == 'convert' &&
186
- arg[1] == ':source -resize "x50" -crop "100x50+114+0" +repage -strip -depth 8 :dest' &&
187
- arg[2][:source] == "#{File.expand_path(@thumb.file.path)}[0]"
194
+ @thumb.expects(:convert).with do |*arg|
195
+ arg[0] == ':source -auto-orient -resize "x50" -crop "100x50+114+0" +repage -strip -depth 8 :dest' &&
196
+ arg[1][:source] == "#{File.expand_path(@thumb.file.path)}[0]"
188
197
  end
189
198
  @thumb.make
190
199
  end
@@ -212,6 +221,8 @@ class ThumbnailTest < Test::Unit::TestCase
212
221
  should "let us know when a command isn't found versus a processing error" do
213
222
  old_path = ENV['PATH']
214
223
  begin
224
+ Cocaine::CommandLine.path = ''
225
+ Paperclip.options[:command_path] = ''
215
226
  ENV['PATH'] = ''
216
227
  assert_raises(Paperclip::Errors::CommandNotFoundError) do
217
228
  silence_stream(STDERR) do
@@ -237,6 +248,17 @@ class ThumbnailTest < Test::Unit::TestCase
237
248
  end
238
249
  end
239
250
 
251
+ context "being thumbnailed with default animated option (true)" do
252
+ should "call identify to check for animated images when sent #make" do
253
+ thumb = Paperclip::Thumbnail.new(@file, :geometry => "100x50#")
254
+ thumb.expects(:identify).at_least_once.with do |*arg|
255
+ arg[0] == '-format %m :file' &&
256
+ arg[1][:file] == "#{File.expand_path(thumb.file.path)}[0]"
257
+ end
258
+ thumb.make
259
+ end
260
+ end
261
+
240
262
  context "passing a custom file geometry parser" do
241
263
  teardown do
242
264
  self.class.send(:remove_const, :GeoParser)
@@ -358,13 +380,19 @@ class ThumbnailTest < Test::Unit::TestCase
358
380
 
359
381
  should "create the 12 frames thumbnail when sent #make" do
360
382
  dst = @thumb.make
361
- cmd = %Q[identify -format "%wx%h" "#{dst.path}"]
362
- assert_equal "50x50"*12, `#{cmd}`.chomp
383
+ cmd = %Q[identify -format "%wx%h," "#{dst.path}"]
384
+ frames = `#{cmd}`.chomp.split(',')
385
+ assert_equal 12, frames.size
386
+ assert_frame_dimensions (45..50), frames
363
387
  end
364
388
 
365
389
  should "use the -coalesce option" do
366
390
  assert_equal @thumb.transformation_command.first, "-coalesce"
367
391
  end
392
+
393
+ should "use the -layers 'optimize' option" do
394
+ assert_equal @thumb.transformation_command.last, '-layers "optimize"'
395
+ end
368
396
  end
369
397
 
370
398
  context "with omitted output format" do
@@ -374,13 +402,65 @@ class ThumbnailTest < Test::Unit::TestCase
374
402
 
375
403
  should "create the 12 frames thumbnail when sent #make" do
376
404
  dst = @thumb.make
377
- cmd = %Q[identify -format "%wx%h" "#{dst.path}"]
378
- assert_equal "50x50"*12, `#{cmd}`.chomp
405
+ cmd = %Q[identify -format "%wx%h," "#{dst.path}"]
406
+ frames = `#{cmd}`.chomp.split(',')
407
+ assert_equal 12, frames.size
408
+ assert_frame_dimensions (45..50), frames
409
+ end
410
+
411
+ should "use the -coalesce option" do
412
+ assert_equal @thumb.transformation_command.first, "-coalesce"
413
+ end
414
+
415
+ should "use the -layers 'optimize' option" do
416
+ assert_equal @thumb.transformation_command.last, '-layers "optimize"'
417
+ end
418
+ end
419
+
420
+ context "with unidentified source format" do
421
+ setup do
422
+ @unidentified_file = File.new(fixture_file("animated.unknown"), 'rb')
423
+ @thumb = Paperclip::Thumbnail.new(@file, :geometry => "60x60")
424
+ end
425
+
426
+ should "create the 12 frames thumbnail when sent #make" do
427
+ dst = @thumb.make
428
+ cmd = %Q[identify -format "%wx%h," "#{dst.path}"]
429
+ frames = `#{cmd}`.chomp.split(',')
430
+ assert_equal 12, frames.size
431
+ assert_frame_dimensions (55..60), frames
379
432
  end
380
433
 
381
434
  should "use the -coalesce option" do
382
435
  assert_equal @thumb.transformation_command.first, "-coalesce"
383
436
  end
437
+
438
+ should "use the -layers 'optimize' option" do
439
+ assert_equal @thumb.transformation_command.last, '-layers "optimize"'
440
+ end
441
+ end
442
+
443
+ context "with no source format" do
444
+ setup do
445
+ @unidentified_file = File.new(fixture_file("animated"), 'rb')
446
+ @thumb = Paperclip::Thumbnail.new(@file, :geometry => "70x70")
447
+ end
448
+
449
+ should "create the 12 frames thumbnail when sent #make" do
450
+ dst = @thumb.make
451
+ cmd = %Q[identify -format "%wx%h," "#{dst.path}"]
452
+ frames = `#{cmd}`.chomp.split(',')
453
+ assert_equal 12, frames.size
454
+ assert_frame_dimensions (60..70), frames
455
+ end
456
+
457
+ should "use the -coalesce option" do
458
+ assert_equal @thumb.transformation_command.first, "-coalesce"
459
+ end
460
+
461
+ should "use the -layers 'optimize' option" do
462
+ assert_equal @thumb.transformation_command.last, '-layers "optimize"'
463
+ end
384
464
  end
385
465
 
386
466
  context "with animated option set to false" do