dm-paperclip 2.3.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/dm-paperclip.rb +2 -1
- data/lib/dm-paperclip/attachment.rb +1 -1
- data/lib/dm-paperclip/iostream.rb +4 -3
- data/lib/dm-paperclip/storage.rb +72 -55
- data/test/attachment_test.rb +12 -6
- data/test/helper.rb +6 -5
- data/test/iostream_test.rb +42 -24
- data/test/paperclip_test.rb +1 -1
- data/test/storage_test.rb +191 -40
- metadata +67 -7
data/lib/dm-paperclip.rb
CHANGED
@@ -25,6 +25,7 @@
|
|
25
25
|
#
|
26
26
|
# See the +has_attached_file+ documentation for more details.
|
27
27
|
|
28
|
+
require 'erb'
|
28
29
|
require 'tempfile'
|
29
30
|
|
30
31
|
require 'dm-core'
|
@@ -42,7 +43,7 @@ require 'dm-paperclip/attachment'
|
|
42
43
|
# documentation for Paperclip::ClassMethods for more useful information.
|
43
44
|
module Paperclip
|
44
45
|
|
45
|
-
VERSION = "2.
|
46
|
+
VERSION = "2.4.0"
|
46
47
|
|
47
48
|
# To configure Paperclip, put this code in an initializer, Rake task, or wherever:
|
48
49
|
#
|
@@ -274,7 +274,7 @@ module Paperclip
|
|
274
274
|
end
|
275
275
|
|
276
276
|
def valid_assignment? file #:nodoc:
|
277
|
-
if file.
|
277
|
+
if file.is_a?(Hash) || (defined?(Mash) && file.is_a?(Mash))
|
278
278
|
file[:filename] || file['filename']
|
279
279
|
else
|
280
280
|
file.nil? || (file.respond_to?(:original_filename) && file.respond_to?(:content_type))
|
@@ -4,7 +4,8 @@ module IOStream
|
|
4
4
|
|
5
5
|
# Returns a Tempfile containing the contents of the readable object.
|
6
6
|
def to_tempfile
|
7
|
-
|
7
|
+
name = respond_to?(:original_filename) ? original_filename : (respond_to?(:path) ? path : "stream")
|
8
|
+
tempfile = Paperclip::Tempfile.new(File.basename(name))
|
8
9
|
tempfile.binmode
|
9
10
|
self.stream_to(tempfile)
|
10
11
|
end
|
@@ -25,7 +26,7 @@ module IOStream
|
|
25
26
|
while self.read(in_blocks_of, buffer) do
|
26
27
|
dstio.write(buffer)
|
27
28
|
end
|
28
|
-
dstio.rewind
|
29
|
+
dstio.rewind
|
29
30
|
dstio
|
30
31
|
end
|
31
32
|
end
|
@@ -55,4 +56,4 @@ if defined? Tempfile
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
end
|
58
|
-
end
|
59
|
+
end
|
data/lib/dm-paperclip/storage.rb
CHANGED
@@ -8,21 +8,21 @@ module Paperclip
|
|
8
8
|
# * +path+: The location of the repository of attachments on disk. This can (and, in
|
9
9
|
# almost all cases, should) be coordinated with the value of the +url+ option to
|
10
10
|
# allow files to be saved into a place where Apache can serve them without
|
11
|
-
# hitting your app. Defaults to
|
11
|
+
# hitting your app. Defaults to
|
12
12
|
# ":rails_root/public/:attachment/:id/:style/:basename.:extension"
|
13
|
-
# By default this places the files in the app's public directory which can be served
|
14
|
-
# directly. If you are using capistrano for deployment, a good idea would be to
|
15
|
-
# make a symlink to the capistrano-created system directory from inside your app's
|
13
|
+
# By default this places the files in the app's public directory which can be served
|
14
|
+
# directly. If you are using capistrano for deployment, a good idea would be to
|
15
|
+
# make a symlink to the capistrano-created system directory from inside your app's
|
16
16
|
# public directory.
|
17
17
|
# See Paperclip::Attachment#interpolate for more information on variable interpolaton.
|
18
18
|
# :path => "/var/app/attachments/:class/:id/:style/:basename.:extension"
|
19
19
|
module Filesystem
|
20
20
|
def self.extended base
|
21
21
|
end
|
22
|
-
|
23
|
-
def exists?(
|
22
|
+
|
23
|
+
def exists?(style_name = default_style)
|
24
24
|
if original_filename
|
25
|
-
File.exist?(path(
|
25
|
+
File.exist?(path(style_name))
|
26
26
|
else
|
27
27
|
false
|
28
28
|
end
|
@@ -30,18 +30,17 @@ module Paperclip
|
|
30
30
|
|
31
31
|
# Returns representation of the data of the file assigned to the given
|
32
32
|
# style, in the format most representative of the current storage.
|
33
|
-
def to_file
|
34
|
-
@queued_for_write[
|
33
|
+
def to_file style_name = default_style
|
34
|
+
@queued_for_write[style_name] || (File.new(path(style_name), 'rb') if exists?(style_name))
|
35
35
|
end
|
36
|
-
alias_method :to_io, :to_file
|
37
36
|
|
38
37
|
def flush_writes #:nodoc:
|
39
|
-
@queued_for_write.each do |
|
38
|
+
@queued_for_write.each do |style_name, file|
|
40
39
|
file.close
|
41
|
-
FileUtils.mkdir_p(File.dirname(path(
|
42
|
-
log("saving #{path(
|
43
|
-
FileUtils.mv(file.path, path(
|
44
|
-
FileUtils.chmod(0644, path(
|
40
|
+
FileUtils.mkdir_p(File.dirname(path(style_name)))
|
41
|
+
log("saving #{path(style_name)}")
|
42
|
+
FileUtils.mv(file.path, path(style_name))
|
43
|
+
FileUtils.chmod(0644, path(style_name))
|
45
44
|
end
|
46
45
|
@queued_for_write = {}
|
47
46
|
end
|
@@ -79,26 +78,26 @@ module Paperclip
|
|
79
78
|
# database.yml file, so different environments can use different accounts:
|
80
79
|
# development:
|
81
80
|
# access_key_id: 123...
|
82
|
-
# secret_access_key: 123...
|
81
|
+
# secret_access_key: 123...
|
83
82
|
# test:
|
84
83
|
# access_key_id: abc...
|
85
|
-
# secret_access_key: abc...
|
84
|
+
# secret_access_key: abc...
|
86
85
|
# production:
|
87
86
|
# access_key_id: 456...
|
88
|
-
# secret_access_key: 456...
|
87
|
+
# secret_access_key: 456...
|
89
88
|
# This is not required, however, and the file may simply look like this:
|
90
89
|
# access_key_id: 456...
|
91
|
-
# secret_access_key: 456...
|
90
|
+
# secret_access_key: 456...
|
92
91
|
# In which case, those access keys will be used in all environments. You can also
|
93
92
|
# put your bucket name in this file, instead of adding it to the code directly.
|
94
|
-
# This is useful when you want the same account but a different bucket for
|
93
|
+
# This is useful when you want the same account but a different bucket for
|
95
94
|
# development versus production.
|
96
95
|
# * +s3_permissions+: This is a String that should be one of the "canned" access
|
97
96
|
# policies that S3 provides (more information can be found here:
|
98
97
|
# http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html#RESTCannedAccessPolicies)
|
99
|
-
# The default for Paperclip is
|
100
|
-
# * +s3_protocol+: The protocol for the URLs generated to your S3 assets. Can be either
|
101
|
-
# 'http' or 'https'. Defaults to 'http' when your :s3_permissions are
|
98
|
+
# The default for Paperclip is :public_read.
|
99
|
+
# * +s3_protocol+: The protocol for the URLs generated to your S3 assets. Can be either
|
100
|
+
# 'http' or 'https'. Defaults to 'http' when your :s3_permissions are :public_read (the
|
102
101
|
# default), and 'https' when your :s3_permissions are anything else.
|
103
102
|
# * +s3_headers+: A hash of headers such as {'Expires' => 1.year.from_now.httpdate}
|
104
103
|
# * +bucket+: This is the name of the S3 bucket that will store your files. Remember
|
@@ -112,7 +111,7 @@ module Paperclip
|
|
112
111
|
# * +url+: There are three options for the S3 url. You can choose to have the bucket's name
|
113
112
|
# placed domain-style (bucket.s3.amazonaws.com) or path-style (s3.amazonaws.com/bucket).
|
114
113
|
# Lastly, you can specify a CNAME (which requires the CNAME to be specified as
|
115
|
-
# :s3_alias_url. You can read more about CNAMEs and S3 at
|
114
|
+
# :s3_alias_url. You can read more about CNAMEs and S3 at
|
116
115
|
# http://docs.amazonwebservices.com/AmazonS3/latest/index.html?VirtualHosting.html
|
117
116
|
# Normally, this won't matter in the slightest and you can leave the default (which is
|
118
117
|
# path-style, or :s3_path_url). But in some cases paths don't work and you need to use
|
@@ -128,17 +127,27 @@ module Paperclip
|
|
128
127
|
# separate parts of your file name.
|
129
128
|
module S3
|
130
129
|
def self.extended base
|
131
|
-
|
130
|
+
begin
|
131
|
+
require 'aws/s3'
|
132
|
+
rescue LoadError => e
|
133
|
+
e.message << " (You may need to install the aws-s3 gem)"
|
134
|
+
raise e
|
135
|
+
end
|
136
|
+
|
132
137
|
base.instance_eval do
|
133
138
|
@s3_credentials = parse_credentials(@options[:s3_credentials])
|
134
139
|
@bucket = @options[:bucket] || @s3_credentials[:bucket]
|
135
140
|
@bucket = @bucket.call(self) if @bucket.is_a?(Proc)
|
136
141
|
@s3_options = @options[:s3_options] || {}
|
137
|
-
@s3_permissions = @options[:s3_permissions] ||
|
138
|
-
@s3_protocol = @options[:s3_protocol] || (@s3_permissions ==
|
142
|
+
@s3_permissions = @options[:s3_permissions] || :public_read
|
143
|
+
@s3_protocol = @options[:s3_protocol] || (@s3_permissions == :public_read ? 'http' : 'https')
|
139
144
|
@s3_headers = @options[:s3_headers] || {}
|
140
145
|
@s3_host_alias = @options[:s3_host_alias]
|
141
146
|
@url = ":s3_path_url" unless @url.to_s.match(/^:s3.*url$/)
|
147
|
+
AWS::S3::Base.establish_connection!( @s3_options.merge(
|
148
|
+
:access_key_id => @s3_credentials[:access_key_id],
|
149
|
+
:secret_access_key => @s3_credentials[:secret_access_key]
|
150
|
+
))
|
142
151
|
end
|
143
152
|
Paperclip.interpolates(:s3_alias_url) do |attachment, style|
|
144
153
|
"#{attachment.s3_protocol}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
|
@@ -150,15 +159,9 @@ module Paperclip
|
|
150
159
|
"#{attachment.s3_protocol}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, "")}"
|
151
160
|
end
|
152
161
|
end
|
153
|
-
|
154
|
-
def
|
155
|
-
|
156
|
-
@s3_credentials[:secret_access_key],
|
157
|
-
@s3_options)
|
158
|
-
end
|
159
|
-
|
160
|
-
def s3_bucket
|
161
|
-
@s3_bucket ||= s3.bucket(@bucket, true, @s3_permissions)
|
162
|
+
|
163
|
+
def expiring_url(time = 3600)
|
164
|
+
AWS::S3::S3Object.url_for(path, bucket_name, :expires_in => time )
|
162
165
|
end
|
163
166
|
|
164
167
|
def bucket_name
|
@@ -170,16 +173,26 @@ module Paperclip
|
|
170
173
|
end
|
171
174
|
|
172
175
|
def parse_credentials creds
|
173
|
-
creds = find_credentials(creds).to_mash
|
176
|
+
creds = find_credentials(creds).to_mash.stringify_keys!
|
174
177
|
if defined? Merb && Merb.respond_to?(:env)
|
175
|
-
(creds[Merb.env] || creds)
|
178
|
+
(creds[Merb.env] || creds).symbolize_keys
|
179
|
+
elsif defined? RAILS_ENV
|
180
|
+
(creds[RAILS_ENV] || creds).symbolize_keys
|
181
|
+
elsif defined? Rails && Rails.respond_to(:env)
|
182
|
+
(creds[Rails.env] || creds).symbolize_keys
|
183
|
+
elsif defined? RACK_ENV
|
184
|
+
(creds[RACK_ENV] || creds).symbolize_keys
|
176
185
|
else
|
177
|
-
|
186
|
+
creds.symbolize_keys
|
178
187
|
end
|
179
188
|
end
|
180
|
-
|
189
|
+
|
181
190
|
def exists?(style = default_style)
|
182
|
-
|
191
|
+
if original_filename
|
192
|
+
AWS::S3::S3Object.exists?(path(style), bucket_name)
|
193
|
+
else
|
194
|
+
false
|
195
|
+
end
|
183
196
|
end
|
184
197
|
|
185
198
|
def s3_protocol
|
@@ -189,18 +202,24 @@ module Paperclip
|
|
189
202
|
# Returns representation of the data of the file assigned to the given
|
190
203
|
# style, in the format most representative of the current storage.
|
191
204
|
def to_file style = default_style
|
192
|
-
@queued_for_write[style]
|
205
|
+
return @queued_for_write[style] if @queued_for_write[style]
|
206
|
+
file = Tempfile.new(path(style))
|
207
|
+
file.write(AWS::S3::S3Object.value(path(style), bucket_name))
|
208
|
+
file.rewind
|
209
|
+
return file
|
193
210
|
end
|
194
|
-
alias_method :to_io, :to_file
|
195
211
|
|
196
212
|
def flush_writes #:nodoc:
|
197
213
|
@queued_for_write.each do |style, file|
|
198
214
|
begin
|
199
215
|
log("saving #{path(style)}")
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
216
|
+
AWS::S3::S3Object.store(path(style),
|
217
|
+
file,
|
218
|
+
bucket_name,
|
219
|
+
{:content_type => instance_read(:content_type),
|
220
|
+
:access => @s3_permissions,
|
221
|
+
}.merge(@s3_headers))
|
222
|
+
rescue AWS::S3::ResponseError => e
|
204
223
|
raise
|
205
224
|
end
|
206
225
|
end
|
@@ -211,22 +230,20 @@ module Paperclip
|
|
211
230
|
@queued_for_delete.each do |path|
|
212
231
|
begin
|
213
232
|
log("deleting #{path}")
|
214
|
-
|
215
|
-
|
216
|
-
end
|
217
|
-
rescue RightAws::AwsError
|
233
|
+
AWS::S3::S3Object.delete(path, bucket_name)
|
234
|
+
rescue AWS::S3::ResponseError
|
218
235
|
# Ignore this.
|
219
236
|
end
|
220
237
|
end
|
221
238
|
@queued_for_delete = []
|
222
239
|
end
|
223
|
-
|
240
|
+
|
224
241
|
def find_credentials creds
|
225
242
|
case creds
|
226
243
|
when File
|
227
|
-
YAML.
|
244
|
+
YAML::load(ERB.new(File.read(creds.path)).result)
|
228
245
|
when String
|
229
|
-
YAML.
|
246
|
+
YAML::load(ERB.new(File.read(creds)).result)
|
230
247
|
when Hash
|
231
248
|
creds
|
232
249
|
else
|
@@ -237,4 +254,4 @@ module Paperclip
|
|
237
254
|
|
238
255
|
end
|
239
256
|
end
|
240
|
-
end
|
257
|
+
end
|
data/test/attachment_test.rb
CHANGED
@@ -149,9 +149,12 @@ class AttachmentTest < Test::Unit::TestCase
|
|
149
149
|
setup do
|
150
150
|
rebuild_model
|
151
151
|
|
152
|
+
@tempfile = mock
|
153
|
+
@tempfile.expects(:size).returns(10)
|
154
|
+
|
152
155
|
@not_file = mock
|
153
156
|
@not_file.stubs(:nil?).returns(false)
|
154
|
-
@not_file.expects(:to_tempfile).returns(
|
157
|
+
@not_file.expects(:to_tempfile).returns(@tempfile)
|
155
158
|
@not_file.expects(:original_filename).returns("filename.png\r\n")
|
156
159
|
@not_file.expects(:content_type).returns("image/png\r\n")
|
157
160
|
@not_file.expects(:size).returns(10)
|
@@ -178,9 +181,12 @@ class AttachmentTest < Test::Unit::TestCase
|
|
178
181
|
setup do
|
179
182
|
rebuild_model
|
180
183
|
|
184
|
+
@tempfile = mock
|
185
|
+
@tempfile.expects(:size).returns(10)
|
186
|
+
|
181
187
|
@not_file = mock
|
182
188
|
@not_file.stubs(:nil?).returns(false)
|
183
|
-
@not_file.expects(:to_tempfile).returns(
|
189
|
+
@not_file.expects(:to_tempfile).returns(@tempfile)
|
184
190
|
@not_file.expects(:original_filename).returns("sheep_say_b_.png\r\n")
|
185
191
|
@not_file.expects(:content_type).returns("image/png\r\n")
|
186
192
|
@not_file.expects(:size).returns(10)
|
@@ -257,12 +263,12 @@ class AttachmentTest < Test::Unit::TestCase
|
|
257
263
|
end
|
258
264
|
|
259
265
|
should "return the proper path when filename has a single .'s" do
|
260
|
-
assert_equal "./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png", @attachment.path
|
266
|
+
assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
|
261
267
|
end
|
262
268
|
|
263
269
|
should "return the proper path when filename has multiple .'s" do
|
264
270
|
@instance.stubs(:avatar_file_name).returns("5k.old.png")
|
265
|
-
assert_equal "./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png", @attachment.path
|
271
|
+
assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png"), File.expand_path(@attachment.path)
|
266
272
|
end
|
267
273
|
|
268
274
|
context "when expecting three styles" do
|
@@ -299,7 +305,7 @@ class AttachmentTest < Test::Unit::TestCase
|
|
299
305
|
|
300
306
|
should "commit the files to disk" do
|
301
307
|
[:large, :medium, :small].each do |style|
|
302
|
-
io = @attachment.
|
308
|
+
io = @attachment.to_file(style)
|
303
309
|
assert File.exists?(io)
|
304
310
|
assert ! io.is_a?(::Tempfile)
|
305
311
|
end
|
@@ -310,7 +316,7 @@ class AttachmentTest < Test::Unit::TestCase
|
|
310
316
|
[:medium, 100, 15, "GIF"],
|
311
317
|
[:small, 32, 32, "JPEG"]].each do |style|
|
312
318
|
cmd = "identify -format '%w %h %b %m' " +
|
313
|
-
"#{@attachment.
|
319
|
+
"#{@attachment.path(style.first)}"
|
314
320
|
out = `#{cmd}`
|
315
321
|
width, height, size, format = out.split(" ")
|
316
322
|
assert_equal style[1].to_s, width.to_s
|
data/test/helper.rb
CHANGED
@@ -22,7 +22,8 @@ Merb.class_eval do
|
|
22
22
|
def self.root
|
23
23
|
"#{ROOT}"
|
24
24
|
end
|
25
|
-
def self.env
|
25
|
+
def self.env(str=nil)
|
26
|
+
ENV['RAILS_ENV'] = str if str
|
26
27
|
ENV['RAILS_ENV']
|
27
28
|
end
|
28
29
|
end
|
@@ -55,7 +56,7 @@ def rebuild_model options = {}
|
|
55
56
|
include DataMapper::Resource
|
56
57
|
include DataMapper::Validate
|
57
58
|
include Paperclip::Resource
|
58
|
-
property :id,
|
59
|
+
property :id, ::DataMapper::Types::Serial
|
59
60
|
property :other, String
|
60
61
|
has_attached_file :avatar, options
|
61
62
|
end
|
@@ -63,8 +64,8 @@ def rebuild_model options = {}
|
|
63
64
|
end
|
64
65
|
|
65
66
|
def temporary_env(new_env)
|
66
|
-
old_env =
|
67
|
-
|
67
|
+
old_env = Merb.env
|
68
|
+
Merb.env(new_env)
|
68
69
|
yield
|
69
|
-
|
70
|
+
Merb.env(old_env)
|
70
71
|
end
|
data/test/iostream_test.rb
CHANGED
@@ -1,10 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require 'test/unit'
|
3
|
-
require 'stringio'
|
4
|
-
require 'tempfile'
|
5
|
-
require 'shoulda'
|
6
|
-
|
7
|
-
require File.join(File.dirname(__FILE__), '..', 'lib', 'dm-paperclip', 'iostream.rb')
|
1
|
+
require 'test/helper'
|
8
2
|
|
9
3
|
class IOStreamTest < Test::Unit::TestCase
|
10
4
|
context "IOStream" do
|
@@ -17,29 +11,46 @@ class IOStreamTest < Test::Unit::TestCase
|
|
17
11
|
|
18
12
|
context "A file" do
|
19
13
|
setup do
|
20
|
-
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"))
|
14
|
+
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
|
21
15
|
end
|
22
16
|
|
17
|
+
teardown { @file.close }
|
18
|
+
|
23
19
|
context "that is sent #stream_to" do
|
24
20
|
|
25
|
-
|
26
|
-
|
21
|
+
context "and given a String" do
|
22
|
+
setup do
|
23
|
+
FileUtils.mkdir_p(File.join(ROOT, 'tmp'))
|
24
|
+
assert @result = @file.stream_to(File.join(ROOT, 'tmp', 'iostream.string.test'))
|
25
|
+
end
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
27
|
+
should "return a File" do
|
28
|
+
assert @result.is_a?(File)
|
29
|
+
end
|
32
30
|
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
should "contain the same data as the original file" do
|
32
|
+
@file.rewind; @result.rewind
|
33
|
+
assert_equal @file.read, @result.read
|
34
|
+
end
|
35
|
+
end
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
context "and given a Tempfile" do
|
38
|
+
setup do
|
39
|
+
tempfile = Tempfile.new('iostream.test')
|
40
|
+
tempfile.binmode
|
41
|
+
assert @result = @file.stream_to(tempfile)
|
42
|
+
end
|
43
|
+
|
44
|
+
should "return a Tempfile" do
|
45
|
+
assert @result.is_a?(Tempfile)
|
46
|
+
end
|
47
|
+
|
48
|
+
should "contain the same data as the original file" do
|
49
|
+
@file.rewind; @result.rewind
|
50
|
+
assert_equal @file.read, @result.read
|
41
51
|
end
|
42
52
|
end
|
53
|
+
|
43
54
|
end
|
44
55
|
|
45
56
|
context "that is sent #to_tempfile" do
|
@@ -47,8 +58,15 @@ class IOStreamTest < Test::Unit::TestCase
|
|
47
58
|
assert @tempfile = @file.to_tempfile
|
48
59
|
end
|
49
60
|
|
50
|
-
should "convert it to a Tempfile" do
|
51
|
-
assert @tempfile.is_a?(Tempfile)
|
61
|
+
should "convert it to a Paperclip Tempfile" do
|
62
|
+
assert @tempfile.is_a?(Paperclip::Tempfile)
|
63
|
+
end
|
64
|
+
|
65
|
+
should "have the name be based on the original_filename" do
|
66
|
+
name = File.basename(@file.path)
|
67
|
+
extension = File.extname(name)
|
68
|
+
basename = File.basename(name, extension)
|
69
|
+
assert_match %r[^#{Regexp.quote(basename)}.*?#{Regexp.quote(extension)}], File.basename(@tempfile.path)
|
52
70
|
end
|
53
71
|
|
54
72
|
should "have the Tempfile contain the same data as the file" do
|
@@ -57,4 +75,4 @@ class IOStreamTest < Test::Unit::TestCase
|
|
57
75
|
end
|
58
76
|
end
|
59
77
|
end
|
60
|
-
end
|
78
|
+
end
|
data/test/paperclip_test.rb
CHANGED
@@ -21,7 +21,7 @@ class PaperclipTest < Test::Unit::TestCase
|
|
21
21
|
include DataMapper::Resource
|
22
22
|
include DataMapper::Validate
|
23
23
|
include Paperclip::Resource
|
24
|
-
property :id,
|
24
|
+
property :id, DataMapper::Types::Serial
|
25
25
|
property :other, String
|
26
26
|
has_attached_file :file
|
27
27
|
end
|
data/test/storage_test.rb
CHANGED
@@ -1,13 +1,10 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'shoulda'
|
4
|
-
require 'right_aws'
|
5
|
-
|
6
|
-
require File.join(File.dirname(__FILE__), '..', 'lib', 'dm-paperclip', 'geometry.rb')
|
1
|
+
require 'test/helper'
|
2
|
+
require 'aws/s3'
|
7
3
|
|
8
4
|
class StorageTest < Test::Unit::TestCase
|
9
5
|
context "Parsing S3 credentials" do
|
10
6
|
setup do
|
7
|
+
AWS::S3::Base.stubs(:establish_connection!)
|
11
8
|
rebuild_model :storage => :s3,
|
12
9
|
:bucket => "testing",
|
13
10
|
:s3_credentials => {:not => :important}
|
@@ -15,48 +12,133 @@ class StorageTest < Test::Unit::TestCase
|
|
15
12
|
@dummy = Dummy.new
|
16
13
|
@avatar = @dummy.avatar
|
17
14
|
|
18
|
-
@current_env =
|
15
|
+
@current_env = Merb.env
|
19
16
|
end
|
20
17
|
|
21
18
|
teardown do
|
22
|
-
|
19
|
+
Merb.env(@current_env)
|
23
20
|
end
|
24
21
|
|
25
|
-
should "get the correct credentials when
|
26
|
-
|
27
|
-
assert_equal({
|
22
|
+
should "get the correct credentials when environment is production" do
|
23
|
+
Merb.env("production")
|
24
|
+
assert_equal({:key => "12345"},
|
28
25
|
@avatar.parse_credentials('production' => {:key => '12345'},
|
29
26
|
:development => {:key => "54321"}))
|
30
27
|
end
|
31
28
|
|
32
|
-
should "get the correct credentials when
|
33
|
-
|
34
|
-
assert_equal({
|
29
|
+
should "get the correct credentials when environment is development" do
|
30
|
+
Merb.env("development")
|
31
|
+
assert_equal({:key => "54321"},
|
35
32
|
@avatar.parse_credentials('production' => {:key => '12345'},
|
36
33
|
:development => {:key => "54321"}))
|
37
34
|
end
|
38
35
|
|
39
36
|
should "return the argument if the key does not exist" do
|
40
|
-
|
41
|
-
assert_equal({
|
37
|
+
Merb.env("not really an env")
|
38
|
+
assert_equal({:test => "12345"}, @avatar.parse_credentials(:test => "12345"))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "" do
|
43
|
+
setup do
|
44
|
+
AWS::S3::Base.stubs(:establish_connection!)
|
45
|
+
rebuild_model :storage => :s3,
|
46
|
+
:s3_credentials => {},
|
47
|
+
:bucket => "bucket",
|
48
|
+
:path => ":attachment/:basename.:extension",
|
49
|
+
:url => ":s3_path_url"
|
50
|
+
@dummy = Dummy.new
|
51
|
+
@dummy.avatar = StringIO.new(".")
|
52
|
+
end
|
53
|
+
|
54
|
+
should "return a url based on an S3 path" do
|
55
|
+
assert_match %r{^http://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url
|
56
|
+
end
|
57
|
+
end
|
58
|
+
context "" do
|
59
|
+
setup do
|
60
|
+
AWS::S3::Base.stubs(:establish_connection!)
|
61
|
+
rebuild_model :storage => :s3,
|
62
|
+
:s3_credentials => {},
|
63
|
+
:bucket => "bucket",
|
64
|
+
:path => ":attachment/:basename.:extension",
|
65
|
+
:url => ":s3_domain_url"
|
66
|
+
@dummy = Dummy.new
|
67
|
+
@dummy.avatar = StringIO.new(".")
|
68
|
+
end
|
69
|
+
|
70
|
+
should "return a url based on an S3 subdomain" do
|
71
|
+
assert_match %r{^http://bucket.s3.amazonaws.com/avatars/stringio.txt}, @dummy.avatar.url
|
72
|
+
end
|
73
|
+
end
|
74
|
+
context "" do
|
75
|
+
setup do
|
76
|
+
AWS::S3::Base.stubs(:establish_connection!)
|
77
|
+
rebuild_model :storage => :s3,
|
78
|
+
:s3_credentials => {
|
79
|
+
:production => { :bucket => "prod_bucket" },
|
80
|
+
:development => { :bucket => "dev_bucket" }
|
81
|
+
},
|
82
|
+
:s3_host_alias => "something.something.com",
|
83
|
+
:path => ":attachment/:basename.:extension",
|
84
|
+
:url => ":s3_alias_url"
|
85
|
+
@dummy = Dummy.new
|
86
|
+
@dummy.avatar = StringIO.new(".")
|
87
|
+
end
|
88
|
+
|
89
|
+
should "return a url based on the host_alias" do
|
90
|
+
assert_match %r{^http://something.something.com/avatars/stringio.txt}, @dummy.avatar.url
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "Generating a url with an expiration" do
|
95
|
+
setup do
|
96
|
+
AWS::S3::Base.stubs(:establish_connection!)
|
97
|
+
rebuild_model :storage => :s3,
|
98
|
+
:s3_credentials => {
|
99
|
+
:production => { :bucket => "prod_bucket" },
|
100
|
+
:development => { :bucket => "dev_bucket" }
|
101
|
+
},
|
102
|
+
:s3_host_alias => "something.something.com",
|
103
|
+
:path => ":attachment/:basename.:extension",
|
104
|
+
:url => ":s3_alias_url"
|
105
|
+
|
106
|
+
Merb.env("production")
|
107
|
+
|
108
|
+
@dummy = Dummy.new
|
109
|
+
@dummy.avatar = StringIO.new(".")
|
110
|
+
|
111
|
+
AWS::S3::S3Object.expects(:url_for).with("avatars/stringio.txt", "prod_bucket", { :expires_in => 3600 })
|
112
|
+
|
113
|
+
@dummy.avatar.expiring_url
|
114
|
+
end
|
115
|
+
|
116
|
+
should "should succeed" do
|
117
|
+
assert true
|
42
118
|
end
|
43
119
|
end
|
44
120
|
|
45
121
|
context "Parsing S3 credentials with a bucket in them" do
|
46
122
|
setup do
|
123
|
+
AWS::S3::Base.stubs(:establish_connection!)
|
47
124
|
rebuild_model :storage => :s3,
|
48
125
|
:s3_credentials => {
|
49
126
|
:production => { :bucket => "prod_bucket" },
|
50
127
|
:development => { :bucket => "dev_bucket" }
|
51
128
|
}
|
52
129
|
@dummy = Dummy.new
|
130
|
+
@old_env = Merb.env
|
53
131
|
end
|
54
132
|
|
55
|
-
|
133
|
+
teardown{ Merb.env(@old_env) }
|
134
|
+
|
135
|
+
should "get the right bucket in production" do
|
136
|
+
Merb.env("production")
|
56
137
|
assert_equal "prod_bucket", @dummy.avatar.bucket_name
|
57
138
|
end
|
58
139
|
|
59
|
-
should "get the right bucket in development"
|
140
|
+
should "get the right bucket in development" do
|
141
|
+
Merb.env("development")
|
60
142
|
assert_equal "dev_bucket", @dummy.avatar.bucket_name
|
61
143
|
end
|
62
144
|
end
|
@@ -82,11 +164,13 @@ class StorageTest < Test::Unit::TestCase
|
|
82
164
|
|
83
165
|
context "when assigned" do
|
84
166
|
setup do
|
85
|
-
@file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'))
|
167
|
+
@file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
|
86
168
|
@dummy = Dummy.new
|
87
169
|
@dummy.avatar = @file
|
88
170
|
end
|
89
171
|
|
172
|
+
teardown { @file.close }
|
173
|
+
|
90
174
|
should "not get a bucket to get a URL" do
|
91
175
|
@dummy.avatar.expects(:s3).never
|
92
176
|
@dummy.avatar.expects(:s3_bucket).never
|
@@ -95,15 +179,7 @@ class StorageTest < Test::Unit::TestCase
|
|
95
179
|
|
96
180
|
context "and saved" do
|
97
181
|
setup do
|
98
|
-
@
|
99
|
-
@bucket_mock = stub
|
100
|
-
RightAws::S3.expects(:new).with("12345", "54321", {}).returns(@s3_mock)
|
101
|
-
@s3_mock.expects(:bucket).with("testing", true, "public-read").returns(@bucket_mock)
|
102
|
-
@key_mock = stub
|
103
|
-
@bucket_mock.expects(:key).returns(@key_mock)
|
104
|
-
@key_mock.expects(:data=)
|
105
|
-
@key_mock.expects(:put)
|
106
|
-
@dummy.id = 1
|
182
|
+
AWS::S3::S3Object.stubs(:store).with(@dummy.avatar.path, anything, 'testing', :content_type => 'image/png', :access => :public_read)
|
107
183
|
@dummy.save
|
108
184
|
end
|
109
185
|
|
@@ -114,13 +190,8 @@ class StorageTest < Test::Unit::TestCase
|
|
114
190
|
|
115
191
|
context "and remove" do
|
116
192
|
setup do
|
117
|
-
|
118
|
-
|
119
|
-
RightAws::S3.expects(:new).with("12345", "54321", {}).returns(@s3_mock)
|
120
|
-
@s3_mock.expects(:bucket).with("testing", true, "public-read").returns(@bucket_mock)
|
121
|
-
@key_mock = stub
|
122
|
-
@bucket_mock.expects(:key).at_least(2).returns(@key_mock)
|
123
|
-
@key_mock.expects(:delete)
|
193
|
+
AWS::S3::S3Object.stubs(:exists?).returns(true)
|
194
|
+
AWS::S3::S3Object.stubs(:delete)
|
124
195
|
@dummy.destroy_attached_files
|
125
196
|
end
|
126
197
|
|
@@ -130,6 +201,84 @@ class StorageTest < Test::Unit::TestCase
|
|
130
201
|
end
|
131
202
|
end
|
132
203
|
end
|
204
|
+
|
205
|
+
context "An attachment with S3 storage and bucket defined as a Proc" do
|
206
|
+
setup do
|
207
|
+
AWS::S3::Base.stubs(:establish_connection!)
|
208
|
+
rebuild_model :storage => :s3,
|
209
|
+
:bucket => lambda { |attachment| "bucket_#{attachment.instance.other}" },
|
210
|
+
:s3_credentials => {:not => :important}
|
211
|
+
end
|
212
|
+
|
213
|
+
should "get the right bucket name" do
|
214
|
+
assert "bucket_a", Dummy.new(:other => 'a').avatar.bucket_name
|
215
|
+
assert "bucket_b", Dummy.new(:other => 'b').avatar.bucket_name
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context "An attachment with S3 storage and specific s3 headers set" do
|
220
|
+
setup do
|
221
|
+
AWS::S3::Base.stubs(:establish_connection!)
|
222
|
+
rebuild_model :storage => :s3,
|
223
|
+
:bucket => "testing",
|
224
|
+
:path => ":attachment/:style/:basename.:extension",
|
225
|
+
:s3_credentials => {
|
226
|
+
'access_key_id' => "12345",
|
227
|
+
'secret_access_key' => "54321"
|
228
|
+
},
|
229
|
+
:s3_headers => {'Cache-Control' => 'max-age=31557600'}
|
230
|
+
end
|
231
|
+
|
232
|
+
context "when assigned" do
|
233
|
+
setup do
|
234
|
+
@file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
|
235
|
+
@dummy = Dummy.new
|
236
|
+
@dummy.avatar = @file
|
237
|
+
end
|
238
|
+
|
239
|
+
teardown { @file.close }
|
240
|
+
|
241
|
+
context "and saved" do
|
242
|
+
setup do
|
243
|
+
AWS::S3::Base.stubs(:establish_connection!)
|
244
|
+
AWS::S3::S3Object.stubs(:store).with(@dummy.avatar.path,
|
245
|
+
anything,
|
246
|
+
'testing',
|
247
|
+
:content_type => 'image/png',
|
248
|
+
:access => :public_read,
|
249
|
+
'Cache-Control' => 'max-age=31557600')
|
250
|
+
@dummy.save
|
251
|
+
end
|
252
|
+
|
253
|
+
should "succeed" do
|
254
|
+
assert true
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
context "with S3 credentials in a YAML file" do
|
261
|
+
setup do
|
262
|
+
ENV['S3_KEY'] = 'env_key'
|
263
|
+
ENV['S3_BUCKET'] = 'env_bucket'
|
264
|
+
ENV['S3_SECRET'] = 'env_secret'
|
265
|
+
|
266
|
+
Merb.env('test')
|
267
|
+
|
268
|
+
rebuild_model :storage => :s3,
|
269
|
+
:s3_credentials => File.new(File.join(File.dirname(__FILE__), "fixtures/s3.yml"))
|
270
|
+
|
271
|
+
Dummy.auto_migrate!
|
272
|
+
|
273
|
+
@dummy = Dummy.new
|
274
|
+
end
|
275
|
+
|
276
|
+
should "run it the file through ERB" do
|
277
|
+
assert_equal 'env_bucket', @dummy.avatar.bucket_name
|
278
|
+
assert_equal 'env_key', AWS::S3::Base.connection.options[:access_key_id]
|
279
|
+
assert_equal 'env_secret', AWS::S3::Base.connection.options[:secret_access_key]
|
280
|
+
end
|
281
|
+
end
|
133
282
|
|
134
283
|
unless ENV["S3_TEST_BUCKET"].blank?
|
135
284
|
context "Using S3 for real, an attachment with S3 storage" do
|
@@ -140,7 +289,7 @@ class StorageTest < Test::Unit::TestCase
|
|
140
289
|
:path => ":class/:attachment/:id/:style.:extension",
|
141
290
|
:s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml"))
|
142
291
|
|
143
|
-
Dummy.
|
292
|
+
Dummy.auto_migrate!
|
144
293
|
@dummy = Dummy.new
|
145
294
|
end
|
146
295
|
|
@@ -150,12 +299,14 @@ class StorageTest < Test::Unit::TestCase
|
|
150
299
|
|
151
300
|
context "when assigned" do
|
152
301
|
setup do
|
153
|
-
@file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'))
|
302
|
+
@file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
|
154
303
|
@dummy.avatar = @file
|
155
304
|
end
|
156
305
|
|
157
|
-
|
158
|
-
|
306
|
+
teardown { @file.close }
|
307
|
+
|
308
|
+
should "still return a Tempfile when sent #to_file" do
|
309
|
+
assert_equal Tempfile, @dummy.avatar.to_file.class
|
159
310
|
end
|
160
311
|
|
161
312
|
context "and saved" do
|
@@ -170,4 +321,4 @@ class StorageTest < Test::Unit::TestCase
|
|
170
321
|
end
|
171
322
|
end
|
172
323
|
end
|
173
|
-
end
|
324
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 2
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 2.
|
7
|
+
- 4
|
8
|
+
- 0
|
9
|
+
version: 2.4.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Ken Robertson
|
@@ -14,10 +14,71 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-04-21 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
|
-
dependencies:
|
20
|
-
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: shoulda
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: mocha
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
- 9
|
42
|
+
- 8
|
43
|
+
version: 0.9.8
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: aws-s3
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
type: :development
|
57
|
+
version_requirements: *id003
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: datamapper
|
60
|
+
prerelease: false
|
61
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
68
|
+
type: :development
|
69
|
+
version_requirements: *id004
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: do_sqlite3
|
72
|
+
prerelease: false
|
73
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id005
|
21
82
|
description:
|
22
83
|
email: ken@invalidlogic.com
|
23
84
|
executables: []
|
@@ -82,7 +143,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
143
|
version: "0"
|
83
144
|
requirements:
|
84
145
|
- ImageMagick
|
85
|
-
- data_mapper
|
86
146
|
rubyforge_project: dm-paperclip
|
87
147
|
rubygems_version: 1.3.6
|
88
148
|
signing_key:
|