reenhanced-paperclip 2.3.8

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 (61) 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 +844 -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/fixtures/uppercase.PNG +0 -0
  46. data/test/geometry_test.rb +177 -0
  47. data/test/helper.rb +146 -0
  48. data/test/integration_test.rb +498 -0
  49. data/test/interpolations_test.rb +127 -0
  50. data/test/iostream_test.rb +71 -0
  51. data/test/matchers/have_attached_file_matcher_test.rb +24 -0
  52. data/test/matchers/validate_attachment_content_type_matcher_test.rb +47 -0
  53. data/test/matchers/validate_attachment_presence_matcher_test.rb +26 -0
  54. data/test/matchers/validate_attachment_size_matcher_test.rb +51 -0
  55. data/test/paperclip_test.rb +271 -0
  56. data/test/processor_test.rb +10 -0
  57. data/test/storage_test.rb +386 -0
  58. data/test/style_test.rb +141 -0
  59. data/test/thumbnail_test.rb +227 -0
  60. data/test/upfile_test.rb +36 -0
  61. metadata +224 -0
data/rails/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'paperclip/railtie'
2
+ Paperclip::Railtie.insert
@@ -0,0 +1,118 @@
1
+ require 'paperclip/matchers'
2
+
3
+ module Paperclip
4
+ # =Paperclip Shoulda Macros
5
+ #
6
+ # These macros are intended for use with shoulda, and will be included into
7
+ # your tests automatically. All of the macros use the standard shoulda
8
+ # assumption that the name of the test is based on the name of the model
9
+ # you're testing (that is, UserTest is the test for the User model), and
10
+ # will load that class for testing purposes.
11
+ module Shoulda
12
+ include Matchers
13
+ # This will test whether you have defined your attachment correctly by
14
+ # checking for all the required fields exist after the definition of the
15
+ # attachment.
16
+ def should_have_attached_file name
17
+ klass = self.name.gsub(/Test$/, '').constantize
18
+ matcher = have_attached_file name
19
+ should matcher.description do
20
+ assert_accepts(matcher, klass)
21
+ end
22
+ end
23
+
24
+ # Tests for validations on the presence of the attachment.
25
+ def should_validate_attachment_presence name
26
+ klass = self.name.gsub(/Test$/, '').constantize
27
+ matcher = validate_attachment_presence name
28
+ should matcher.description do
29
+ assert_accepts(matcher, klass)
30
+ end
31
+ end
32
+
33
+ # Tests that you have content_type validations specified. There are two
34
+ # options, :valid and :invalid. Both accept an array of strings. The
35
+ # strings should be a list of content types which will pass and fail
36
+ # validation, respectively.
37
+ def should_validate_attachment_content_type name, options = {}
38
+ klass = self.name.gsub(/Test$/, '').constantize
39
+ valid = [options[:valid]].flatten
40
+ invalid = [options[:invalid]].flatten
41
+ matcher = validate_attachment_content_type(name).allowing(valid).rejecting(invalid)
42
+ should matcher.description do
43
+ assert_accepts(matcher, klass)
44
+ end
45
+ end
46
+
47
+ # Tests to ensure that you have file size validations turned on. You
48
+ # can pass the same options to this that you can to
49
+ # validate_attachment_file_size - :less_than, :greater_than, and :in.
50
+ # :less_than checks that a file is less than a certain size, :greater_than
51
+ # checks that a file is more than a certain size, and :in takes a Range or
52
+ # Array which specifies the lower and upper limits of the file size.
53
+ def should_validate_attachment_size name, options = {}
54
+ klass = self.name.gsub(/Test$/, '').constantize
55
+ min = options[:greater_than] || (options[:in] && options[:in].first) || 0
56
+ max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
57
+ range = (min..max)
58
+ matcher = validate_attachment_size(name).in(range)
59
+ should matcher.description do
60
+ assert_accepts(matcher, klass)
61
+ end
62
+ end
63
+
64
+ # Stubs the HTTP PUT for an attachment using S3 storage.
65
+ #
66
+ # @example
67
+ # stub_paperclip_s3('user', 'avatar', 'png')
68
+ def stub_paperclip_s3(model, attachment, extension)
69
+ definition = model.gsub(" ", "_").classify.constantize.
70
+ attachment_definitions[attachment.to_sym]
71
+
72
+ path = "http://s3.amazonaws.com/:id/#{definition[:path]}"
73
+ path.gsub!(/:([^\/\.]+)/) do |match|
74
+ "([^\/\.]+)"
75
+ end
76
+
77
+ begin
78
+ FakeWeb.register_uri(:put, Regexp.new(path), :body => "OK")
79
+ rescue NameError
80
+ raise NameError, "the stub_paperclip_s3 shoulda macro requires the fakeweb gem."
81
+ end
82
+ end
83
+
84
+ # Stub S3 and return a file for attachment. Best with Factory Girl.
85
+ # Uses a strict directory convention:
86
+ #
87
+ # features/support/paperclip
88
+ #
89
+ # This method is used by the Paperclip-provided Cucumber step:
90
+ #
91
+ # When I attach a "demo_tape" "mp3" file to a "band" on S3
92
+ #
93
+ # @example
94
+ # Factory.define :band_with_demo_tape, :parent => :band do |band|
95
+ # band.demo_tape { band.paperclip_fixture("band", "demo_tape", "png") }
96
+ # end
97
+ def paperclip_fixture(model, attachment, extension)
98
+ stub_paperclip_s3(model, attachment, extension)
99
+ base_path = File.join(File.dirname(__FILE__), "..", "..",
100
+ "features", "support", "paperclip")
101
+ File.new(File.join(base_path, model, "#{attachment}.#{extension}"))
102
+ end
103
+ end
104
+ end
105
+
106
+ if defined?(ActionController::Integration::Session)
107
+ class ActionController::Integration::Session #:nodoc:
108
+ include Paperclip::Shoulda
109
+ end
110
+ end
111
+
112
+ class Factory
113
+ include Paperclip::Shoulda #:nodoc:
114
+ end
115
+
116
+ class Test::Unit::TestCase #:nodoc:
117
+ extend Paperclip::Shoulda
118
+ end
@@ -0,0 +1,844 @@
1
+ # encoding: utf-8
2
+ require './test/helper'
3
+
4
+ class Dummy
5
+ # This is a dummy class
6
+ end
7
+
8
+ class AttachmentTest < 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 "Attachment default_options" do
18
+ setup do
19
+ rebuild_model
20
+ @old_default_options = Paperclip::Attachment.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::Attachment.default_options.merge! @old_default_options
30
+ end
31
+
32
+ should "be overrideable" do
33
+ Paperclip::Attachment.default_options.merge!(@new_default_options)
34
+ @new_default_options.keys.each do |key|
35
+ assert_equal @new_default_options[key],
36
+ Paperclip::Attachment.default_options[key]
37
+ end
38
+ end
39
+
40
+ context "without an Attachment" 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 Attachment" do
51
+ setup do
52
+ @dummy = Dummy.new
53
+ @attachment = @dummy.avatar
54
+ end
55
+
56
+ Paperclip::Attachment.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::Attachment.default_options.merge!(@new_default_options)
67
+ @dummy = Dummy.new
68
+ @attachment = @dummy.avatar
69
+ end
70
+
71
+ Paperclip::Attachment.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
+ Rails.stubs(:env).returns(@rails_env)
110
+ end
111
+
112
+ should "return the proper path" do
113
+ assert_equal "#{@rails_env}/#{@id}.png", @dummy.avatar.path
114
+ end
115
+ end
116
+
117
+ context "An attachment with a default style and an extension interpolation" do
118
+ setup do
119
+ @attachment = attachment :path => ":basename.:extension",
120
+ :styles => { :default => ["100x100", :png] },
121
+ :default_style => :default
122
+ @file = StringIO.new("...")
123
+ @file.stubs(:original_filename).returns("file.jpg")
124
+ end
125
+ should "return the right extension for the path" do
126
+ @attachment.assign(@file)
127
+ assert_equal "file.png", @attachment.path
128
+ end
129
+ end
130
+
131
+ context "An attachment with :convert_options" do
132
+ setup do
133
+ rebuild_model :styles => {
134
+ :thumb => "100x100",
135
+ :large => "400x400"
136
+ },
137
+ :convert_options => {
138
+ :all => "-do_stuff",
139
+ :thumb => "-thumbnailize"
140
+ }
141
+ @dummy = Dummy.new
142
+ @dummy.avatar
143
+ end
144
+
145
+ should "report the correct options when sent #extra_options_for(:thumb)" do
146
+ assert_equal "-thumbnailize -do_stuff", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
147
+ end
148
+
149
+ should "report the correct options when sent #extra_options_for(:large)" do
150
+ assert_equal "-do_stuff", @dummy.avatar.send(:extra_options_for, :large)
151
+ end
152
+ end
153
+
154
+ context "An attachment with :convert_options that is a proc" do
155
+ setup do
156
+ rebuild_model :styles => {
157
+ :thumb => "100x100",
158
+ :large => "400x400"
159
+ },
160
+ :convert_options => {
161
+ :all => lambda{|i| i.all },
162
+ :thumb => lambda{|i| i.thumb }
163
+ }
164
+ Dummy.class_eval do
165
+ def all; "-all"; end
166
+ def thumb; "-thumb"; end
167
+ end
168
+ @dummy = Dummy.new
169
+ @dummy.avatar
170
+ end
171
+
172
+ should "report the correct options when sent #extra_options_for(:thumb)" do
173
+ assert_equal "-thumb -all", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
174
+ end
175
+
176
+ should "report the correct options when sent #extra_options_for(:large)" do
177
+ assert_equal "-all", @dummy.avatar.send(:extra_options_for, :large)
178
+ end
179
+ end
180
+
181
+ context "An attachment with :path that is a proc" do
182
+ setup do
183
+ rebuild_model :path => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
184
+
185
+ @file = File.new(File.join(File.dirname(__FILE__),
186
+ "fixtures",
187
+ "5k.png"), 'rb')
188
+ @dummyA = Dummy.new(:other => 'a')
189
+ @dummyA.avatar = @file
190
+ @dummyB = Dummy.new(:other => 'b')
191
+ @dummyB.avatar = @file
192
+ end
193
+
194
+ teardown { @file.close }
195
+
196
+ should "return correct path" do
197
+ assert_equal "path/a.png", @dummyA.avatar.path
198
+ assert_equal "path/b.png", @dummyB.avatar.path
199
+ end
200
+ end
201
+
202
+ context "An attachment with :styles that is a proc" do
203
+ setup do
204
+ rebuild_model :styles => lambda{ |attachment| {:thumb => "50x50#", :large => "400x400"} }
205
+
206
+ @attachment = Dummy.new.avatar
207
+ end
208
+
209
+ should "have the correct geometry" do
210
+ assert_equal "50x50#", @attachment.styles[:thumb][:geometry]
211
+ end
212
+ end
213
+
214
+ context "An attachment with :url that is a proc" do
215
+ setup do
216
+ rebuild_model :url => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
217
+
218
+ @file = File.new(File.join(File.dirname(__FILE__),
219
+ "fixtures",
220
+ "5k.png"), 'rb')
221
+ @dummyA = Dummy.new(:other => 'a')
222
+ @dummyA.avatar = @file
223
+ @dummyB = Dummy.new(:other => 'b')
224
+ @dummyB.avatar = @file
225
+ end
226
+
227
+ teardown { @file.close }
228
+
229
+ should "return correct url" do
230
+ assert_equal "path/a.png", @dummyA.avatar.url(:original, false)
231
+ assert_equal "path/b.png", @dummyB.avatar.url(:original, false)
232
+ end
233
+ end
234
+
235
+ geometry_specs = [
236
+ [ lambda{|z| "50x50#" }, :png ],
237
+ lambda{|z| "50x50#" },
238
+ { :geometry => lambda{|z| "50x50#" } }
239
+ ]
240
+ geometry_specs.each do |geometry_spec|
241
+ context "An attachment geometry like #{geometry_spec}" do
242
+ setup do
243
+ rebuild_model :styles => { :normal => geometry_spec }
244
+ @attachment = Dummy.new.avatar
245
+ end
246
+
247
+ context "when assigned" do
248
+ setup do
249
+ @file = StringIO.new(".")
250
+ @attachment.assign(@file)
251
+ end
252
+
253
+ should "have the correct geometry" do
254
+ assert_equal "50x50#", @attachment.styles[:normal][:geometry]
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ context "An attachment with both 'normal' and hash-style styles" do
261
+ setup do
262
+ rebuild_model :styles => {
263
+ :normal => ["50x50#", :png],
264
+ :hash => { :geometry => "50x50#", :format => :png }
265
+ }
266
+ @dummy = Dummy.new
267
+ @attachment = @dummy.avatar
268
+ end
269
+
270
+ [:processors, :whiny, :convert_options, :geometry, :format].each do |field|
271
+ should "have the same #{field} field" do
272
+ assert_equal @attachment.styles[:normal][field], @attachment.styles[:hash][field]
273
+ end
274
+ end
275
+ end
276
+
277
+ context "An attachment with :processors that is a proc" do
278
+ setup do
279
+ rebuild_model :styles => { :normal => '' }, :processors => lambda { |a| [ :test ] }
280
+ @attachment = Dummy.new.avatar
281
+ end
282
+
283
+ context "when assigned" do
284
+ setup do
285
+ @attachment.assign(StringIO.new("."))
286
+ end
287
+
288
+ should "have the correct processors" do
289
+ assert_equal [ :test ], @attachment.styles[:normal][:processors]
290
+ end
291
+ end
292
+ end
293
+
294
+ context "An attachment with erroring processor" do
295
+ setup do
296
+ rebuild_model :processor => [:thumbnail], :styles => { :small => '' }, :whiny_thumbnails => true
297
+ @dummy = Dummy.new
298
+ Paperclip::Thumbnail.expects(:make).raises(Paperclip::PaperclipError, "cannot be processed.")
299
+ @file = StringIO.new("...")
300
+ @file.stubs(:to_tempfile).returns(@file)
301
+ @dummy.avatar = @file
302
+ end
303
+
304
+ should "correctly forward processing error message to the instance" do
305
+ @dummy.valid?
306
+ assert_contains @dummy.errors.full_messages, "Avatar cannot be processed."
307
+ end
308
+ end
309
+
310
+ context "An attachment with multiple processors" do
311
+ setup do
312
+ class Paperclip::Test < Paperclip::Processor; end
313
+ @style_params = { :once => {:one => 1, :two => 2} }
314
+ rebuild_model :processors => [:thumbnail, :test], :styles => @style_params
315
+ @dummy = Dummy.new
316
+ @file = StringIO.new("...")
317
+ @file.stubs(:to_tempfile).returns(@file)
318
+ Paperclip::Test.stubs(:make).returns(@file)
319
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
320
+ end
321
+
322
+ context "when assigned" do
323
+ setup { @dummy.avatar = @file }
324
+
325
+ before_should "call #make on all specified processors" do
326
+ Paperclip::Thumbnail.expects(:make).with(any_parameters).returns(@file)
327
+ Paperclip::Test.expects(:make).with(any_parameters).returns(@file)
328
+ end
329
+
330
+ before_should "call #make with the right parameters passed as second argument" do
331
+ expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
332
+ Paperclip::Thumbnail.expects(:make).with(anything, expected_params, anything).returns(@file)
333
+ end
334
+
335
+ before_should "call #make with attachment passed as third argument" do
336
+ Paperclip::Test.expects(:make).with(anything, anything, @dummy.avatar).returns(@file)
337
+ end
338
+ end
339
+ end
340
+
341
+ should "include the filesystem module when loading the filesystem storage" do
342
+ rebuild_model :storage => :filesystem
343
+ @dummy = Dummy.new
344
+ assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
345
+ end
346
+
347
+ should "include the filesystem module even if capitalization is wrong" do
348
+ rebuild_model :storage => :FileSystem
349
+ @dummy = Dummy.new
350
+ assert @dummy.avatar.is_a?(Paperclip::Storage::Filesystem)
351
+ end
352
+
353
+ should "raise an error if you try to include a storage module that doesn't exist" do
354
+ rebuild_model :storage => :not_here
355
+ @dummy = Dummy.new
356
+ assert_raises(Paperclip::StorageMethodNotFound) do
357
+ @dummy.avatar
358
+ end
359
+ end
360
+
361
+ context "An attachment with styles but no processors defined" do
362
+ setup do
363
+ rebuild_model :processors => [], :styles => {:something => '1'}
364
+ @dummy = Dummy.new
365
+ @file = StringIO.new("...")
366
+ end
367
+ should "raise when assigned to" do
368
+ assert_raises(RuntimeError){ @dummy.avatar = @file }
369
+ end
370
+ end
371
+
372
+ context "An attachment without styles and with no processors defined" do
373
+ setup do
374
+ rebuild_model :processors => [], :styles => {}
375
+ @dummy = Dummy.new
376
+ @file = StringIO.new("...")
377
+ end
378
+ should "not raise when assigned to" do
379
+ @dummy.avatar = @file
380
+ end
381
+ end
382
+
383
+ context "Assigning an attachment with post_process hooks" do
384
+ setup do
385
+ rebuild_class :styles => { :something => "100x100#" }
386
+ Dummy.class_eval do
387
+ before_avatar_post_process :do_before_avatar
388
+ after_avatar_post_process :do_after_avatar
389
+ before_post_process :do_before_all
390
+ after_post_process :do_after_all
391
+ def do_before_avatar; end
392
+ def do_after_avatar; end
393
+ def do_before_all; end
394
+ def do_after_all; end
395
+ end
396
+ @file = StringIO.new(".")
397
+ @file.stubs(:to_tempfile).returns(@file)
398
+ @dummy = Dummy.new
399
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
400
+ @attachment = @dummy.avatar
401
+ end
402
+
403
+ should "call the defined callbacks when assigned" do
404
+ @dummy.expects(:do_before_avatar).with()
405
+ @dummy.expects(:do_after_avatar).with()
406
+ @dummy.expects(:do_before_all).with()
407
+ @dummy.expects(:do_after_all).with()
408
+ Paperclip::Thumbnail.expects(:make).returns(@file)
409
+ @dummy.avatar = @file
410
+ end
411
+
412
+ should "not cancel the processing if a before_post_process returns nil" do
413
+ @dummy.expects(:do_before_avatar).with().returns(nil)
414
+ @dummy.expects(:do_after_avatar).with()
415
+ @dummy.expects(:do_before_all).with().returns(nil)
416
+ @dummy.expects(:do_after_all).with()
417
+ Paperclip::Thumbnail.expects(:make).returns(@file)
418
+ @dummy.avatar = @file
419
+ end
420
+
421
+ should "cancel the processing if a before_post_process returns false" do
422
+ @dummy.expects(:do_before_avatar).never
423
+ @dummy.expects(:do_after_avatar).never
424
+ @dummy.expects(:do_before_all).with().returns(false)
425
+ @dummy.expects(:do_after_all)
426
+ Paperclip::Thumbnail.expects(:make).never
427
+ @dummy.avatar = @file
428
+ end
429
+
430
+ should "cancel the processing if a before_avatar_post_process returns false" do
431
+ @dummy.expects(:do_before_avatar).with().returns(false)
432
+ @dummy.expects(:do_after_avatar)
433
+ @dummy.expects(:do_before_all).with().returns(true)
434
+ @dummy.expects(:do_after_all)
435
+ Paperclip::Thumbnail.expects(:make).never
436
+ @dummy.avatar = @file
437
+ end
438
+ end
439
+
440
+ context "Assigning an attachment" do
441
+ setup do
442
+ rebuild_model :styles => { :something => "100x100#" }
443
+ @file = StringIO.new(".")
444
+ @file.stubs(:original_filename).returns("5k.png\n\n")
445
+ @file.stubs(:content_type).returns("image/png\n\n")
446
+ @file.stubs(:to_tempfile).returns(@file)
447
+ @dummy = Dummy.new
448
+ Paperclip::Thumbnail.expects(:make).returns(@file)
449
+ @attachment = @dummy.avatar
450
+ @dummy.avatar = @file
451
+ end
452
+
453
+ should "strip whitespace from original_filename field" do
454
+ assert_equal "5k.png", @dummy.avatar.original_filename
455
+ end
456
+
457
+ should "strip whitespace from content_type field" do
458
+ assert_equal "image/png", @dummy.avatar.instance.avatar_content_type
459
+ end
460
+ end
461
+
462
+ context "Attachment with strange letters" do
463
+ setup do
464
+ rebuild_model
465
+
466
+ @not_file = mock("not_file")
467
+ @tempfile = mock("tempfile")
468
+ @not_file.stubs(:nil?).returns(false)
469
+ @not_file.expects(:size).returns(10)
470
+ @tempfile.expects(:size).returns(10)
471
+ @not_file.expects(:original_filename).returns("sheep_say_bæ.png\r\n")
472
+ @not_file.expects(:content_type).returns("image/png\r\n")
473
+
474
+ @dummy = Dummy.new
475
+ @attachment = @dummy.avatar
476
+ @attachment.expects(:valid_assignment?).with(@not_file).returns(true)
477
+ @attachment.expects(:queue_existing_for_delete)
478
+ @attachment.expects(:post_process)
479
+ @attachment.expects(:to_tempfile).returns(@tempfile)
480
+ @attachment.expects(:generate_fingerprint).with(@tempfile).returns("12345")
481
+ @attachment.expects(:generate_fingerprint).with(@not_file).returns("12345")
482
+ @dummy.avatar = @not_file
483
+ end
484
+
485
+ should "not remove strange letters" do
486
+ assert_equal "sheep_say_bæ.png", @dummy.avatar.original_filename
487
+ end
488
+ end
489
+
490
+ context "Attachment with uppercase extension and a default style" do
491
+ setup do
492
+ @old_defaults = Paperclip::Attachment.default_options.dup
493
+ Paperclip::Attachment.default_options.merge!({
494
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
495
+ })
496
+ FileUtils.rm_rf("tmp")
497
+ rebuild_model
498
+ @instance = Dummy.new
499
+ @instance.stubs(:id).returns 123
500
+
501
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "uppercase.PNG"), 'rb')
502
+
503
+ styles = {:styles => { :large => ["400x400", :jpg],
504
+ :medium => ["100x100", :jpg],
505
+ :small => ["32x32#", :jpg]},
506
+ :default_style => :small}
507
+ @attachment = Paperclip::Attachment.new(:avatar,
508
+ @instance,
509
+ styles)
510
+ now = Time.now
511
+ Time.stubs(:now).returns(now)
512
+ @attachment.assign(@file)
513
+ @attachment.save
514
+ end
515
+
516
+ teardown do
517
+ @file.close
518
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
519
+ end
520
+
521
+ should "should have matching to_s and url methods" do
522
+ file = @attachment.to_file
523
+ assert file
524
+ assert_match @attachment.to_s, @attachment.url
525
+ assert_match @attachment.to_s(:small), @attachment.url(:small)
526
+ file.close
527
+ end
528
+ end
529
+
530
+ context "An attachment" do
531
+ setup do
532
+ @old_defaults = Paperclip::Attachment.default_options.dup
533
+ Paperclip::Attachment.default_options.merge!({
534
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
535
+ })
536
+ FileUtils.rm_rf("tmp")
537
+ rebuild_model
538
+ @instance = Dummy.new
539
+ @instance.stubs(:id).returns 123
540
+ @attachment = Paperclip::Attachment.new(:avatar, @instance)
541
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
542
+ end
543
+
544
+ teardown do
545
+ @file.close
546
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
547
+ end
548
+
549
+ should "raise if there are not the correct columns when you try to assign" do
550
+ @other_attachment = Paperclip::Attachment.new(:not_here, @instance)
551
+ assert_raises(Paperclip::PaperclipError) do
552
+ @other_attachment.assign(@file)
553
+ end
554
+ end
555
+
556
+ should "return its default_url when no file assigned" do
557
+ assert @attachment.to_file.nil?
558
+ assert_equal "/avatars/original/missing.png", @attachment.url
559
+ assert_equal "/avatars/blah/missing.png", @attachment.url(:blah)
560
+ end
561
+
562
+ should "return nil as path when no file assigned" do
563
+ assert @attachment.to_file.nil?
564
+ assert_equal nil, @attachment.path
565
+ assert_equal nil, @attachment.path(:blah)
566
+ end
567
+
568
+ context "with a file assigned in the database" do
569
+ setup do
570
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
571
+ @attachment.stubs(:instance_read).with(:content_type).returns("image/png")
572
+ @attachment.stubs(:instance_read).with(:file_size).returns(12345)
573
+ dtnow = DateTime.now
574
+ @now = Time.now
575
+ Time.stubs(:now).returns(@now)
576
+ @attachment.stubs(:instance_read).with(:updated_at).returns(dtnow)
577
+ end
578
+
579
+ should "return a correct url even if the file does not exist" do
580
+ assert_nil @attachment.to_file
581
+ assert_match %r{^/system/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah)
582
+ end
583
+
584
+ should "make sure the updated_at mtime is in the url if it is defined" do
585
+ assert_match %r{#{@now.to_i}$}, @attachment.url(:blah)
586
+ end
587
+
588
+ should "make sure the updated_at mtime is NOT in the url if false is passed to the url method" do
589
+ assert_no_match %r{#{@now.to_i}$}, @attachment.url(:blah, false)
590
+ end
591
+
592
+ context "with the updated_at field removed" do
593
+ setup do
594
+ @attachment.stubs(:instance_read).with(:updated_at).returns(nil)
595
+ end
596
+
597
+ should "only return the url without the updated_at when sent #url" do
598
+ assert_match "/avatars/#{@instance.id}/blah/5k.png", @attachment.url(:blah)
599
+ end
600
+ end
601
+
602
+ should "return the proper path when filename has a single .'s" do
603
+ assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
604
+ end
605
+
606
+ should "return the proper path when filename has multiple .'s" do
607
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.old.png")
608
+ assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png"), File.expand_path(@attachment.path)
609
+ end
610
+
611
+ context "when expecting three styles" do
612
+ setup do
613
+ styles = {:styles => { :large => ["400x400", :png],
614
+ :medium => ["100x100", :gif],
615
+ :small => ["32x32#", :jpg]}}
616
+ @attachment = Paperclip::Attachment.new(:avatar,
617
+ @instance,
618
+ styles)
619
+ end
620
+
621
+ context "and assigned a file" do
622
+ setup do
623
+ now = Time.now
624
+ Time.stubs(:now).returns(now)
625
+ @attachment.assign(@file)
626
+ end
627
+
628
+ should "be dirty" do
629
+ assert @attachment.dirty?
630
+ end
631
+
632
+ context "and saved" do
633
+ setup do
634
+ @attachment.save
635
+ end
636
+
637
+ should "return the real url" do
638
+ file = @attachment.to_file
639
+ assert file
640
+ assert_match %r{^/system/avatars/#{@instance.id}/original/5k\.png}, @attachment.url
641
+ assert_match %r{^/system/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small)
642
+ file.close
643
+ end
644
+
645
+ should "commit the files to disk" do
646
+ [:large, :medium, :small].each do |style|
647
+ io = @attachment.to_file(style)
648
+ # p "in commit to disk test, io is #{io.inspect} and @instance.id is #{@instance.id}"
649
+ assert File.exists?(io.path)
650
+ assert ! io.is_a?(::Tempfile)
651
+ io.close
652
+ end
653
+ end
654
+
655
+ should "save the files as the right formats and sizes" do
656
+ [[:large, 400, 61, "PNG"],
657
+ [:medium, 100, 15, "GIF"],
658
+ [:small, 32, 32, "JPEG"]].each do |style|
659
+ cmd = %Q[identify -format "%w %h %b %m" "#{@attachment.path(style.first)}"]
660
+ out = `#{cmd}`
661
+ width, height, size, format = out.split(" ")
662
+ assert_equal style[1].to_s, width.to_s
663
+ assert_equal style[2].to_s, height.to_s
664
+ assert_equal style[3].to_s, format.to_s
665
+ end
666
+ end
667
+
668
+ should "still have its #file attribute not be nil" do
669
+ assert ! (file = @attachment.to_file).nil?
670
+ file.close
671
+ end
672
+
673
+ context "and trying to delete" do
674
+ setup do
675
+ @existing_names = @attachment.styles.keys.collect do |style|
676
+ @attachment.path(style)
677
+ end
678
+ end
679
+
680
+ should "delete the files after assigning nil" do
681
+ @attachment.expects(:instance_write).with(:file_name, nil)
682
+ @attachment.expects(:instance_write).with(:content_type, nil)
683
+ @attachment.expects(:instance_write).with(:file_size, nil)
684
+ @attachment.expects(:instance_write).with(:updated_at, nil)
685
+ @attachment.assign nil
686
+ @attachment.save
687
+ @existing_names.each{|f| assert ! File.exists?(f) }
688
+ end
689
+
690
+ should "delete the files when you call #clear and #save" do
691
+ @attachment.expects(:instance_write).with(:file_name, nil)
692
+ @attachment.expects(:instance_write).with(:content_type, nil)
693
+ @attachment.expects(:instance_write).with(:file_size, nil)
694
+ @attachment.expects(:instance_write).with(:updated_at, nil)
695
+ @attachment.clear
696
+ @attachment.save
697
+ @existing_names.each{|f| assert ! File.exists?(f) }
698
+ end
699
+
700
+ should "delete the files when you call #delete" do
701
+ @attachment.expects(:instance_write).with(:file_name, nil)
702
+ @attachment.expects(:instance_write).with(:content_type, nil)
703
+ @attachment.expects(:instance_write).with(:file_size, nil)
704
+ @attachment.expects(:instance_write).with(:updated_at, nil)
705
+ @attachment.destroy
706
+ @existing_names.each{|f| assert ! File.exists?(f) }
707
+ end
708
+ end
709
+ end
710
+ end
711
+ end
712
+
713
+ end
714
+
715
+ context "when trying a nonexistant storage type" do
716
+ setup do
717
+ rebuild_model :storage => :not_here
718
+ end
719
+
720
+ should "not be able to find the module" do
721
+ assert_raise(Paperclip::StorageMethodNotFound){ Dummy.new.avatar }
722
+ end
723
+ end
724
+ end
725
+
726
+ context "An attachment with only a avatar_file_name column" do
727
+ setup do
728
+ ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
729
+ table.column :avatar_file_name, :string
730
+ end
731
+ rebuild_class
732
+ @dummy = Dummy.new
733
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
734
+ end
735
+
736
+ teardown { @file.close }
737
+
738
+ should "not error when assigned an attachment" do
739
+ assert_nothing_raised { @dummy.avatar = @file }
740
+ end
741
+
742
+ should "return the time when sent #avatar_updated_at" do
743
+ now = Time.now
744
+ Time.stubs(:now).returns(now)
745
+ @dummy.avatar = @file
746
+ assert_equal now.to_i, @dummy.avatar.updated_at.to_i
747
+ end
748
+
749
+ should "return nil when reloaded and sent #avatar_updated_at" do
750
+ @dummy.save
751
+ @dummy.reload
752
+ assert_nil @dummy.avatar.updated_at
753
+ end
754
+
755
+ should "return the right value when sent #avatar_file_size" do
756
+ @dummy.avatar = @file
757
+ assert_equal @file.size, @dummy.avatar.size
758
+ end
759
+
760
+ context "and avatar_updated_at column" do
761
+ setup do
762
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_updated_at, :timestamp
763
+ rebuild_class
764
+ @dummy = Dummy.new
765
+ end
766
+
767
+ should "not error when assigned an attachment" do
768
+ assert_nothing_raised { @dummy.avatar = @file }
769
+ end
770
+
771
+ should "return the right value when sent #avatar_updated_at" do
772
+ now = Time.now
773
+ Time.stubs(:now).returns(now)
774
+ @dummy.avatar = @file
775
+ assert_equal now.to_i, @dummy.avatar.updated_at
776
+ end
777
+ end
778
+
779
+ context "and avatar_content_type column" do
780
+ setup do
781
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_content_type, :string
782
+ rebuild_class
783
+ @dummy = Dummy.new
784
+ end
785
+
786
+ should "not error when assigned an attachment" do
787
+ assert_nothing_raised { @dummy.avatar = @file }
788
+ end
789
+
790
+ should "return the right value when sent #avatar_content_type" do
791
+ @dummy.avatar = @file
792
+ assert_equal "image/png", @dummy.avatar.content_type
793
+ end
794
+ end
795
+
796
+ context "and avatar_file_size column" do
797
+ setup do
798
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_file_size, :integer
799
+ rebuild_class
800
+ @dummy = Dummy.new
801
+ end
802
+
803
+ should "not error when assigned an attachment" do
804
+ assert_nothing_raised { @dummy.avatar = @file }
805
+ end
806
+
807
+ should "return the right value when sent #avatar_file_size" do
808
+ @dummy.avatar = @file
809
+ assert_equal @file.size, @dummy.avatar.size
810
+ end
811
+
812
+ should "return the right value when saved, reloaded, and sent #avatar_file_size" do
813
+ @dummy.avatar = @file
814
+ @dummy.save
815
+ @dummy = Dummy.find(@dummy.id)
816
+ assert_equal @file.size, @dummy.avatar.size
817
+ end
818
+ end
819
+
820
+ context "and avatar_fingerprint column" do
821
+ setup do
822
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_fingerprint, :string
823
+ rebuild_class
824
+ @dummy = Dummy.new
825
+ end
826
+
827
+ should "not error when assigned an attachment" do
828
+ assert_nothing_raised { @dummy.avatar = @file }
829
+ end
830
+
831
+ should "return the right value when sent #avatar_fingerprint" do
832
+ @dummy.avatar = @file
833
+ assert_equal 'aec488126c3b33c08a10c3fa303acf27', @dummy.avatar_fingerprint
834
+ end
835
+
836
+ should "return the right value when saved, reloaded, and sent #avatar_fingerprint" do
837
+ @dummy.avatar = @file
838
+ @dummy.save
839
+ @dummy = Dummy.find(@dummy.id)
840
+ assert_equal 'aec488126c3b33c08a10c3fa303acf27', @dummy.avatar_fingerprint
841
+ end
842
+ end
843
+ end
844
+ end