joshpuetz-paperclip 2.3.0

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 (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/attachment.rb +414 -0
  9. data/lib/paperclip/callback_compatability.rb +33 -0
  10. data/lib/paperclip/geometry.rb +115 -0
  11. data/lib/paperclip/interpolations.rb +105 -0
  12. data/lib/paperclip/iostream.rb +58 -0
  13. data/lib/paperclip/matchers/have_attached_file_matcher.rb +49 -0
  14. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +66 -0
  15. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +48 -0
  16. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +83 -0
  17. data/lib/paperclip/matchers.rb +4 -0
  18. data/lib/paperclip/processor.rb +49 -0
  19. data/lib/paperclip/storage.rb +236 -0
  20. data/lib/paperclip/thumbnail.rb +70 -0
  21. data/lib/paperclip/upfile.rb +48 -0
  22. data/lib/paperclip.rb +350 -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 +125 -0
@@ -0,0 +1,30 @@
1
+ require 'test/helper'
2
+
3
+ class ValidateAttachmentContentTypeMatcherTest < Test::Unit::TestCase
4
+ context "validate_attachment_content_type" do
5
+ setup do
6
+ reset_table("dummies") do |d|
7
+ d.string :avatar_file_name
8
+ end
9
+ @dummy_class = reset_class "Dummy"
10
+ @dummy_class.has_attached_file :avatar
11
+ @matcher = self.class.validate_attachment_content_type(:avatar).
12
+ allowing(%w(image/png image/jpeg)).
13
+ rejecting(%w(audio/mp3 application/octet-stream))
14
+ end
15
+
16
+ should "reject a class with no validation" do
17
+ assert_rejects @matcher, @dummy_class
18
+ end
19
+
20
+ should "reject a class with a validation that doesn't match" do
21
+ @dummy_class.validates_attachment_content_type :avatar, :content_type => %r{audio/.*}
22
+ assert_rejects @matcher, @dummy_class
23
+ end
24
+
25
+ should "accept a class with a validation" do
26
+ @dummy_class.validates_attachment_content_type :avatar, :content_type => %r{image/.*}
27
+ assert_accepts @matcher, @dummy_class
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ require 'test/helper'
2
+
3
+ class ValidateAttachmentPresenceMatcherTest < Test::Unit::TestCase
4
+ context "validate_attachment_presence" do
5
+ setup do
6
+ reset_table("dummies"){|d| d.string :avatar_file_name }
7
+ @dummy_class = reset_class "Dummy"
8
+ @dummy_class.has_attached_file :avatar
9
+ @matcher = self.class.validate_attachment_presence(:avatar)
10
+ end
11
+
12
+ should "reject a class with no validation" do
13
+ assert_rejects @matcher, @dummy_class
14
+ end
15
+
16
+ should "accept a class with a validation" do
17
+ @dummy_class.validates_attachment_presence :avatar
18
+ assert_accepts @matcher, @dummy_class
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,50 @@
1
+ require 'test/helper'
2
+
3
+ class ValidateAttachmentSizeMatcherTest < Test::Unit::TestCase
4
+ context "validate_attachment_size" do
5
+ setup do
6
+ reset_table("dummies") do |d|
7
+ d.string :avatar_file_name
8
+ end
9
+ @dummy_class = reset_class "Dummy"
10
+ @dummy_class.has_attached_file :avatar
11
+ end
12
+
13
+ context "of limited size" do
14
+ setup{ @matcher = self.class.validate_attachment_size(:avatar).in(256..1024) }
15
+
16
+ should "reject a class with no validation" do
17
+ assert_rejects @matcher, @dummy_class
18
+ end
19
+
20
+ should "reject a class with a validation that's too high" do
21
+ @dummy_class.validates_attachment_size :avatar, :in => 256..2048
22
+ assert_rejects @matcher, @dummy_class
23
+ end
24
+
25
+ should "reject a class with a validation that's too low" do
26
+ @dummy_class.validates_attachment_size :avatar, :in => 0..1024
27
+ assert_rejects @matcher, @dummy_class
28
+ end
29
+
30
+ should "accept a class with a validation that matches" do
31
+ @dummy_class.validates_attachment_size :avatar, :in => 256..1024
32
+ assert_accepts @matcher, @dummy_class
33
+ end
34
+ end
35
+
36
+ context "validates_attachment_size with infinite range" do
37
+ setup{ @matcher = self.class.validate_attachment_size(:avatar) }
38
+
39
+ should "accept a class with an upper limit" do
40
+ @dummy_class.validates_attachment_size :avatar, :less_than => 1
41
+ assert_accepts @matcher, @dummy_class
42
+ end
43
+
44
+ should "accept a class with no upper limit" do
45
+ @dummy_class.validates_attachment_size :avatar, :greater_than => 1
46
+ assert_accepts @matcher, @dummy_class
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,291 @@
1
+ require 'test/helper'
2
+
3
+ class PaperclipTest < Test::Unit::TestCase
4
+ [:image_magick_path, :convert_path].each do |path|
5
+ context "Calling Paperclip.run with an #{path} specified" do
6
+ setup do
7
+ Paperclip.options[:image_magick_path] = nil
8
+ Paperclip.options[:convert_path] = nil
9
+ Paperclip.options[path] = "/usr/bin"
10
+ end
11
+
12
+ should "execute the right command" do
13
+ Paperclip.expects(:path_for_command).with("convert").returns("/usr/bin/convert")
14
+ Paperclip.expects(:bit_bucket).returns("/dev/null")
15
+ Paperclip.expects(:"`").with("/usr/bin/convert one.jpg two.jpg 2>/dev/null")
16
+ Paperclip.run("convert", "one.jpg two.jpg")
17
+ end
18
+ end
19
+ end
20
+
21
+ context "Calling Paperclip.run with no path specified" do
22
+ setup do
23
+ Paperclip.options[:image_magick_path] = nil
24
+ Paperclip.options[:convert_path] = nil
25
+ end
26
+
27
+ should "execute the right command" do
28
+ Paperclip.expects(:path_for_command).with("convert").returns("convert")
29
+ Paperclip.expects(:bit_bucket).returns("/dev/null")
30
+ Paperclip.expects(:"`").with("convert one.jpg two.jpg 2>/dev/null")
31
+ Paperclip.run("convert", "one.jpg two.jpg")
32
+ end
33
+
34
+ should "log the command when :log_command is set" do
35
+ Paperclip.options[:log_command] = true
36
+ Paperclip.expects(:bit_bucket).returns("/dev/null")
37
+ Paperclip.expects(:log).with("this is the command 2>/dev/null")
38
+ Paperclip.expects(:"`").with("this is the command 2>/dev/null")
39
+ Paperclip.run("this","is the command")
40
+ end
41
+ end
42
+
43
+ should "raise when sent #processor and the name of a class that exists but isn't a subclass of Processor" do
44
+ assert_raises(Paperclip::PaperclipError){ Paperclip.processor(:attachment) }
45
+ end
46
+
47
+ should "raise when sent #processor and the name of a class that doesn't exist" do
48
+ assert_raises(NameError){ Paperclip.processor(:boogey_man) }
49
+ end
50
+
51
+ should "return a class when sent #processor and the name of a class under Paperclip" do
52
+ assert_equal ::Paperclip::Thumbnail, Paperclip.processor(:thumbnail)
53
+ end
54
+
55
+ should "call a proc sent to check_guard" do
56
+ @dummy = Dummy.new
57
+ @dummy.expects(:one).returns(:one)
58
+ assert_equal :one, @dummy.avatar.send(:check_guard, lambda{|x| x.one })
59
+ end
60
+
61
+ should "call a method name sent to check_guard" do
62
+ @dummy = Dummy.new
63
+ @dummy.expects(:one).returns(:one)
64
+ assert_equal :one, @dummy.avatar.send(:check_guard, :one)
65
+ end
66
+
67
+ context "Paperclip.bit_bucket" do
68
+ context "on systems without /dev/null" do
69
+ setup do
70
+ File.expects(:exists?).with("/dev/null").returns(false)
71
+ end
72
+
73
+ should "return 'NUL'" do
74
+ assert_equal "NUL", Paperclip.bit_bucket
75
+ end
76
+ end
77
+
78
+ context "on systems with /dev/null" do
79
+ setup do
80
+ File.expects(:exists?).with("/dev/null").returns(true)
81
+ end
82
+
83
+ should "return '/dev/null'" do
84
+ assert_equal "/dev/null", Paperclip.bit_bucket
85
+ end
86
+ end
87
+ end
88
+
89
+ context "An ActiveRecord model with an 'avatar' attachment" do
90
+ setup do
91
+ rebuild_model :path => "tmp/:class/omg/:style.:extension"
92
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
93
+ end
94
+
95
+ teardown { @file.close }
96
+
97
+ should "not error when trying to also create a 'blah' attachment" do
98
+ assert_nothing_raised do
99
+ Dummy.class_eval do
100
+ has_attached_file :blah
101
+ end
102
+ end
103
+ end
104
+
105
+ context "that is attr_protected" do
106
+ setup do
107
+ Dummy.class_eval do
108
+ attr_protected :avatar
109
+ end
110
+ @dummy = Dummy.new
111
+ end
112
+
113
+ should "not assign the avatar on mass-set" do
114
+ @dummy.attributes = { :other => "I'm set!",
115
+ :avatar => @file }
116
+
117
+ assert_equal "I'm set!", @dummy.other
118
+ assert ! @dummy.avatar?
119
+ end
120
+
121
+ should "still allow assigment on normal set" do
122
+ @dummy.other = "I'm set!"
123
+ @dummy.avatar = @file
124
+
125
+ assert_equal "I'm set!", @dummy.other
126
+ assert @dummy.avatar?
127
+ end
128
+ end
129
+
130
+ context "with a subclass" do
131
+ setup do
132
+ class ::SubDummy < Dummy; end
133
+ end
134
+
135
+ should "be able to use the attachment from the subclass" do
136
+ assert_nothing_raised do
137
+ @subdummy = SubDummy.create(:avatar => @file)
138
+ end
139
+ end
140
+
141
+ should "be able to see the attachment definition from the subclass's class" do
142
+ assert_equal "tmp/:class/omg/:style.:extension", SubDummy.attachment_definitions[:avatar][:path]
143
+ end
144
+
145
+ teardown do
146
+ Object.send(:remove_const, "SubDummy") rescue nil
147
+ end
148
+ end
149
+
150
+ should "have an #avatar method" do
151
+ assert Dummy.new.respond_to?(:avatar)
152
+ end
153
+
154
+ should "have an #avatar= method" do
155
+ assert Dummy.new.respond_to?(:avatar=)
156
+ end
157
+
158
+ context "that is valid" do
159
+ setup do
160
+ @dummy = Dummy.new
161
+ @dummy.avatar = @file
162
+ end
163
+
164
+ should "be valid" do
165
+ assert @dummy.valid?
166
+ end
167
+
168
+ context "then has a validation added that makes it invalid" do
169
+ setup do
170
+ assert @dummy.save
171
+ Dummy.class_eval do
172
+ validates_attachment_content_type :avatar, :content_type => ["text/plain"]
173
+ end
174
+ @dummy2 = Dummy.find(@dummy.id)
175
+ end
176
+
177
+ should "be invalid when reloaded" do
178
+ assert ! @dummy2.valid?, @dummy2.errors.inspect
179
+ end
180
+
181
+ should "be able to call #valid? twice without having duplicate errors" do
182
+ @dummy2.avatar.valid?
183
+ first_errors = @dummy2.avatar.errors
184
+ @dummy2.avatar.valid?
185
+ assert_equal first_errors, @dummy2.avatar.errors
186
+ end
187
+ end
188
+ end
189
+
190
+ context "a validation with an if guard clause" do
191
+ setup do
192
+ Dummy.send(:"validates_attachment_presence", :avatar, :if => lambda{|i| i.foo })
193
+ @dummy = Dummy.new
194
+ end
195
+
196
+ should "attempt validation if the guard returns true" do
197
+ @dummy.expects(:foo).returns(true)
198
+ @dummy.avatar.expects(:validate_presence).returns(nil)
199
+ @dummy.valid?
200
+ end
201
+
202
+ should "not attempt validation if the guard returns false" do
203
+ @dummy.expects(:foo).returns(false)
204
+ @dummy.avatar.expects(:validate_presence).never
205
+ @dummy.valid?
206
+ end
207
+ end
208
+
209
+ context "a validation with an unless guard clause" do
210
+ setup do
211
+ Dummy.send(:"validates_attachment_presence", :avatar, :unless => lambda{|i| i.foo })
212
+ @dummy = Dummy.new
213
+ end
214
+
215
+ should "attempt validation if the guard returns true" do
216
+ @dummy.expects(:foo).returns(false)
217
+ @dummy.avatar.expects(:validate_presence).returns(nil)
218
+ @dummy.valid?
219
+ end
220
+
221
+ should "not attempt validation if the guard returns false" do
222
+ @dummy.expects(:foo).returns(true)
223
+ @dummy.avatar.expects(:validate_presence).never
224
+ @dummy.valid?
225
+ end
226
+ end
227
+
228
+ def self.should_validate validation, options, valid_file, invalid_file
229
+ context "with #{validation} validation and #{options.inspect} options" do
230
+ setup do
231
+ Dummy.send(:"validates_attachment_#{validation}", :avatar, options)
232
+ @dummy = Dummy.new
233
+ end
234
+ context "and assigning nil" do
235
+ setup do
236
+ @dummy.avatar = nil
237
+ @dummy.valid?
238
+ end
239
+ if validation == :presence
240
+ should "have an error on the attachment" do
241
+ assert @dummy.errors.on(:avatar)
242
+ end
243
+ else
244
+ should "not have an error on the attachment" do
245
+ assert_nil @dummy.errors.on(:avatar)
246
+ end
247
+ end
248
+ end
249
+ context "and assigned a valid file" do
250
+ setup do
251
+ @dummy.avatar = valid_file
252
+ @dummy.valid?
253
+ end
254
+ should "not have an error when assigned a valid file" do
255
+ assert ! @dummy.avatar.errors.key?(validation)
256
+ end
257
+ should "not have an error on the attachment" do
258
+ assert_nil @dummy.errors.on(:avatar)
259
+ end
260
+ end
261
+ context "and assigned an invalid file" do
262
+ setup do
263
+ @dummy.avatar = invalid_file
264
+ @dummy.valid?
265
+ end
266
+ should "have an error when assigned a valid file" do
267
+ assert_not_nil @dummy.avatar.errors[validation]
268
+ end
269
+ should "have an error on the attachment" do
270
+ assert @dummy.errors.on(:avatar)
271
+ end
272
+ end
273
+ end
274
+ end
275
+
276
+ [[:presence, {}, "5k.png", nil],
277
+ [:size, {:in => 1..10240}, nil, "12k.png"],
278
+ [:size, {:less_than => 10240}, "5k.png", "12k.png"],
279
+ [:size, {:greater_than => 8096}, "12k.png", "5k.png"],
280
+ [:content_type, {:content_type => "image/png"}, "5k.png", "text.txt"],
281
+ [:content_type, {:content_type => "text/plain"}, "text.txt", "5k.png"],
282
+ [:content_type, {:content_type => %r{image/.*}}, "5k.png", "text.txt"]].each do |args|
283
+ validation, options, valid_file, invalid_file = args
284
+ valid_file &&= File.open(File.join(FIXTURES_DIR, valid_file), "rb")
285
+ invalid_file &&= File.open(File.join(FIXTURES_DIR, invalid_file), "rb")
286
+
287
+ should_validate validation, options, valid_file, invalid_file
288
+ end
289
+
290
+ end
291
+ end
@@ -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,282 @@
1
+ require 'test/helper'
2
+
3
+ class StorageTest < Test::Unit::TestCase
4
+ context "Parsing S3 credentials" do
5
+ setup do
6
+ rebuild_model :storage => :s3,
7
+ :bucket => "testing",
8
+ :s3_credentials => {:not => :important}
9
+
10
+ @dummy = Dummy.new
11
+ @avatar = @dummy.avatar
12
+
13
+ @current_env = RAILS_ENV
14
+ end
15
+
16
+ teardown do
17
+ Object.const_set("RAILS_ENV", @current_env)
18
+ end
19
+
20
+ should "get the correct credentials when RAILS_ENV is production" do
21
+ Object.const_set('RAILS_ENV', "production")
22
+ assert_equal({:key => "12345"},
23
+ @avatar.parse_credentials('production' => {:key => '12345'},
24
+ :development => {:key => "54321"}))
25
+ end
26
+
27
+ should "get the correct credentials when RAILS_ENV is development" do
28
+ Object.const_set('RAILS_ENV', "development")
29
+ assert_equal({:key => "54321"},
30
+ @avatar.parse_credentials('production' => {:key => '12345'},
31
+ :development => {:key => "54321"}))
32
+ end
33
+
34
+ should "return the argument if the key does not exist" do
35
+ Object.const_set('RAILS_ENV', "not really an env")
36
+ assert_equal({:test => "12345"}, @avatar.parse_credentials(:test => "12345"))
37
+ end
38
+ end
39
+
40
+ context "" do
41
+ setup do
42
+ rebuild_model :storage => :s3,
43
+ :s3_credentials => {},
44
+ :bucket => "bucket",
45
+ :path => ":attachment/:basename.:extension",
46
+ :url => ":s3_path_url"
47
+ @dummy = Dummy.new
48
+ @dummy.avatar = StringIO.new(".")
49
+ end
50
+
51
+ should "return a url based on an S3 path" do
52
+ assert_match %r{^http://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url
53
+ end
54
+ end
55
+ context "" do
56
+ setup do
57
+ rebuild_model :storage => :s3,
58
+ :s3_credentials => {},
59
+ :bucket => "bucket",
60
+ :path => ":attachment/:basename.:extension",
61
+ :url => ":s3_domain_url"
62
+ @dummy = Dummy.new
63
+ @dummy.avatar = StringIO.new(".")
64
+ end
65
+
66
+ should "return a url based on an S3 subdomain" do
67
+ assert_match %r{^http://bucket.s3.amazonaws.com/avatars/stringio.txt}, @dummy.avatar.url
68
+ end
69
+ end
70
+ context "" do
71
+ setup do
72
+ rebuild_model :storage => :s3,
73
+ :s3_credentials => {
74
+ :production => { :bucket => "prod_bucket" },
75
+ :development => { :bucket => "dev_bucket" }
76
+ },
77
+ :s3_host_alias => "something.something.com",
78
+ :path => ":attachment/:basename.:extension",
79
+ :url => ":s3_alias_url"
80
+ @dummy = Dummy.new
81
+ @dummy.avatar = StringIO.new(".")
82
+ end
83
+
84
+ should "return a url based on the host_alias" do
85
+ assert_match %r{^http://something.something.com/avatars/stringio.txt}, @dummy.avatar.url
86
+ end
87
+ end
88
+
89
+ context "Parsing S3 credentials with a bucket in them" do
90
+ setup do
91
+ rebuild_model :storage => :s3,
92
+ :s3_credentials => {
93
+ :production => { :bucket => "prod_bucket" },
94
+ :development => { :bucket => "dev_bucket" }
95
+ }
96
+ @dummy = Dummy.new
97
+ @old_env = RAILS_ENV
98
+ end
99
+
100
+ teardown{ Object.const_set("RAILS_ENV", @old_env) }
101
+
102
+ should "get the right bucket in production" do
103
+ Object.const_set("RAILS_ENV", "production")
104
+ assert_equal "prod_bucket", @dummy.avatar.bucket_name
105
+ end
106
+
107
+ should "get the right bucket in development" do
108
+ Object.const_set("RAILS_ENV", "development")
109
+ assert_equal "dev_bucket", @dummy.avatar.bucket_name
110
+ end
111
+ end
112
+
113
+ context "An attachment with S3 storage" do
114
+ setup do
115
+ rebuild_model :storage => :s3,
116
+ :bucket => "testing",
117
+ :path => ":attachment/:style/:basename.:extension",
118
+ :s3_credentials => {
119
+ 'access_key_id' => "12345",
120
+ 'secret_access_key' => "54321"
121
+ }
122
+ end
123
+
124
+ should "be extended by the S3 module" do
125
+ assert Dummy.new.avatar.is_a?(Paperclip::Storage::S3)
126
+ end
127
+
128
+ should "not be extended by the Filesystem module" do
129
+ assert ! Dummy.new.avatar.is_a?(Paperclip::Storage::Filesystem)
130
+ end
131
+
132
+ context "when assigned" do
133
+ setup do
134
+ @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
135
+ @dummy = Dummy.new
136
+ @dummy.avatar = @file
137
+ end
138
+
139
+ teardown { @file.close }
140
+
141
+ should "not get a bucket to get a URL" do
142
+ @dummy.avatar.expects(:s3).never
143
+ @dummy.avatar.expects(:s3_bucket).never
144
+ assert_match %r{^http://s3\.amazonaws\.com/testing/avatars/original/5k\.png}, @dummy.avatar.url
145
+ end
146
+
147
+ context "and saved" do
148
+ setup do
149
+ @s3_mock = stub
150
+ @bucket_mock = stub
151
+ RightAws::S3.expects(:new).with("12345", "54321", {}).returns(@s3_mock)
152
+ @s3_mock.expects(:bucket).with("testing", true, "public-read").returns(@bucket_mock)
153
+ @key_mock = stub
154
+ @bucket_mock.expects(:key).returns(@key_mock)
155
+ @key_mock.expects(:data=)
156
+ @key_mock.expects(:put).with(nil, 'public-read', 'Content-type' => 'image/png')
157
+ @dummy.save
158
+ end
159
+
160
+ should "succeed" do
161
+ assert true
162
+ end
163
+ end
164
+
165
+ context "and remove" do
166
+ setup do
167
+ @s3_mock = stub
168
+ @bucket_mock = stub
169
+ RightAws::S3.expects(:new).with("12345", "54321", {}).returns(@s3_mock)
170
+ @s3_mock.expects(:bucket).with("testing", true, "public-read").returns(@bucket_mock)
171
+ @key_mock = stub
172
+ @bucket_mock.expects(:key).at_least(2).returns(@key_mock)
173
+ @key_mock.expects(:delete)
174
+ @dummy.destroy_attached_files
175
+ end
176
+
177
+ should "succeed" do
178
+ assert true
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ context "An attachment with S3 storage and bucket defined as a Proc" do
185
+ setup do
186
+ rebuild_model :storage => :s3,
187
+ :bucket => lambda { |attachment| "bucket_#{attachment.instance.other}" },
188
+ :s3_credentials => {:not => :important}
189
+ end
190
+
191
+ should "get the right bucket name" do
192
+ assert "bucket_a", Dummy.new(:other => 'a').avatar.bucket_name
193
+ assert "bucket_b", Dummy.new(:other => 'b').avatar.bucket_name
194
+ end
195
+ end
196
+
197
+ context "An attachment with S3 storage and specific s3 headers set" do
198
+ setup do
199
+ rebuild_model :storage => :s3,
200
+ :bucket => "testing",
201
+ :path => ":attachment/:style/:basename.:extension",
202
+ :s3_credentials => {
203
+ 'access_key_id' => "12345",
204
+ 'secret_access_key' => "54321"
205
+ },
206
+ :s3_headers => {'Cache-Control' => 'max-age=31557600'}
207
+ end
208
+
209
+ context "when assigned" do
210
+ setup do
211
+ @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
212
+ @dummy = Dummy.new
213
+ @dummy.avatar = @file
214
+ end
215
+
216
+ teardown { @file.close }
217
+
218
+ context "and saved" do
219
+ setup do
220
+ @s3_mock = stub
221
+ @bucket_mock = stub
222
+ RightAws::S3.expects(:new).with("12345", "54321", {}).returns(@s3_mock)
223
+ @s3_mock.expects(:bucket).with("testing", true, "public-read").returns(@bucket_mock)
224
+ @key_mock = stub
225
+ @bucket_mock.expects(:key).returns(@key_mock)
226
+ @key_mock.expects(:data=)
227
+ @key_mock.expects(:put).with(nil,
228
+ 'public-read',
229
+ 'Content-type' => 'image/png',
230
+ 'Cache-Control' => 'max-age=31557600')
231
+ @dummy.save
232
+ end
233
+
234
+ should "succeed" do
235
+ assert true
236
+ end
237
+ end
238
+ end
239
+ end
240
+
241
+ unless ENV["S3_TEST_BUCKET"].blank?
242
+ context "Using S3 for real, an attachment with S3 storage" do
243
+ setup do
244
+ rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" },
245
+ :storage => :s3,
246
+ :bucket => ENV["S3_TEST_BUCKET"],
247
+ :path => ":class/:attachment/:id/:style.:extension",
248
+ :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml"))
249
+
250
+ Dummy.delete_all
251
+ @dummy = Dummy.new
252
+ end
253
+
254
+ should "be extended by the S3 module" do
255
+ assert Dummy.new.avatar.is_a?(Paperclip::Storage::S3)
256
+ end
257
+
258
+ context "when assigned" do
259
+ setup do
260
+ @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
261
+ @dummy.avatar = @file
262
+ end
263
+
264
+ teardown { @file.close }
265
+
266
+ should "still return a Tempfile when sent #to_io" do
267
+ assert_equal Tempfile, @dummy.avatar.to_io.class
268
+ end
269
+
270
+ context "and saved" do
271
+ setup do
272
+ @dummy.save
273
+ end
274
+
275
+ should "be on S3" do
276
+ assert true
277
+ end
278
+ end
279
+ end
280
+ end
281
+ end
282
+ end