dejanstrbac-paperclip 2.2.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/LICENSE +26 -0
  2. data/README.rdoc +174 -0
  3. data/Rakefile +99 -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/paperclip.rb +350 -0
  9. data/lib/paperclip/attachment.rb +421 -0
  10. data/lib/paperclip/callback_compatability.rb +33 -0
  11. data/lib/paperclip/geometry.rb +115 -0
  12. data/lib/paperclip/interpolations.rb +105 -0
  13. data/lib/paperclip/iostream.rb +58 -0
  14. data/lib/paperclip/matchers.rb +4 -0
  15. data/lib/paperclip/matchers/have_attached_file_matcher.rb +49 -0
  16. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +66 -0
  17. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +48 -0
  18. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +83 -0
  19. data/lib/paperclip/processor.rb +49 -0
  20. data/lib/paperclip/storage.rb +236 -0
  21. data/lib/paperclip/thumbnail.rb +70 -0
  22. data/lib/paperclip/upfile.rb +48 -0
  23. data/shoulda_macros/paperclip.rb +68 -0
  24. data/tasks/paperclip_tasks.rake +79 -0
  25. data/test/attachment_test.rb +767 -0
  26. data/test/database.yml +4 -0
  27. data/test/fixtures/12k.png +0 -0
  28. data/test/fixtures/50x50.png +0 -0
  29. data/test/fixtures/5k.png +0 -0
  30. data/test/fixtures/bad.png +1 -0
  31. data/test/fixtures/s3.yml +4 -0
  32. data/test/fixtures/text.txt +0 -0
  33. data/test/fixtures/twopage.pdf +0 -0
  34. data/test/geometry_test.rb +177 -0
  35. data/test/helper.rb +99 -0
  36. data/test/integration_test.rb +481 -0
  37. data/test/interpolations_test.rb +120 -0
  38. data/test/iostream_test.rb +71 -0
  39. data/test/matchers/have_attached_file_matcher_test.rb +21 -0
  40. data/test/matchers/validate_attachment_content_type_matcher_test.rb +30 -0
  41. data/test/matchers/validate_attachment_presence_matcher_test.rb +21 -0
  42. data/test/matchers/validate_attachment_size_matcher_test.rb +50 -0
  43. data/test/paperclip_test.rb +291 -0
  44. data/test/processor_test.rb +10 -0
  45. data/test/storage_test.rb +282 -0
  46. data/test/thumbnail_test.rb +177 -0
  47. metadata +124 -0
