ryansch-paperclip 2.3.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/LICENSE +26 -0
  2. data/README.md +239 -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 +384 -0
  12. data/lib/paperclip/attachment.rb +378 -0
  13. data/lib/paperclip/callback_compatability.rb +61 -0
  14. data/lib/paperclip/command_line.rb +86 -0
  15. data/lib/paperclip/geometry.rb +115 -0
  16. data/lib/paperclip/interpolations.rb +130 -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 +3 -0
  26. data/lib/paperclip/storage/filesystem.rb +74 -0
  27. data/lib/paperclip/storage/fog.rb +98 -0
  28. data/lib/paperclip/storage/s3.rb +192 -0
  29. data/lib/paperclip/style.rb +91 -0
  30. data/lib/paperclip/thumbnail.rb +79 -0
  31. data/lib/paperclip/upfile.rb +55 -0
  32. data/lib/paperclip/version.rb +3 -0
  33. data/lib/tasks/paperclip.rake +72 -0
  34. data/rails/init.rb +2 -0
  35. data/shoulda_macros/paperclip.rb +118 -0
  36. data/test/attachment_test.rb +939 -0
  37. data/test/command_line_test.rb +138 -0
  38. data/test/database.yml +4 -0
  39. data/test/fixtures/12k.png +0 -0
  40. data/test/fixtures/50x50.png +0 -0
  41. data/test/fixtures/5k.png +0 -0
  42. data/test/fixtures/bad.png +1 -0
  43. data/test/fixtures/s3.yml +8 -0
  44. data/test/fixtures/text.txt +0 -0
  45. data/test/fixtures/twopage.pdf +0 -0
  46. data/test/fixtures/uppercase.PNG +0 -0
  47. data/test/fog_test.rb +107 -0
  48. data/test/geometry_test.rb +177 -0
  49. data/test/helper.rb +146 -0
  50. data/test/integration_test.rb +570 -0
  51. data/test/interpolations_test.rb +143 -0
  52. data/test/iostream_test.rb +71 -0
  53. data/test/matchers/have_attached_file_matcher_test.rb +24 -0
  54. data/test/matchers/validate_attachment_content_type_matcher_test.rb +47 -0
  55. data/test/matchers/validate_attachment_presence_matcher_test.rb +26 -0
  56. data/test/matchers/validate_attachment_size_matcher_test.rb +51 -0
  57. data/test/paperclip_test.rb +306 -0
  58. data/test/processor_test.rb +10 -0
  59. data/test/storage_test.rb +416 -0
  60. data/test/style_test.rb +163 -0
  61. data/test/thumbnail_test.rb +227 -0
  62. data/test/upfile_test.rb +36 -0
  63. metadata +233 -0
