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.
- data/LICENSE +26 -0
- data/README.rdoc +174 -0
- data/Rakefile +99 -0
- data/generators/paperclip/USAGE +5 -0
- data/generators/paperclip/paperclip_generator.rb +27 -0
- data/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
- data/init.rb +1 -0
- data/lib/paperclip/attachment.rb +414 -0
- data/lib/paperclip/callback_compatability.rb +33 -0
- data/lib/paperclip/geometry.rb +115 -0
- data/lib/paperclip/interpolations.rb +105 -0
- data/lib/paperclip/iostream.rb +58 -0
- data/lib/paperclip/matchers/have_attached_file_matcher.rb +49 -0
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +66 -0
- data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +48 -0
- data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +83 -0
- data/lib/paperclip/matchers.rb +4 -0
- data/lib/paperclip/processor.rb +49 -0
- data/lib/paperclip/storage.rb +236 -0
- data/lib/paperclip/thumbnail.rb +70 -0
- data/lib/paperclip/upfile.rb +48 -0
- data/lib/paperclip.rb +350 -0
- data/shoulda_macros/paperclip.rb +68 -0
- data/tasks/paperclip_tasks.rake +79 -0
- data/test/attachment_test.rb +767 -0
- data/test/database.yml +4 -0
- data/test/fixtures/12k.png +0 -0
- data/test/fixtures/50x50.png +0 -0
- data/test/fixtures/5k.png +0 -0
- data/test/fixtures/bad.png +1 -0
- data/test/fixtures/s3.yml +4 -0
- data/test/fixtures/text.txt +0 -0
- data/test/fixtures/twopage.pdf +0 -0
- data/test/geometry_test.rb +177 -0
- data/test/helper.rb +99 -0
- data/test/integration_test.rb +481 -0
- data/test/interpolations_test.rb +120 -0
- data/test/iostream_test.rb +71 -0
- data/test/matchers/have_attached_file_matcher_test.rb +21 -0
- data/test/matchers/validate_attachment_content_type_matcher_test.rb +30 -0
- data/test/matchers/validate_attachment_presence_matcher_test.rb +21 -0
- data/test/matchers/validate_attachment_size_matcher_test.rb +50 -0
- data/test/paperclip_test.rb +291 -0
- data/test/processor_test.rb +10 -0
- data/test/storage_test.rb +282 -0
- data/test/thumbnail_test.rb +177 -0
- 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
|