path-paperclip 2.3.3

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 (57) hide show
  1. data/LICENSE +26 -0
  2. data/README.rdoc +198 -0
  3. data/Rakefile +76 -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 +440 -0
  12. data/lib/paperclip/attachment.rb +401 -0
  13. data/lib/paperclip/callback_compatability.rb +61 -0
  14. data/lib/paperclip/geometry.rb +150 -0
  15. data/lib/paperclip/interpolations.rb +113 -0
  16. data/lib/paperclip/iostream.rb +59 -0
  17. data/lib/paperclip/matchers.rb +33 -0
  18. data/lib/paperclip/matchers/have_attached_file_matcher.rb +57 -0
  19. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +75 -0
  20. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +54 -0
  21. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +95 -0
  22. data/lib/paperclip/processor.rb +49 -0
  23. data/lib/paperclip/railtie.rb +20 -0
  24. data/lib/paperclip/storage.rb +258 -0
  25. data/lib/paperclip/style.rb +90 -0
  26. data/lib/paperclip/thumbnail.rb +78 -0
  27. data/lib/paperclip/upfile.rb +52 -0
  28. data/lib/paperclip/version.rb +3 -0
  29. data/lib/tasks/paperclip.rake +95 -0
  30. data/rails/init.rb +2 -0
  31. data/shoulda_macros/paperclip.rb +119 -0
  32. data/test/attachment_test.rb +796 -0
  33. data/test/database.yml +4 -0
  34. data/test/fixtures/12k.png +0 -0
  35. data/test/fixtures/50x50.png +0 -0
  36. data/test/fixtures/5k.png +0 -0
  37. data/test/fixtures/bad.png +1 -0
  38. data/test/fixtures/ceedub.gif +0 -0
  39. data/test/fixtures/s3.yml +8 -0
  40. data/test/fixtures/text.txt +1 -0
  41. data/test/fixtures/twopage.pdf +0 -0
  42. data/test/geometry_test.rb +177 -0
  43. data/test/helper.rb +152 -0
  44. data/test/integration_test.rb +610 -0
  45. data/test/interpolations_test.rb +135 -0
  46. data/test/iostream_test.rb +78 -0
  47. data/test/matchers/have_attached_file_matcher_test.rb +24 -0
  48. data/test/matchers/validate_attachment_content_type_matcher_test.rb +54 -0
  49. data/test/matchers/validate_attachment_presence_matcher_test.rb +26 -0
  50. data/test/matchers/validate_attachment_size_matcher_test.rb +51 -0
  51. data/test/paperclip_test.rb +389 -0
  52. data/test/processor_test.rb +10 -0
  53. data/test/storage_test.rb +407 -0
  54. data/test/style_test.rb +141 -0
  55. data/test/thumbnail_test.rb +227 -0
  56. data/test/upfile_test.rb +36 -0
  57. metadata +221 -0