data/test/helper.rb ADDED
@@ -0,0 +1,146 @@
1
+ require 'rubygems'
2
+ require 'tempfile'
3
+ require 'test/unit'
4
+
5
+ require 'shoulda'
6
+ require 'mocha'
7
+
8
+ require 'active_record'
9
+ require 'active_record/version'
10
+ require 'active_support'
11
+
12
+ puts "Testing against version #{ActiveRecord::VERSION::STRING}"
13
+
14
+ `ruby -e 'exit 0'` # Prime $? with a value.
15
+
16
+ begin
17
+ require 'ruby-debug'
18
+ rescue LoadError => e
19
+ puts "debugger disabled"
20
+ end
21
+
22
+ ROOT = File.join(File.dirname(__FILE__), '..')
23
+
24
+ def silence_warnings
25
+ old_verbose, $VERBOSE = $VERBOSE, nil
26
+ yield
27
+ ensure
28
+ $VERBOSE = old_verbose
29
+ end
30
+
31
+ class Test::Unit::TestCase
32
+ def setup
33
+ silence_warnings do
34
+ Object.const_set(:Rails, stub('Rails', :root => ROOT, :env => 'test'))
35
+ end
36
+ end
37
+ end
38
+
39
+ $LOAD_PATH << File.join(ROOT, 'lib')
40
+ $LOAD_PATH << File.join(ROOT, 'lib', 'paperclip')
41
+
42
+ require File.join(ROOT, 'lib', 'paperclip.rb')
43
+
44
+ require './shoulda_macros/paperclip'
45
+
46
+ FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures")
47
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
48
+ ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.dirname(__FILE__) + "/debug.log")
49
+ ActiveRecord::Base.establish_connection(config['test'])
50
+
51
+ def reset_class class_name
52
+ ActiveRecord::Base.send(:include, Paperclip::Glue)
53
+ Object.send(:remove_const, class_name) rescue nil
54
+ klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
55
+ klass.class_eval{ include Paperclip::Glue }
56
+ klass
57
+ end
58
+
59
+ def reset_table table_name, &block
60
+ block ||= lambda { |table| true }
61
+ ActiveRecord::Base.connection.create_table :dummies, {:force => true}, &block
62
+ end
63
+
64
+ def modify_table table_name, &block
65
+ ActiveRecord::Base.connection.change_table :dummies, &block
66
+ end
67
+
68
+ def rebuild_model options = {}
69
+ ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
70
+ table.column :other, :string
71
+ table.column :avatar_file_name, :string
72
+ table.column :avatar_content_type, :string
73
+ table.column :avatar_file_size, :integer
74
+ table.column :avatar_updated_at, :datetime
75
+ table.column :avatar_fingerprint, :string
76
+ end
77
+ rebuild_class options
78
+ end
79
+
80
+ def rebuild_class options = {}
81
+ ActiveRecord::Base.send(:include, Paperclip::Glue)
82
+ Object.send(:remove_const, "Dummy") rescue nil
83
+ Object.const_set("Dummy", Class.new(ActiveRecord::Base))
84
+ Dummy.class_eval do
85
+ include Paperclip::Glue
86
+ has_attached_file :avatar, options
87
+ end
88
+ end
89
+
90
+ class FakeModel
91
+ attr_accessor :avatar_file_name,
92
+ :avatar_file_size,
93
+ :avatar_updated_at,
94
+ :avatar_content_type,
95
+ :avatar_fingerprint,
96
+ :id
97
+
98
+ def errors
99
+ @errors ||= []
100
+ end
101
+
102
+ def run_paperclip_callbacks name, *args
103
+ end
104
+
105
+ end
106
+
107
+ def attachment options
108
+ Paperclip::Attachment.new(:avatar, FakeModel.new, options)
109
+ end
110
+
111
+ def silence_warnings
112
+ old_verbose, $VERBOSE = $VERBOSE, nil
113
+ yield
114
+ ensure
115
+ $VERBOSE = old_verbose
116
+ end
117
+
118
+ def should_accept_dummy_class
119
+ should "accept the class" do
120
+ assert_accepts @matcher, @dummy_class
121
+ end
122
+
123
+ should "accept an instance of that class" do
124
+ assert_accepts @matcher, @dummy_class.new
125
+ end
126
+ end
127
+
128
+ def should_reject_dummy_class
129
+ should "reject the class" do
130
+ assert_rejects @matcher, @dummy_class
131
+ end
132
+
133
+ should "reject an instance of that class" do
134
+ assert_rejects @matcher, @dummy_class.new
135
+ end
136
+ end
137
+
138
+ def with_exitstatus_returning(code)
139
+ saved_exitstatus = $?.nil? ? 0 : $?.exitstatus
140
+ begin
141
+ `ruby -e 'exit #{code.to_i}'`
142
+ yield
143
+ ensure
144
+ `ruby -e 'exit #{saved_exitstatus.to_i}'`
145
+ end
146
+ end
@@ -0,0 +1,570 @@
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 "Attachment" do
73
+ setup do
74
+ @thumb_path = "./test/../public/system/avatars/1/thumb/5k.png"
75
+ File.delete(@thumb_path) if File.exists?(@thumb_path)
76
+ rebuild_model :styles => { :thumb => "50x50#" }
77
+ @dummy = Dummy.new
78
+ @file = File.new(File.join(File.dirname(__FILE__),
79
+ "fixtures",
80
+ "5k.png"), 'rb')
81
+
82
+ end
83
+
84
+ teardown { @file.close }
85
+
86
+ should "not create the thumbnails upon saving when post-processing is disabled" do
87
+ @dummy.avatar.post_processing = false
88
+ @dummy.avatar = @file
89
+ assert @dummy.save
90
+ assert !File.exists?(@thumb_path)
91
+ end
92
+
93
+ should "create the thumbnails upon saving when post_processing is enabled" do
94
+ @dummy.avatar.post_processing = true
95
+ @dummy.avatar = @file
96
+ assert @dummy.save
97
+ assert File.exists?(@thumb_path)
98
+ end
99
+ end
100
+
101
+ context "Attachment with no generated thumbnails" do
102
+ setup do
103
+ @thumb_small_path = "./test/../public/system/avatars/1/thumb_small/5k.png"
104
+ @thumb_large_path = "./test/../public/system/avatars/1/thumb_large/5k.png"
105
+ File.delete(@thumb_small_path) if File.exists?(@thumb_small_path)
106
+ File.delete(@thumb_large_path) if File.exists?(@thumb_large_path)
107
+ rebuild_model :styles => { :thumb_small => "50x50#", :thumb_large => "60x60#" }
108
+ @dummy = Dummy.new
109
+ @file = File.new(File.join(File.dirname(__FILE__),
110
+ "fixtures",
111
+ "5k.png"), 'rb')
112
+
113
+ @dummy.avatar.post_processing = false
114
+ @dummy.avatar = @file
115
+ assert @dummy.save
116
+ @dummy.avatar.post_processing = true
117
+ end
118
+
119
+ teardown { @file.close }
120
+
121
+ should "allow us to create all thumbnails in one go" do
122
+ assert !File.exists?(@thumb_small_path)
123
+ assert !File.exists?(@thumb_large_path)
124
+
125
+ @dummy.avatar.reprocess!
126
+
127
+ assert File.exists?(@thumb_small_path)
128
+ assert File.exists?(@thumb_large_path)
129
+ end
130
+
131
+ should "allow us to selectively create each thumbnail" do
132
+ assert !File.exists?(@thumb_small_path)
133
+ assert !File.exists?(@thumb_large_path)
134
+
135
+ @dummy.avatar.reprocess! :thumb_small
136
+ assert File.exists?(@thumb_small_path)
137
+ assert !File.exists?(@thumb_large_path)
138
+
139
+ @dummy.avatar.reprocess! :thumb_large
140
+ assert File.exists?(@thumb_large_path)
141
+ end
142
+ end
143
+
144
+ context "A model that modifies its original" do
145
+ setup do
146
+ rebuild_model :styles => { :original => "2x2#" }
147
+ @dummy = Dummy.new
148
+ @file = File.new(File.join(File.dirname(__FILE__),
149
+ "fixtures",
150
+ "5k.png"), 'rb')
151
+ @dummy.avatar = @file
152
+ end
153
+
154
+ should "report the file size of the processed file and not the original" do
155
+ assert_not_equal @file.size, @dummy.avatar.size
156
+ end
157
+
158
+ teardown { @file.close }
159
+ end
160
+
161
+ context "A model with attachments scoped under an id" do
162
+ setup do
163
+ rebuild_model :styles => { :large => "100x100",
164
+ :medium => "50x50" },
165
+ :path => ":rails_root/tmp/:id/:attachments/:style.:extension"
166
+ @dummy = Dummy.new
167
+ @file = File.new(File.join(File.dirname(__FILE__),
168
+ "fixtures",
169
+ "5k.png"), 'rb')
170
+ @dummy.avatar = @file
171
+ end
172
+
173
+ teardown { @file.close }
174
+
175
+ context "when saved" do
176
+ setup do
177
+ @dummy.save
178
+ @saved_path = @dummy.avatar.path(:large)
179
+ end
180
+
181
+ should "have a large file in the right place" do
182
+ assert File.exists?(@dummy.avatar.path(:large))
183
+ end
184
+
185
+ context "and deleted" do
186
+ setup do
187
+ @dummy.avatar.clear
188
+ @dummy.save
189
+ end
190
+
191
+ should "not have a large file in the right place anymore" do
192
+ assert ! File.exists?(@saved_path)
193
+ end
194
+
195
+ should "not have its next two parent directories" do
196
+ assert ! File.exists?(File.dirname(@saved_path))
197
+ assert ! File.exists?(File.dirname(File.dirname(@saved_path)))
198
+ end
199
+
200
+ before_should "not die if an unexpected SystemCallError happens" do
201
+ FileUtils.stubs(:rmdir).raises(Errno::EPIPE)
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ context "A model with no attachment validation" do
208
+ setup do
209
+ rebuild_model :styles => { :large => "300x300>",
210
+ :medium => "100x100",
211
+ :thumb => ["32x32#", :gif] },
212
+ :default_style => :medium,
213
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
214
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
215
+ @dummy = Dummy.new
216
+ end
217
+
218
+ should "have its definition return false when asked about whiny_thumbnails" do
219
+ assert ! Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
220
+ end
221
+
222
+ context "when validates_attachment_thumbnails is called" do
223
+ setup do
224
+ Dummy.validates_attachment_thumbnails :avatar
225
+ end
226
+
227
+ should "have its definition return true when asked about whiny_thumbnails" do
228
+ assert_equal true, Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
229
+ end
230
+ end
231
+
232
+ context "redefined to have attachment validations" do
233
+ setup do
234
+ rebuild_model :styles => { :large => "300x300>",
235
+ :medium => "100x100",
236
+ :thumb => ["32x32#", :gif] },
237
+ :whiny_thumbnails => true,
238
+ :default_style => :medium,
239
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
240
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
241
+ end
242
+
243
+ should "have its definition return true when asked about whiny_thumbnails" do
244
+ assert_equal true, Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
245
+ end
246
+ end
247
+ end
248
+
249
+ context "A model with no convert_options setting" do
250
+ setup do
251
+ rebuild_model :styles => { :large => "300x300>",
252
+ :medium => "100x100",
253
+ :thumb => ["32x32#", :gif] },
254
+ :default_style => :medium,
255
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
256
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
257
+ @dummy = Dummy.new
258
+ end
259
+
260
+ should "have its definition return nil when asked about convert_options" do
261
+ assert ! Dummy.attachment_definitions[:avatar][:convert_options]
262
+ end
263
+
264
+ context "redefined to have convert_options setting" do
265
+ setup do
266
+ rebuild_model :styles => { :large => "300x300>",
267
+ :medium => "100x100",
268
+ :thumb => ["32x32#", :gif] },
269
+ :convert_options => "-strip -depth 8",
270
+ :default_style => :medium,
271
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
272
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
273
+ end
274
+
275
+ should "have its definition return convert_options value when asked about convert_options" do
276
+ assert_equal "-strip -depth 8", Dummy.attachment_definitions[:avatar][:convert_options]
277
+ end
278
+ end
279
+ end
280
+
281
+ context "A model with a filesystem attachment" do
282
+ setup do
283
+ rebuild_model :styles => { :large => "300x300>",
284
+ :medium => "100x100",
285
+ :thumb => ["32x32#", :gif] },
286
+ :whiny_thumbnails => true,
287
+ :default_style => :medium,
288
+ :url => "/:attachment/:class/:style/:id/:basename.:extension",
289
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
290
+ @dummy = Dummy.new
291
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
292
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
293
+
294
+ assert @dummy.avatar = @file
295
+ assert @dummy.valid?, @dummy.errors.full_messages.join(", ")
296
+ assert @dummy.save
297
+ end
298
+
299
+ should "write and delete its files" do
300
+ [["434x66", :original],
301
+ ["300x46", :large],
302
+ ["100x15", :medium],
303
+ ["32x32", :thumb]].each do |geo, style|
304
+ cmd = %Q[identify -format "%wx%h" "#{@dummy.avatar.path(style)}"]
305
+ assert_equal geo, `#{cmd}`.chomp, cmd
306
+ end
307
+
308
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
309
+
310
+ @d2 = Dummy.find(@dummy.id)
311
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path}"`.chomp
312
+ assert_equal "434x66", `identify -format "%wx%h" "#{@d2.avatar.path(:original)}"`.chomp
313
+ assert_equal "300x46", `identify -format "%wx%h" "#{@d2.avatar.path(:large)}"`.chomp
314
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path(:medium)}"`.chomp
315
+ assert_equal "32x32", `identify -format "%wx%h" "#{@d2.avatar.path(:thumb)}"`.chomp
316
+
317
+ @dummy.avatar = "not a valid file but not nil"
318
+ assert_equal File.basename(@file.path), @dummy.avatar_file_name
319
+ assert @dummy.valid?
320
+ assert @dummy.save
321
+
322
+ saved_paths.each do |p|
323
+ assert File.exists?(p)
324
+ end
325
+
326
+ @dummy.avatar.clear
327
+ assert_nil @dummy.avatar_file_name
328
+ assert @dummy.valid?
329
+ assert @dummy.save
330
+
331
+ saved_paths.each do |p|
332
+ assert ! File.exists?(p)
333
+ end
334
+
335
+ @d2 = Dummy.find(@dummy.id)
336
+ assert_nil @d2.avatar_file_name
337
+ end
338
+
339
+ should "work exactly the same when new as when reloaded" do
340
+ @d2 = Dummy.find(@dummy.id)
341
+
342
+ assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
343
+ [:thumb, :medium, :large, :original].each do |style|
344
+ assert_equal @dummy.avatar.path(style), @d2.avatar.path(style)
345
+ end
346
+
347
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
348
+
349
+ @d2.avatar.clear
350
+ assert @d2.save
351
+
352
+ saved_paths.each do |p|
353
+ assert ! File.exists?(p)
354
+ end
355
+ end
356
+
357
+ should "know the difference between good files, bad files, and not files" do
358
+ expected = @dummy.avatar.to_file
359
+ @dummy.avatar = "not a file"
360
+ assert @dummy.valid?
361
+ assert_equal expected.path, @dummy.avatar.path
362
+ expected.close
363
+
364
+ @dummy.avatar = @bad_file
365
+ assert ! @dummy.valid?
366
+ end
367
+
368
+ should "know the difference between good files, bad files, and not files when validating" do
369
+ Dummy.validates_attachment_presence :avatar
370
+ @d2 = Dummy.find(@dummy.id)
371
+ @d2.avatar = @file
372
+ assert @d2.valid?, @d2.errors.full_messages.inspect
373
+ @d2.avatar = @bad_file
374
+ assert ! @d2.valid?
375
+ end
376
+
377
+ should "be able to reload without saving and not have the file disappear" do
378
+ @dummy.avatar = @file
379
+ assert @dummy.save
380
+ @dummy.avatar.clear
381
+ assert_nil @dummy.avatar_file_name
382
+ @dummy.reload
383
+ assert_equal "5k.png", @dummy.avatar_file_name
384
+ end
385
+
386
+ context "that is assigned its file from another Paperclip attachment" do
387
+ setup do
388
+ @dummy2 = Dummy.new
389
+ @file2 = File.new(File.join(FIXTURES_DIR, "12k.png"), 'rb')
390
+ assert @dummy2.avatar = @file2
391
+ @dummy2.save
392
+ end
393
+
394
+ should "work when assigned a file" do
395
+ assert_not_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
396
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
397
+
398
+ assert @dummy.avatar = @dummy2.avatar
399
+ @dummy.save
400
+ assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
401
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
402
+ end
403
+ end
404
+
405
+ end
406
+
407
+ context "A model with an attachments association and a Paperclip attachment" do
408
+ setup do
409
+ Dummy.class_eval do
410
+ has_many :attachments, :class_name => 'Dummy'
411
+ end
412
+
413
+ @dummy = Dummy.new
414
+ @dummy.avatar = File.new(File.join(File.dirname(__FILE__),
415
+ "fixtures",
416
+ "5k.png"), 'rb')
417
+ end
418
+
419
+ should "should not error when saving" do
420
+ assert_nothing_raised do
421
+ @dummy.save!
422
+ end
423
+ end
424
+ end
425
+
426
+ if ENV['S3_TEST_BUCKET']
427
+ def s3_files_for attachment
428
+ [:thumb, :medium, :large, :original].inject({}) do |files, style|
429
+ data = `curl "#{attachment.url(style)}" 2>/dev/null`.chomp
430
+ t = Tempfile.new("paperclip-test")
431
+ t.binmode
432
+ t.write(data)
433
+ t.rewind
434
+ files[style] = t
435
+ files
436
+ end
437
+ end
438
+
439
+ def s3_headers_for attachment, style
440
+ `curl --head "#{attachment.url(style)}" 2>/dev/null`.split("\n").inject({}) do |h,head|
441
+ split_head = head.chomp.split(/\s*:\s*/, 2)
442
+ h[split_head.first.downcase] = split_head.last unless split_head.empty?
443
+ h
444
+ end
445
+ end
446
+
447
+ context "A model with an S3 attachment" do
448
+ setup do
449
+ rebuild_model :styles => { :large => "300x300>",
450
+ :medium => "100x100",
451
+ :thumb => ["32x32#", :gif] },
452
+ :storage => :s3,
453
+ :whiny_thumbnails => true,
454
+ :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml")),
455
+ :default_style => :medium,
456
+ :bucket => ENV['S3_TEST_BUCKET'],
457
+ :path => ":class/:attachment/:id/:style/:basename.:extension"
458
+ @dummy = Dummy.new
459
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
460
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
461
+
462
+ assert @dummy.avatar = @file
463
+ assert @dummy.valid?
464
+ assert @dummy.save
465
+
466
+ @files_on_s3 = s3_files_for @dummy.avatar
467
+ end
468
+
469
+ should "have the same contents as the original" do
470
+ @file.rewind
471
+ assert_equal @file.read, @files_on_s3[:original].read
472
+ end
473
+
474
+ should "write and delete its files" do
475
+ [["434x66", :original],
476
+ ["300x46", :large],
477
+ ["100x15", :medium],
478
+ ["32x32", :thumb]].each do |geo, style|
479
+ cmd = %Q[identify -format "%wx%h" "#{@files_on_s3[style].path}"]
480
+ assert_equal geo, `#{cmd}`.chomp, cmd
481
+ end
482
+
483
+ @d2 = Dummy.find(@dummy.id)
484
+ @d2_files = s3_files_for @d2.avatar
485
+ [["434x66", :original],
486
+ ["300x46", :large],
487
+ ["100x15", :medium],
488
+ ["32x32", :thumb]].each do |geo, style|
489
+ cmd = %Q[identify -format "%wx%h" "#{@d2_files[style].path}"]
490
+ assert_equal geo, `#{cmd}`.chomp, cmd
491
+ end
492
+
493
+ @dummy.avatar = "not a valid file but not nil"
494
+ assert_equal File.basename(@file.path), @dummy.avatar_file_name
495
+ assert @dummy.valid?
496
+ assert @dummy.save
497
+
498
+ [:thumb, :medium, :large, :original].each do |style|
499
+ assert @dummy.avatar.exists?(style)
500
+ end
501
+
502
+ @dummy.avatar.clear
503
+ assert_nil @dummy.avatar_file_name
504
+ assert @dummy.valid?
505
+ assert @dummy.save
506
+
507
+ [:thumb, :medium, :large, :original].each do |style|
508
+ assert ! @dummy.avatar.exists?(style)
509
+ end
510
+
511
+ @d2 = Dummy.find(@dummy.id)
512
+ assert_nil @d2.avatar_file_name
513
+ end
514
+
515
+ should "work exactly the same when new as when reloaded" do
516
+ @d2 = Dummy.find(@dummy.id)
517
+
518
+ assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
519
+ [:thumb, :medium, :large, :original].each do |style|
520
+ assert_equal @dummy.avatar.to_file(style).read, @d2.avatar.to_file(style).read
521
+ end
522
+
523
+ saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) }
524
+
525
+ @d2.avatar.clear
526
+ assert @d2.save
527
+
528
+ [:thumb, :medium, :large, :original].each do |style|
529
+ assert ! @dummy.avatar.exists?(style)
530
+ end
531
+ end
532
+
533
+ should "know the difference between good files, bad files, not files, and nil" do
534
+ expected = @dummy.avatar.to_file
535
+ @dummy.avatar = "not a file"
536
+ assert @dummy.valid?
537
+ assert_equal expected.read, @dummy.avatar.to_file.read
538
+
539
+ @dummy.avatar = @bad_file
540
+ assert ! @dummy.valid?
541
+ @dummy.avatar = nil
542
+ assert @dummy.valid?
543
+
544
+ Dummy.validates_attachment_presence :avatar
545
+ @d2 = Dummy.find(@dummy.id)
546
+ @d2.avatar = @file
547
+ assert @d2.valid?
548
+ @d2.avatar = @bad_file
549
+ assert ! @d2.valid?
550
+ @d2.avatar = nil
551
+ assert ! @d2.valid?
552
+ end
553
+
554
+ should "be able to reload without saving and not have the file disappear" do
555
+ @dummy.avatar = @file
556
+ assert @dummy.save
557
+ @dummy.avatar = nil
558
+ assert_nil @dummy.avatar_file_name
559
+ @dummy.reload
560
+ assert_equal "5k.png", @dummy.avatar_file_name
561
+ end
562
+
563
+ should "have the right content type" do
564
+ headers = s3_headers_for(@dummy.avatar, :original)
565
+ assert_equal 'image/png', headers['content-type']
566
+ end
567
+ end
568
+ end
569
+ end
570
+