@@ -0,0 +1,68 @@
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
+ end
64
+ end
65
+
66
+ class Test::Unit::TestCase #:nodoc:
67
+ extend Paperclip::Shoulda
68
+ end
@@ -0,0 +1,79 @@
1
+ def obtain_class
2
+ class_name = ENV['CLASS'] || ENV['class']
3
+ raise "Must specify CLASS" unless class_name
4
+ @klass = Object.const_get(class_name)
5
+ end
6
+
7
+ def obtain_attachments
8
+ name = ENV['ATTACHMENT'] || ENV['attachment']
9
+ raise "Class #{@klass.name} has no attachments specified" unless @klass.respond_to?(:attachment_definitions)
10
+ if !name.blank? && @klass.attachment_definitions.keys.include?(name)
11
+ [ name ]
12
+ else
13
+ @klass.attachment_definitions.keys
14
+ end
15
+ end
16
+
17
+ def for_all_attachments
18
+ klass = obtain_class
19
+ names = obtain_attachments
20
+ ids = klass.connection.select_values(klass.send(:construct_finder_sql, :select => 'id'))
21
+
22
+ ids.each do |id|
23
+ instance = klass.find(id)
24
+ names.each do |name|
25
+ result = if instance.send("#{ name }?")
26
+ yield(instance, name)
27
+ else
28
+ true
29
+ end
30
+ print result ? "." : "x"; $stdout.flush
31
+ end
32
+ end
33
+ puts " Done."
34
+ end
35
+
36
+ namespace :paperclip do
37
+ desc "Refreshes both metadata and thumbnails."
38
+ task :refresh => ["paperclip:refresh:metadata", "paperclip:refresh:thumbnails"]
39
+
40
+ namespace :refresh do
41
+ desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT)."
42
+ task :thumbnails => :environment do
43
+ errors = []
44
+ for_all_attachments do |instance, name|
45
+ result = instance.send(name).reprocess!
46
+ errors << [instance.id, instance.errors] unless instance.errors.blank?
47
+ result
48
+ end
49
+ errors.each{|e| puts "#{e.first}: #{e.last.full_messages.inspect}" }
50
+ end
51
+
52
+ desc "Regenerates content_type/size metadata for a given CLASS (and optional ATTACHMENT)."
53
+ task :metadata => :environment do
54
+ for_all_attachments do |instance, name|
55
+ if file = instance.send(name).to_file
56
+ instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
57
+ instance.send("#{name}_content_type=", file.content_type.strip)
58
+ instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
59
+ instance.save(false)
60
+ else
61
+ true
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ desc "Cleans out invalid attachments. Useful after you've added new validations."
68
+ task :clean => :environment do
69
+ for_all_attachments do |instance, name|
70
+ instance.send(name).send(:validate)
71
+ if instance.send(name).valid?
72
+ true
73
+ else
74
+ instance.send("#{name}=", nil)
75
+ instance.save
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,767 @@
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
+ end
110
+
111
+ should "return the proper path" do
112
+ temporary_rails_env(@rails_env) {
113
+ assert_equal "#{@rails_env}/#{@id}.png", @dummy.avatar.path
114
+ }
115
+ end
116
+ end
117
+
118
+ context "An attachment with a default style and an extension interpolation" do
119
+ setup do
120
+ @attachment = attachment :path => ":basename.:extension",
121
+ :styles => { :default => ["100x100", :png] },
122
+ :default_style => :default
123
+ @file = StringIO.new("...")
124
+ @file.expects(:original_filename).returns("file.jpg")
125
+ end
126
+ should "return the right extension for the path" do
127
+ @attachment.assign(@file)
128
+ assert_equal "file.png", @attachment.path
129
+ end
130
+ end
131
+
132
+ context "An attachment with :convert_options" do
133
+ setup do
134
+ rebuild_model :styles => {
135
+ :thumb => "100x100",
136
+ :large => "400x400"
137
+ },
138
+ :convert_options => {
139
+ :all => "-do_stuff",
140
+ :thumb => "-thumbnailize"
141
+ }
142
+ @dummy = Dummy.new
143
+ @dummy.avatar
144
+ end
145
+
146
+ should "report the correct options when sent #extra_options_for(:thumb)" do
147
+ assert_equal "-thumbnailize -do_stuff", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
148
+ end
149
+
150
+ should "report the correct options when sent #extra_options_for(:large)" do
151
+ assert_equal "-do_stuff", @dummy.avatar.send(:extra_options_for, :large)
152
+ end
153
+
154
+ before_should "call extra_options_for(:thumb/:large)" do
155
+ Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb)
156
+ Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large)
157
+ end
158
+ end
159
+
160
+ context "An attachment with :convert_options that is a proc" do
161
+ setup do
162
+ rebuild_model :styles => {
163
+ :thumb => "100x100",
164
+ :large => "400x400"
165
+ },
166
+ :convert_options => {
167
+ :all => lambda{|i| i.all },
168
+ :thumb => lambda{|i| i.thumb }
169
+ }
170
+ Dummy.class_eval do
171
+ def all; "-all"; end
172
+ def thumb; "-thumb"; end
173
+ end
174
+ @dummy = Dummy.new
175
+ @dummy.avatar
176
+ end
177
+
178
+ should "report the correct options when sent #extra_options_for(:thumb)" do
179
+ assert_equal "-thumb -all", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
180
+ end
181
+
182
+ should "report the correct options when sent #extra_options_for(:large)" do
183
+ assert_equal "-all", @dummy.avatar.send(:extra_options_for, :large)
184
+ end
185
+
186
+ before_should "call extra_options_for(:thumb/:large)" do
187
+ Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb)
188
+ Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large)
189
+ end
190
+ end
191
+
192
+ context "An attachment with :path that is a proc" do
193
+ setup do
194
+ rebuild_model :path => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
195
+
196
+ @file = File.new(File.join(File.dirname(__FILE__),
197
+ "fixtures",
198
+ "5k.png"), 'rb')
199
+ @dummyA = Dummy.new(:other => 'a')
200
+ @dummyA.avatar = @file
201
+ @dummyB = Dummy.new(:other => 'b')
202
+ @dummyB.avatar = @file
203
+ end
204
+
205
+ teardown { @file.close }
206
+
207
+ should "return correct path" do
208
+ assert_equal "path/a.png", @dummyA.avatar.path
209
+ assert_equal "path/b.png", @dummyB.avatar.path
210
+ end
211
+ end
212
+
213
+ context "An attachment with :styles that is a proc" do
214
+ setup do
215
+ rebuild_model :styles => lambda{ |attachment| {:thumb => "50x50#", :large => "400x400"} }
216
+
217
+ @attachment = Dummy.new.avatar
218
+ end
219
+
220
+ should "have the correct geometry" do
221
+ assert_equal "50x50#", @attachment.styles[:thumb][:geometry]
222
+ end
223
+ end
224
+
225
+ context "An attachment with :url that is a proc" do
226
+ setup do
227
+ rebuild_model :url => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
228
+
229
+ @file = File.new(File.join(File.dirname(__FILE__),
230
+ "fixtures",
231
+ "5k.png"), 'rb')
232
+ @dummyA = Dummy.new(:other => 'a')
233
+ @dummyA.avatar = @file
234
+ @dummyB = Dummy.new(:other => 'b')
235
+ @dummyB.avatar = @file
236
+ end
237
+
238
+ teardown { @file.close }
239
+
240
+ should "return correct url" do
241
+ assert_equal "path/a.png", @dummyA.avatar.url(:original, false)
242
+ assert_equal "path/b.png", @dummyB.avatar.url(:original, false)
243
+ end
244
+ end
245
+
246
+ geometry_specs = [
247
+ [ lambda{|z| "50x50#" }, :png ],
248
+ lambda{|z| "50x50#" },
249
+ { :geometry => lambda{|z| "50x50#" } }
250
+ ]
251
+ geometry_specs.each do |geometry_spec|
252
+ context "An attachment geometry like #{geometry_spec}" do
253
+ setup do
254
+ rebuild_model :styles => { :normal => geometry_spec }
255
+ @attachment = Dummy.new.avatar
256
+ end
257
+
258
+ should "not run the procs immediately" do
259
+ assert_kind_of Proc, @attachment.styles[:normal][:geometry]
260
+ end
261
+
262
+ context "when assigned" do
263
+ setup do
264
+ @file = StringIO.new(".")
265
+ @attachment.assign(@file)
266
+ end
267
+
268
+ should "have the correct geometry" do
269
+ assert_equal "50x50#", @attachment.styles[:normal][:geometry]
270
+ end
271
+ end
272
+ end
273
+ end
274
+
275
+ context "An attachment with both 'normal' and hash-style styles" do
276
+ setup do
277
+ rebuild_model :styles => {
278
+ :normal => ["50x50#", :png],
279
+ :hash => { :geometry => "50x50#", :format => :png }
280
+ }
281
+ @dummy = Dummy.new
282
+ @attachment = @dummy.avatar
283
+ end
284
+
285
+ [:processors, :whiny, :convert_options, :geometry, :format].each do |field|
286
+ should "have the same #{field} field" do
287
+ assert_equal @attachment.styles[:normal][field], @attachment.styles[:hash][field]
288
+ end
289
+ end
290
+ end
291
+
292
+ context "An attachment with :processors that is a proc" do
293
+ setup do
294
+ rebuild_model :styles => { :normal => '' }, :processors => lambda { |a| [ :test ] }
295
+ @attachment = Dummy.new.avatar
296
+ end
297
+
298
+ should "not run the proc immediately" do
299
+ assert_kind_of Proc, @attachment.styles[:normal][:processors]
300
+ end
301
+
302
+ context "when assigned" do
303
+ setup do
304
+ @attachment.assign(StringIO.new("."))
305
+ end
306
+
307
+ should "have the correct processors" do
308
+ assert_equal [ :test ], @attachment.styles[:normal][:processors]
309
+ end
310
+ end
311
+ end
312
+
313
+ context "An attachment with erroring processor" do
314
+ setup do
315
+ rebuild_model :processor => [:thumbnail], :styles => { :small => '' }, :whiny_thumbnails => true
316
+ @dummy = Dummy.new
317
+ Paperclip::Thumbnail.expects(:make).raises(Paperclip::PaperclipError, "cannot be processed.")
318
+ @file = StringIO.new("...")
319
+ @file.stubs(:to_tempfile).returns(@file)
320
+ @dummy.avatar = @file
321
+ end
322
+
323
+ should "correctly forward processing error message to the instance" do
324
+ @dummy.valid?
325
+ assert_contains @dummy.errors.full_messages, "Avatar cannot be processed."
326
+ end
327
+ end
328
+
329
+ context "An attachment with multiple processors" do
330
+ setup do
331
+ class Paperclip::Test < Paperclip::Processor; end
332
+ @style_params = { :once => {:one => 1, :two => 2} }
333
+ rebuild_model :processors => [:thumbnail, :test], :styles => @style_params
334
+ @dummy = Dummy.new
335
+ @file = StringIO.new("...")
336
+ @file.stubs(:to_tempfile).returns(@file)
337
+ Paperclip::Test.stubs(:make).returns(@file)
338
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
339
+ end
340
+
341
+ context "when assigned" do
342
+ setup { @dummy.avatar = @file }
343
+
344
+ before_should "call #make on all specified processors" do
345
+ expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
346
+ Paperclip::Thumbnail.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
347
+ Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
348
+ end
349
+
350
+ before_should "call #make with attachment passed as third argument" do
351
+ expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
352
+ Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
353
+ end
354
+ end
355
+ end
356
+
357
+ context "An attachment with no processors defined" do
358
+ setup do
359
+ rebuild_model :processors => [], :styles => {:something => 1}
360
+ @dummy = Dummy.new
361
+ @file = StringIO.new("...")
362
+ end
363
+ should "raise when assigned to" do
364
+ assert_raises(RuntimeError){ @dummy.avatar = @file }
365
+ end
366
+ end
367
+
368
+ context "Assigning an attachment with post_process hooks" do
369
+ setup do
370
+ rebuild_model :styles => { :something => "100x100#" }
371
+ Dummy.class_eval do
372
+ before_avatar_post_process :do_before_avatar
373
+ after_avatar_post_process :do_after_avatar
374
+ before_post_process :do_before_all
375
+ after_post_process :do_after_all
376
+ def do_before_avatar; end
377
+ def do_after_avatar; end
378
+ def do_before_all; end
379
+ def do_after_all; end
380
+ end
381
+ @file = StringIO.new(".")
382
+ @file.stubs(:to_tempfile).returns(@file)
383
+ @dummy = Dummy.new
384
+ Paperclip::Thumbnail.stubs(:make).returns(@file)
385
+ @attachment = @dummy.avatar
386
+ end
387
+
388
+ should "call the defined callbacks when assigned" do
389
+ @dummy.expects(:do_before_avatar).with()
390
+ @dummy.expects(:do_after_avatar).with()
391
+ @dummy.expects(:do_before_all).with()
392
+ @dummy.expects(:do_after_all).with()
393
+ Paperclip::Thumbnail.expects(:make).returns(@file)
394
+ @dummy.avatar = @file
395
+ end
396
+
397
+ should "not cancel the processing if a before_post_process returns nil" do
398
+ @dummy.expects(:do_before_avatar).with().returns(nil)
399
+ @dummy.expects(:do_after_avatar).with()
400
+ @dummy.expects(:do_before_all).with().returns(nil)
401
+ @dummy.expects(:do_after_all).with()
402
+ Paperclip::Thumbnail.expects(:make).returns(@file)
403
+ @dummy.avatar = @file
404
+ end
405
+
406
+ should "cancel the processing if a before_post_process returns false" do
407
+ @dummy.expects(:do_before_avatar).never
408
+ @dummy.expects(:do_after_avatar).never
409
+ @dummy.expects(:do_before_all).with().returns(false)
410
+ @dummy.expects(:do_after_all).never
411
+ Paperclip::Thumbnail.expects(:make).never
412
+ @dummy.avatar = @file
413
+ end
414
+
415
+ should "cancel the processing if a before_avatar_post_process returns false" do
416
+ @dummy.expects(:do_before_avatar).with().returns(false)
417
+ @dummy.expects(:do_after_avatar).never
418
+ @dummy.expects(:do_before_all).with().returns(true)
419
+ @dummy.expects(:do_after_all).never
420
+ Paperclip::Thumbnail.expects(:make).never
421
+ @dummy.avatar = @file
422
+ end
423
+ end
424
+
425
+ context "Assigning an attachment" do
426
+ setup do
427
+ rebuild_model :styles => { :something => "100x100#" }
428
+ @file = StringIO.new(".")
429
+ @file.expects(:original_filename).returns("5k.png\n\n")
430
+ @file.expects(:content_type).returns("image/png\n\n")
431
+ @file.stubs(:to_tempfile).returns(@file)
432
+ @dummy = Dummy.new
433
+ Paperclip::Thumbnail.expects(:make).returns(@file)
434
+ @dummy.expects(:run_callbacks).with(:before_avatar_post_process, {:original => @file})
435
+ @dummy.expects(:run_callbacks).with(:before_post_process, {:original => @file})
436
+ @dummy.expects(:run_callbacks).with(:after_avatar_post_process, {:original => @file, :something => @file})
437
+ @dummy.expects(:run_callbacks).with(:after_post_process, {:original => @file, :something => @file})
438
+ @attachment = @dummy.avatar
439
+ @dummy.avatar = @file
440
+ end
441
+
442
+ should "strip whitespace from original_filename field" do
443
+ assert_equal "5k.png", @dummy.avatar.original_filename
444
+ end
445
+
446
+ should "strip whitespace from content_type field" do
447
+ assert_equal "image/png", @dummy.avatar.instance.avatar_content_type
448
+ end
449
+ end
450
+
451
+ context "Attachment with strange letters" do
452
+ setup do
453
+ rebuild_model
454
+
455
+ @not_file = mock
456
+ @tempfile = mock
457
+ @not_file.stubs(:nil?).returns(false)
458
+ @not_file.expects(:size).returns(10)
459
+ @tempfile.expects(:size).returns(10)
460
+ @not_file.expects(:to_tempfile).returns(@tempfile)
461
+ @not_file.expects(:original_filename).returns("sheep_say_bæ.png\r\n")
462
+ @not_file.expects(:content_type).returns("image/png\r\n")
463
+
464
+ @dummy = Dummy.new
465
+ @attachment = @dummy.avatar
466
+ @attachment.expects(:valid_assignment?).with(@not_file).returns(true)
467
+ @attachment.expects(:queue_existing_for_delete)
468
+ @attachment.expects(:post_process)
469
+ @attachment.expects(:valid?).returns(true)
470
+ @attachment.expects(:validate)
471
+ @dummy.avatar = @not_file
472
+ end
473
+
474
+ should "remove strange letters and replace with underscore (_)" do
475
+ assert_equal "sheep_say_b_.png", @dummy.avatar.original_filename
476
+ end
477
+
478
+ end
479
+
480
+ context "An attachment" do
481
+ setup do
482
+ @old_defaults = Paperclip::Attachment.default_options.dup
483
+ Paperclip::Attachment.default_options.merge!({
484
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
485
+ })
486
+ FileUtils.rm_rf("tmp")
487
+ rebuild_model
488
+ @instance = Dummy.new
489
+ @attachment = Paperclip::Attachment.new(:avatar, @instance)
490
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
491
+ end
492
+
493
+ teardown do
494
+ @file.close
495
+ Paperclip::Attachment.default_options.merge!(@old_defaults)
496
+ end
497
+
498
+ should "raise if there are not the correct columns when you try to assign" do
499
+ @other_attachment = Paperclip::Attachment.new(:not_here, @instance)
500
+ assert_raises(Paperclip::PaperclipError) do
501
+ @other_attachment.assign(@file)
502
+ end
503
+ end
504
+
505
+ should "return its default_url when no file assigned" do
506
+ assert @attachment.to_file.nil?
507
+ assert_equal "/avatars/original/missing.png", @attachment.url
508
+ assert_equal "/avatars/blah/missing.png", @attachment.url(:blah)
509
+ end
510
+
511
+ should "return nil as path when no file assigned" do
512
+ assert @attachment.to_file.nil?
513
+ assert_equal nil, @attachment.path
514
+ assert_equal nil, @attachment.path(:blah)
515
+ end
516
+
517
+ context "with a file assigned in the database" do
518
+ setup do
519
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
520
+ @attachment.stubs(:instance_read).with(:content_type).returns("image/png")
521
+ @attachment.stubs(:instance_read).with(:file_size).returns(12345)
522
+ now = Time.now
523
+ Time.stubs(:now).returns(now)
524
+ @attachment.stubs(:instance_read).with(:updated_at).returns(Time.now)
525
+ end
526
+
527
+ should "return a correct url even if the file does not exist" do
528
+ assert_nil @attachment.to_file
529
+ assert_match %r{^/system/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah)
530
+ end
531
+
532
+ should "make sure the updated_at mtime is in the url if it is defined" do
533
+ assert_match %r{#{Time.now.to_i}$}, @attachment.url(:blah)
534
+ end
535
+
536
+ should "make sure the updated_at mtime is NOT in the url if false is passed to the url method" do
537
+ assert_no_match %r{#{Time.now.to_i}$}, @attachment.url(:blah, false)
538
+ end
539
+
540
+ context "with the updated_at field removed" do
541
+ setup do
542
+ @attachment.stubs(:instance_read).with(:updated_at).returns(nil)
543
+ end
544
+
545
+ should "only return the url without the updated_at when sent #url" do
546
+ assert_match "/avatars/#{@instance.id}/blah/5k.png", @attachment.url(:blah)
547
+ end
548
+ end
549
+
550
+ should "return the proper path when filename has a single .'s" do
551
+ assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
552
+ end
553
+
554
+ should "return the proper path when filename has multiple .'s" do
555
+ @attachment.stubs(:instance_read).with(:file_name).returns("5k.old.png")
556
+ assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png"), File.expand_path(@attachment.path)
557
+ end
558
+
559
+ context "when expecting three styles" do
560
+ setup do
561
+ styles = {:styles => { :large => ["400x400", :png],
562
+ :medium => ["100x100", :gif],
563
+ :small => ["32x32#", :jpg]}}
564
+ @attachment = Paperclip::Attachment.new(:avatar,
565
+ @instance,
566
+ styles)
567
+ end
568
+
569
+ context "and assigned a file" do
570
+ setup do
571
+ now = Time.now
572
+ Time.stubs(:now).returns(now)
573
+ @attachment.assign(@file)
574
+ end
575
+
576
+ should "be dirty" do
577
+ assert @attachment.dirty?
578
+ end
579
+
580
+ context "and saved" do
581
+ setup do
582
+ @attachment.save
583
+ end
584
+
585
+ should "return the real url" do
586
+ file = @attachment.to_file
587
+ assert file
588
+ assert_match %r{^/system/avatars/#{@instance.id}/original/5k\.png}, @attachment.url
589
+ assert_match %r{^/system/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small)
590
+ file.close
591
+ end
592
+
593
+ should "commit the files to disk" do
594
+ [:large, :medium, :small].each do |style|
595
+ io = @attachment.to_io(style)
596
+ assert File.exists?(io)
597
+ assert ! io.is_a?(::Tempfile)
598
+ io.close
599
+ end
600
+ end
601
+
602
+ should "save the files as the right formats and sizes" do
603
+ [[:large, 400, 61, "PNG"],
604
+ [:medium, 100, 15, "GIF"],
605
+ [:small, 32, 32, "JPEG"]].each do |style|
606
+ cmd = %Q[identify -format "%w %h %b %m" "#{@attachment.path(style.first)}"]
607
+ out = `#{cmd}`
608
+ width, height, size, format = out.split(" ")
609
+ assert_equal style[1].to_s, width.to_s
610
+ assert_equal style[2].to_s, height.to_s
611
+ assert_equal style[3].to_s, format.to_s
612
+ end
613
+ end
614
+
615
+ should "still have its #file attribute not be nil" do
616
+ assert ! (file = @attachment.to_file).nil?
617
+ file.close
618
+ end
619
+
620
+ context "and trying to delete" do
621
+ setup do
622
+ @existing_names = @attachment.styles.keys.collect do |style|
623
+ @attachment.path(style)
624
+ end
625
+ end
626
+
627
+ should "delete the files after assigning nil" do
628
+ @attachment.expects(:instance_write).with(:file_name, nil)
629
+ @attachment.expects(:instance_write).with(:content_type, nil)
630
+ @attachment.expects(:instance_write).with(:file_size, nil)
631
+ @attachment.expects(:instance_write).with(:updated_at, nil)
632
+ @attachment.assign nil
633
+ @attachment.save
634
+ @existing_names.each{|f| assert ! File.exists?(f) }
635
+ end
636
+
637
+ should "delete the files when you call #clear and #save" do
638
+ @attachment.expects(:instance_write).with(:file_name, nil)
639
+ @attachment.expects(:instance_write).with(:content_type, nil)
640
+ @attachment.expects(:instance_write).with(:file_size, nil)
641
+ @attachment.expects(:instance_write).with(:updated_at, nil)
642
+ @attachment.clear
643
+ @attachment.save
644
+ @existing_names.each{|f| assert ! File.exists?(f) }
645
+ end
646
+
647
+ should "delete the files when you call #delete" do
648
+ @attachment.expects(:instance_write).with(:file_name, nil)
649
+ @attachment.expects(:instance_write).with(:content_type, nil)
650
+ @attachment.expects(:instance_write).with(:file_size, nil)
651
+ @attachment.expects(:instance_write).with(:updated_at, nil)
652
+ @attachment.destroy
653
+ @existing_names.each{|f| assert ! File.exists?(f) }
654
+ end
655
+ end
656
+ end
657
+ end
658
+ end
659
+
660
+ end
661
+
662
+ context "when trying a nonexistant storage type" do
663
+ setup do
664
+ rebuild_model :storage => :not_here
665
+ end
666
+
667
+ should "not be able to find the module" do
668
+ assert_raise(NameError){ Dummy.new.avatar }
669
+ end
670
+ end
671
+ end
672
+
673
+ context "An attachment with only a avatar_file_name column" do
674
+ setup do
675
+ ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
676
+ table.column :avatar_file_name, :string
677
+ end
678
+ rebuild_class
679
+ @dummy = Dummy.new
680
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
681
+ end
682
+
683
+ teardown { @file.close }
684
+
685
+ should "not error when assigned an attachment" do
686
+ assert_nothing_raised { @dummy.avatar = @file }
687
+ end
688
+
689
+ should "return the time when sent #avatar_updated_at" do
690
+ now = Time.now
691
+ Time.stubs(:now).returns(now)
692
+ @dummy.avatar = @file
693
+ assert now, @dummy.avatar.updated_at
694
+ end
695
+
696
+ should "return nil when reloaded and sent #avatar_updated_at" do
697
+ @dummy.save
698
+ @dummy.reload
699
+ assert_nil @dummy.avatar.updated_at
700
+ end
701
+
702
+ should "return the right value when sent #avatar_file_size" do
703
+ @dummy.avatar = @file
704
+ assert_equal @file.size, @dummy.avatar.size
705
+ end
706
+
707
+ context "and avatar_updated_at column" do
708
+ setup do
709
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_updated_at, :timestamp
710
+ rebuild_class
711
+ @dummy = Dummy.new
712
+ end
713
+
714
+ should "not error when assigned an attachment" do
715
+ assert_nothing_raised { @dummy.avatar = @file }
716
+ end
717
+
718
+ should "return the right value when sent #avatar_updated_at" do
719
+ now = Time.now
720
+ Time.stubs(:now).returns(now)
721
+ @dummy.avatar = @file
722
+ assert_equal now.to_i, @dummy.avatar.updated_at
723
+ end
724
+ end
725
+
726
+ context "and avatar_content_type column" do
727
+ setup do
728
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_content_type, :string
729
+ rebuild_class
730
+ @dummy = Dummy.new
731
+ end
732
+
733
+ should "not error when assigned an attachment" do
734
+ assert_nothing_raised { @dummy.avatar = @file }
735
+ end
736
+
737
+ should "return the right value when sent #avatar_content_type" do
738
+ @dummy.avatar = @file
739
+ assert_equal "image/png", @dummy.avatar.content_type
740
+ end
741
+ end
742
+
743
+ context "and avatar_file_size column" do
744
+ setup do
745
+ ActiveRecord::Base.connection.add_column :dummies, :avatar_file_size, :integer
746
+ rebuild_class
747
+ @dummy = Dummy.new
748
+ end
749
+
750
+ should "not error when assigned an attachment" do
751
+ assert_nothing_raised { @dummy.avatar = @file }
752
+ end
753
+
754
+ should "return the right value when sent #avatar_file_size" do
755
+ @dummy.avatar = @file
756
+ assert_equal @file.size, @dummy.avatar.size
757
+ end
758
+
759
+ should "return the right value when saved, reloaded, and sent #avatar_file_size" do
760
+ @dummy.avatar = @file
761
+ @dummy.save
762
+ @dummy = Dummy.find(@dummy.id)
763
+ assert_equal @file.size, @dummy.avatar.size
764
+ end
765
+ end
766
+ end
767
+ end