whitby3001-paperclip-cloudfiles 2.3.8.1

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