@@ -0,0 +1,10 @@
1
+ require 'test/helper'
2
+
3
+ class ProcessorTest < Test::Unit::TestCase
4
+ should "instantiate and call #make when sent #make to the class" do
5
+ processor = mock
6
+ processor.expects(:make).with()
7
+ Paperclip::Processor.expects(:new).with(:one, :two, :three).returns(processor)
8
+ Paperclip::Processor.make(:one, :two, :three)
9
+ end
10
+ end
@@ -0,0 +1,407 @@
1
+ require 'test/helper'
2
+ require 'aws/s3'
3
+
4
+ class StorageTest < Test::Unit::TestCase
5
+ def rails_env(env)
6
+ silence_warnings do
7
+ Object.const_set(:Rails, stub('Rails', :env => env))
8
+ end
9
+ end
10
+
11
+ context "With a local file" do
12
+ setup do
13
+ rebuild_model
14
+ @dummy = Dummy.new
15
+ @avatar = @dummy.avatar
16
+ end
17
+
18
+ should "be extended by the Filesystem module" do
19
+ assert Dummy.new.avatar.is_a?(Paperclip::Storage::Filesystem)
20
+ end
21
+
22
+ should "call FileUtils.rm" do
23
+ @avatar.stubs(:file?).returns true
24
+ @avatar.stubs(:original_filename).returns "original.png"
25
+ File.stubs(:exist?).returns true
26
+
27
+ FileUtils.expects(:rm)
28
+ @dummy.destroy_attached_files
29
+ end
30
+
31
+ context "with #delete_files? == false" do
32
+ setup do
33
+ rebuild_model :delete_files => false
34
+ @dummy = Dummy.new
35
+ @avatar = @dummy.avatar
36
+ end
37
+
38
+ should "not call FileUtils.rm" do
39
+ @avatar.stubs(:file?).returns true
40
+ @avatar.stubs(:original_filename).returns "original.png"
41
+ File.stubs(:exist?).returns true
42
+
43
+ FileUtils.expects(:rm).never
44
+ @dummy.destroy_attached_files
45
+ end
46
+ end
47
+ end
48
+
49
+ context "Parsing S3 credentials" do
50
+ setup do
51
+ AWS::S3::Base.stubs(:establish_connection!)
52
+ rebuild_model :storage => :s3,
53
+ :bucket => "testing",
54
+ :s3_credentials => {:not => :important}
55
+
56
+ @dummy = Dummy.new
57
+ @avatar = @dummy.avatar
58
+ end
59
+
60
+ should "get the correct credentials when RAILS_ENV is production" do
61
+ rails_env("production")
62
+ assert_equal({:key => "12345"},
63
+ @avatar.parse_credentials('production' => {:key => '12345'},
64
+ :development => {:key => "54321"}))
65
+ end
66
+
67
+ should "get the correct credentials when RAILS_ENV is development" do
68
+ rails_env("development")
69
+ assert_equal({:key => "54321"},
70
+ @avatar.parse_credentials('production' => {:key => '12345'},
71
+ :development => {:key => "54321"}))
72
+ end
73
+
74
+ should "return the argument if the key does not exist" do
75
+ rails_env("not really an env")
76
+ assert_equal({:test => "12345"}, @avatar.parse_credentials(:test => "12345"))
77
+ end
78
+ end
79
+
80
+ context "" do
81
+ setup do
82
+ AWS::S3::Base.stubs(:establish_connection!)
83
+ rebuild_model :storage => :s3,
84
+ :s3_credentials => {},
85
+ :bucket => "bucket",
86
+ :path => ":attachment/:basename.:extension",
87
+ :url => ":s3_path_url"
88
+ @dummy = Dummy.new
89
+ @dummy.avatar = StringIO.new("text")
90
+ end
91
+
92
+ should "return a url based on an S3 path" do
93
+ assert_match %r{^http://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url
94
+ end
95
+ end
96
+
97
+ context "" do
98
+ setup do
99
+ AWS::S3::Base.stubs(:establish_connection!)
100
+ rebuild_model :storage => :s3,
101
+ :s3_credentials => {},
102
+ :bucket => "bucket",
103
+ :path => ":attachment/:basename.:extension",
104
+ :url => ":s3_domain_url"
105
+ @dummy = Dummy.new
106
+ @dummy.avatar = StringIO.new("text")
107
+ end
108
+
109
+ should "return a url based on an S3 subdomain" do
110
+ assert_match %r{^http://bucket.s3.amazonaws.com/avatars/stringio.txt}, @dummy.avatar.url
111
+ end
112
+ end
113
+
114
+ context "" do
115
+ setup do
116
+ AWS::S3::Base.stubs(:establish_connection!)
117
+ rebuild_model :storage => :s3,
118
+ :s3_credentials => {
119
+ :production => { :bucket => "prod_bucket" },
120
+ :development => { :bucket => "dev_bucket" }
121
+ },
122
+ :s3_host_alias => "something.something.com",
123
+ :path => ":attachment/:basename.:extension",
124
+ :url => ":s3_alias_url"
125
+ @dummy = Dummy.new
126
+ @dummy.avatar = StringIO.new("text")
127
+ end
128
+
129
+ should "return a url based on the host_alias" do
130
+ assert_match %r{^http://something.something.com/avatars/stringio.txt}, @dummy.avatar.url
131
+ end
132
+ end
133
+
134
+ context "Generating a url with an expiration" do
135
+ setup do
136
+ AWS::S3::Base.stubs(:establish_connection!)
137
+ rebuild_model :storage => :s3,
138
+ :s3_credentials => {
139
+ :production => { :bucket => "prod_bucket" },
140
+ :development => { :bucket => "dev_bucket" }
141
+ },
142
+ :s3_host_alias => "something.something.com",
143
+ :path => ":attachment/:basename.:extension",
144
+ :url => ":s3_alias_url"
145
+
146
+ rails_env("production")
147
+
148
+ @dummy = Dummy.new
149
+ @dummy.avatar = StringIO.new(".")
150
+
151
+ AWS::S3::S3Object.expects(:url_for).with("avatars/stringio.bin", "prod_bucket", { :expires_in => 3600 })
152
+
153
+ @dummy.avatar.expiring_url
154
+ end
155
+
156
+ should "should succeed" do
157
+ assert true
158
+ end
159
+ end
160
+
161
+ context "uses simple url" do
162
+ setup do
163
+ AWS::S3::Base.stubs(:establish_connection!)
164
+ rebuild_model :storage => :s3,
165
+ :s3_credentials => {
166
+ :production => { :bucket => "prod_bucket" },
167
+ :development => { :bucket => "dev_bucket" }
168
+ },
169
+ :s3_host_alias => "something.something.com",
170
+ :path => ":attachment/:basename.:extension",
171
+ :url => ":s3_simple_url"
172
+ @dummy = Dummy.new
173
+ @dummy.avatar = StringIO.new("text")
174
+ end
175
+
176
+ should "return a url based on the host_alias" do
177
+ assert_match %r{^/avatars/stringio.txt}, @dummy.avatar.url
178
+ end
179
+ end
180
+
181
+ context "Parsing S3 credentials with a bucket in them" do
182
+ setup do
183
+ AWS::S3::Base.stubs(:establish_connection!)
184
+ rebuild_model :storage => :s3,
185
+ :s3_credentials => {
186
+ :production => { :bucket => "prod_bucket" },
187
+ :development => { :bucket => "dev_bucket" }
188
+ }
189
+ @dummy = Dummy.new
190
+ end
191
+
192
+ should "get the right bucket in production" do
193
+ rails_env("production")
194
+ assert_equal "prod_bucket", @dummy.avatar.bucket_name
195
+ end
196
+
197
+ should "get the right bucket in development" do
198
+ rails_env("development")
199
+ assert_equal "dev_bucket", @dummy.avatar.bucket_name
200
+ end
201
+ end
202
+
203
+ context "An attachment with S3 storage" do
204
+ setup do
205
+ rebuild_model :storage => :s3,
206
+ :bucket => "testing",
207
+ :path => ":attachment/:style/:basename.:extension",
208
+ :s3_credentials => {
209
+ 'access_key_id' => "12345",
210
+ 'secret_access_key' => "54321"
211
+ }
212
+ end
213
+
214
+ should "be extended by the S3 module" do
215
+ assert Dummy.new.avatar.is_a?(Paperclip::Storage::S3)
216
+ end
217
+
218
+ should "not be extended by the Filesystem module" do
219
+ assert ! Dummy.new.avatar.is_a?(Paperclip::Storage::Filesystem)
220
+ end
221
+
222
+ context "when assigned" do
223
+ setup do
224
+ @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
225
+ @dummy = Dummy.new
226
+ @dummy.avatar = @file
227
+ end
228
+
229
+ teardown { @file.close }
230
+
231
+ should "not get a bucket to get a URL" do
232
+ @dummy.avatar.expects(:s3).never
233
+ @dummy.avatar.expects(:s3_bucket).never
234
+ assert_match %r{^http://s3\.amazonaws\.com/testing/avatars/original/5k\.png}, @dummy.avatar.url
235
+ end
236
+
237
+ context "and saved" do
238
+ setup do
239
+ AWS::S3::S3Object.stubs(:store).with(@dummy.avatar.path, anything, 'testing', :content_type => 'image/png', :access => :public_read)
240
+ @dummy.save
241
+ end
242
+
243
+ should "succeed" do
244
+ assert true
245
+ end
246
+ end
247
+
248
+ context "and remove" do
249
+ setup do
250
+ AWS::S3::S3Object.stubs(:exists?).returns(true)
251
+ end
252
+
253
+ should "delete files" do
254
+ AWS::S3::S3Object.expects(:delete)
255
+ @dummy.destroy_attached_files
256
+ end
257
+
258
+ should "not delete files when #delete_files? == false" do
259
+ @dummy.avatar.stubs(:delete_files?).returns(false)
260
+ @dummy.destroy_attached_files
261
+ end
262
+ end
263
+ end
264
+ end
265
+
266
+ context "An attachment with S3 storage and bucket defined as a Proc" do
267
+ setup do
268
+ AWS::S3::Base.stubs(:establish_connection!)
269
+ rebuild_model :storage => :s3,
270
+ :bucket => lambda { |attachment| "bucket_#{attachment.instance.other}" },
271
+ :s3_credentials => {:not => :important}
272
+ end
273
+
274
+ should "get the right bucket name" do
275
+ assert "bucket_a", Dummy.new(:other => 'a').avatar.bucket_name
276
+ assert "bucket_b", Dummy.new(:other => 'b').avatar.bucket_name
277
+ end
278
+ end
279
+
280
+ context "An attachment with S3 storage and specific s3 headers set" do
281
+ setup do
282
+ AWS::S3::Base.stubs(:establish_connection!)
283
+ rebuild_model :storage => :s3,
284
+ :bucket => "testing",
285
+ :path => ":attachment/:style/:basename.:extension",
286
+ :s3_credentials => {
287
+ 'access_key_id' => "12345",
288
+ 'secret_access_key' => "54321"
289
+ },
290
+ :s3_headers => {'Cache-Control' => 'max-age=31557600'}
291
+ end
292
+
293
+ context "when assigned" do
294
+ setup do
295
+ @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
296
+ @dummy = Dummy.new
297
+ @dummy.avatar = @file
298
+ end
299
+
300
+ teardown { @file.close }
301
+
302
+ context "and saved" do
303
+ setup do
304
+ AWS::S3::Base.stubs(:establish_connection!)
305
+ AWS::S3::S3Object.stubs(:store).with(@dummy.avatar.path,
306
+ anything,
307
+ 'testing',
308
+ :content_type => 'image/png',
309
+ :access => :public_read,
310
+ 'Cache-Control' => 'max-age=31557600')
311
+ @dummy.save
312
+ end
313
+
314
+ should "succeed" do
315
+ assert true
316
+ end
317
+ end
318
+ end
319
+ end
320
+
321
+ context "with S3 credentials supplied as Pathname" do
322
+ setup do
323
+ ENV['S3_KEY'] = 'pathname_key'
324
+ ENV['S3_BUCKET'] = 'pathname_bucket'
325
+ ENV['S3_SECRET'] = 'pathname_secret'
326
+
327
+ rails_env('test')
328
+
329
+ rebuild_model :storage => :s3,
330
+ :s3_credentials => Pathname.new(File.join(File.dirname(__FILE__))).join("fixtures/s3.yml")
331
+
332
+ Dummy.delete_all
333
+ @dummy = Dummy.new
334
+ end
335
+
336
+ should "parse the credentials" do
337
+ assert_equal 'pathname_bucket', @dummy.avatar.bucket_name
338
+ assert_equal 'pathname_key', AWS::S3::Base.connection.options[:access_key_id]
339
+ assert_equal 'pathname_secret', AWS::S3::Base.connection.options[:secret_access_key]
340
+ end
341
+ end
342
+
343
+ context "with S3 credentials in a YAML file" do
344
+ setup do
345
+ ENV['S3_KEY'] = 'env_key'
346
+ ENV['S3_BUCKET'] = 'env_bucket'
347
+ ENV['S3_SECRET'] = 'env_secret'
348
+
349
+ rails_env('test')
350
+
351
+ rebuild_model :storage => :s3,
352
+ :s3_credentials => File.new(File.join(File.dirname(__FILE__), "fixtures/s3.yml"))
353
+
354
+ Dummy.delete_all
355
+
356
+ @dummy = Dummy.new
357
+ end
358
+
359
+ should "run it the file through ERB" do
360
+ assert_equal 'env_bucket', @dummy.avatar.bucket_name
361
+ assert_equal 'env_key', AWS::S3::Base.connection.options[:access_key_id]
362
+ assert_equal 'env_secret', AWS::S3::Base.connection.options[:secret_access_key]
363
+ end
364
+ end
365
+
366
+ unless ENV["S3_TEST_BUCKET"].blank?
367
+ context "Using S3 for real, an attachment with S3 storage" do
368
+ setup do
369
+ rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" },
370
+ :storage => :s3,
371
+ :bucket => ENV["S3_TEST_BUCKET"],
372
+ :path => ":class/:attachment/:id/:style.:extension",
373
+ :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml"))
374
+
375
+ Dummy.delete_all
376
+ @dummy = Dummy.new
377
+ end
378
+
379
+ should "be extended by the S3 module" do
380
+ assert Dummy.new.avatar.is_a?(Paperclip::Storage::S3)
381
+ end
382
+
383
+ context "when assigned" do
384
+ setup do
385
+ @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
386
+ @dummy.avatar = @file
387
+ end
388
+
389
+ teardown { @file.close }
390
+
391
+ should "still return a Tempfile when sent #to_file" do
392
+ assert_equal Tempfile, @dummy.avatar.to_file.class
393
+ end
394
+
395
+ context "and saved" do
396
+ setup do
397
+ @dummy.save
398
+ end
399
+
400
+ should "be on S3" do
401
+ assert true
402
+ end
403
+ end
404
+ end
405
+ end
406
+ end
407
+ end
@@ -0,0 +1,141 @@
1
+ # encoding: utf-8
2
+ require 'test/helper'
3
+
4
+ class StyleTest < Test::Unit::TestCase
5
+
6
+ context "A style rule" do
7
+ setup do
8
+ @attachment = attachment :path => ":basename.:extension",
9
+ :styles => { :foo => {:geometry => "100x100#", :format => :png} }
10
+ @style = @attachment.styles[:foo]
11
+ end
12
+
13
+ should "be held as a Style object" do
14
+ assert_kind_of Paperclip::Style, @style
15
+ end
16
+
17
+ should "get processors from the attachment definition" do
18
+ assert_equal [:thumbnail], @style.processors
19
+ end
20
+
21
+ should "have the right geometry" do
22
+ assert_equal "100x100#", @style.geometry
23
+ end
24
+
25
+ should "be whiny if the attachment is" do
26
+ @attachment.expects(:whiny).returns(true)
27
+ assert @style.whiny?
28
+ end
29
+
30
+ should "respond to hash notation" do
31
+ assert_equal [:thumbnail], @style[:processors]
32
+ assert_equal "100x100#", @style[:geometry]
33
+ end
34
+ end
35
+
36
+ context "A style rule with properties supplied as procs" do
37
+ setup do
38
+ @attachment = attachment :path => ":basename.:extension",
39
+ :whiny_thumbnails => true,
40
+ :processors => lambda {|a| [:test]},
41
+ :styles => {
42
+ :foo => lambda{|a| "300x300#"},
43
+ :bar => {
44
+ :geometry => lambda{|a| "300x300#"}
45
+ }
46
+ }
47
+ end
48
+
49
+ should "defer processing of procs until they are needed" do
50
+ assert_kind_of Proc, @attachment.styles[:foo].instance_variable_get("@geometry")
51
+ assert_kind_of Proc, @attachment.styles[:bar].instance_variable_get("@geometry")
52
+ assert_kind_of Proc, @attachment.instance_variable_get("@processors")
53
+ end
54
+
55
+ should "call procs when they are needed" do
56
+ assert_equal "300x300#", @attachment.styles[:foo].geometry
57
+ assert_equal "300x300#", @attachment.styles[:bar].geometry
58
+ assert_equal [:test], @attachment.styles[:foo].processors
59
+ assert_equal [:test], @attachment.styles[:bar].processors
60
+ end
61
+ end
62
+
63
+ context "An attachment with style rules in various forms" do
64
+ setup do
65
+ @attachment = attachment :path => ":basename.:extension",
66
+ :styles => {
67
+ :aslist => ["100x100", :png],
68
+ :ashash => {:geometry => "100x100", :format => :png},
69
+ :asstring => "100x100"
70
+ }
71
+ end
72
+ should "have the right number of styles" do
73
+ assert_kind_of Hash, @attachment.styles
74
+ assert_equal 3, @attachment.styles.size
75
+ end
76
+
77
+ should "have styles as Style objects" do
78
+ [:aslist, :ashash, :aslist].each do |s|
79
+ assert_kind_of Paperclip::Style, @attachment.styles[s]
80
+ end
81
+ end
82
+
83
+ should "have the right geometries" do
84
+ [:aslist, :ashash, :aslist].each do |s|
85
+ assert_equal @attachment.styles[s].geometry, "100x100"
86
+ end
87
+ end
88
+
89
+ should "have the right formats" do
90
+ assert_equal @attachment.styles[:aslist].format, :png
91
+ assert_equal @attachment.styles[:ashash].format, :png
92
+ assert_nil @attachment.styles[:asstring].format
93
+ end
94
+
95
+ end
96
+
97
+ context "An attachment with :convert_options" do
98
+ setup do
99
+ @attachment = attachment :path => ":basename.:extension",
100
+ :styles => {:thumb => "100x100", :large => "400x400"},
101
+ :convert_options => {:all => "-do_stuff", :thumb => "-thumbnailize"}
102
+ @style = @attachment.styles[:thumb]
103
+ @file = StringIO.new("...")
104
+ @file.stubs(:original_filename).returns("file.jpg")
105
+ end
106
+
107
+ before_should "not have called extra_options_for(:thumb/:large) on initialization" do
108
+ @attachment.expects(:extra_options_for).never
109
+ end
110
+
111
+ should "call extra_options_for(:thumb/:large) when convert options are requested" do
112
+ @attachment.expects(:extra_options_for).with(:thumb)
113
+ @attachment.styles[:thumb].convert_options
114
+ end
115
+ end
116
+
117
+ context "A style rule with its own :processors" do
118
+ setup do
119
+ @attachment = attachment :path => ":basename.:extension",
120
+ :styles => {
121
+ :foo => {
122
+ :geometry => "100x100#",
123
+ :format => :png,
124
+ :processors => [:test]
125
+ }
126
+ },
127
+ :processors => [:thumbnail]
128
+ @style = @attachment.styles[:foo]
129
+ end
130
+
131
+ should "not get processors from the attachment" do
132
+ @attachment.expects(:processors).never
133
+ assert_not_equal [:thumbnail], @style.processors
134
+ end
135
+
136
+ should "report its own processors" do
137
+ assert_equal [:test], @style.processors
138
+ end
139
+
140
+ end
141
+ end