paperclip-aws 1.4.1 → 1.6.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/Gemfile CHANGED
@@ -1,5 +1,5 @@
1
1
  source "http://rubygems.org"
2
- gem 'paperclip', '>=2.4.0'
2
+ gem 'paperclip', '~>2.6.0'
3
3
  gem 'aws-sdk', '>=1.2.0'
4
4
 
5
5
  group :development do
@@ -8,7 +8,7 @@ group :development do
8
8
  end
9
9
 
10
10
  group :test do
11
- gem "sqlite3"
11
+ gem "sqlite3", '~> 1.3.4'
12
12
  gem 'shoulda-context'
13
13
  gem "mocha"
14
14
  end
data/Gemfile.lock CHANGED
@@ -1,25 +1,25 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- activemodel (3.1.3)
5
- activesupport (= 3.1.3)
4
+ activemodel (3.2.1)
5
+ activesupport (= 3.2.1)
6
6
  builder (~> 3.0.0)
7
- i18n (~> 0.6)
8
- activerecord (3.1.3)
9
- activemodel (= 3.1.3)
10
- activesupport (= 3.1.3)
11
- arel (~> 2.2.1)
7
+ activerecord (3.2.1)
8
+ activemodel (= 3.2.1)
9
+ activesupport (= 3.2.1)
10
+ arel (~> 3.0.0)
12
11
  tzinfo (~> 0.3.29)
13
- activesupport (3.1.3)
12
+ activesupport (3.2.1)
13
+ i18n (~> 0.6)
14
14
  multi_json (~> 1.0)
15
- arel (2.2.1)
16
- aws-sdk (1.2.3)
15
+ arel (3.0.0)
16
+ aws-sdk (1.3.4)
17
17
  httparty (~> 0.7)
18
18
  json (~> 1.4)
19
19
  nokogiri (>= 1.4.4)
20
20
  uuidtools (~> 2.1)
21
21
  builder (3.0.0)
22
- cocaine (0.2.0)
22
+ cocaine (0.2.1)
23
23
  git (1.2.5)
24
24
  httparty (0.8.1)
25
25
  multi_json
@@ -29,22 +29,22 @@ GEM
29
29
  bundler (~> 1.0)
30
30
  git (>= 1.2.5)
31
31
  rake
32
- json (1.6.2)
32
+ json (1.6.5)
33
33
  metaclass (0.0.1)
34
34
  mime-types (1.17.2)
35
- mocha (0.10.0)
35
+ mocha (0.10.4)
36
36
  metaclass (~> 0.0.1)
37
37
  multi_json (1.0.4)
38
38
  multi_xml (0.4.1)
39
39
  nokogiri (1.5.0)
40
- paperclip (2.4.5)
40
+ paperclip (2.6.0)
41
41
  activerecord (>= 2.3.0)
42
42
  activesupport (>= 2.3.2)
43
43
  cocaine (>= 0.0.2)
44
44
  mime-types
45
45
  rake (0.9.2.2)
46
46
  shoulda-context (1.0.0)
47
- sqlite3 (1.3.4)
47
+ sqlite3 (1.3.5)
48
48
  tzinfo (0.3.31)
49
49
  uuidtools (2.1.2)
50
50
 
@@ -56,6 +56,6 @@ DEPENDENCIES
56
56
  bundler (~> 1.0.0)
57
57
  jeweler (~> 1.6.4)
58
58
  mocha
59
- paperclip (>= 2.4.0)
59
+ paperclip (~> 2.6.0)
60
60
  shoulda-context
61
- sqlite3
61
+ sqlite3 (~> 1.3.4)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.4.1
1
+ 1.6.0
data/lib/paperclip-aws.rb CHANGED
@@ -13,47 +13,26 @@ module Paperclip
13
13
  rescue LoadError => e
14
14
  e.message << " (You may need to install the aws-sdk gem)"
15
15
  raise e
16
- end unless defined?(AWS)
16
+ end unless defined?(AWS::Core)
17
17
 
18
- attr_accessor :s3_options
19
- base.instance_eval do
20
-
21
-
22
- @s3_credentials = parse_credentials(@options.s3_credentials)
23
- @s3_permissions = set_permissions(@options.s3_permissions)
24
-
25
- @s3_protocol = @options.s3_protocol ||
26
- Proc.new do |style, attachment|
27
- permission = (@s3_permissions[style.to_sym] || @s3_permissions[:default])
28
- permission = permission.call(attachment, style) if permission.is_a?(Proc)
29
- (permission == :public_read) ? 'http' : 'https'
30
- end
31
-
32
- @s3_headers = @options.s3_headers || {}
33
-
34
- @s3_bucket = @options.bucket
35
- @s3_bucket = @s3_bucket.call(self) if @s3_bucket.is_a?(Proc)
36
-
37
- @s3_options = @options.s3_options || {}
38
- # setup Amazon Server Side encryption
39
- @s3_options.reverse_merge!({
40
- :sse => false,
41
- :storage_class => :standard,
42
- :content_disposition => nil
43
- })
44
-
45
- @s3_endpoint = @s3_credentials[:endpoint] || 's3.amazonaws.com'
46
-
47
- @s3_host_alias = @options.s3_host_alias
48
- @s3_host_alias = @s3_host_alias.call(self) if @s3_host_alias.is_a?(Proc)
49
-
50
- @s3 = AWS::S3.new(
51
- :access_key_id => @s3_credentials[:access_key_id],
52
- :secret_access_key => @s3_credentials[:secret_access_key],
53
- :s3_endpoint => @s3_endpoint
54
- )
55
- end
18
+ attr_accessor :s3_credentials, :s3_bucket, :s3_permissions, :s3_options, :s3_protocol, :s3_host_alias
56
19
 
20
+ base.instance_eval do
21
+ self.setup_credentials
22
+ self.setup_bucket
23
+ self.setup_permissions
24
+ self.setup_s3_protocol
25
+ self.setup_s3_options
26
+ self.setup_s3_host_alias
27
+ end
28
+ end
29
+
30
+ def s3
31
+ @s3 ||= AWS::S3.new(
32
+ :access_key_id => @s3_credentials[:access_key_id],
33
+ :secret_access_key => @s3_credentials[:secret_access_key],
34
+ :s3_endpoint => @s3_endpoint
35
+ )
57
36
  end
58
37
 
59
38
  def url(style=default_style, options={})
@@ -69,42 +48,23 @@ module Paperclip
69
48
  :action => :read
70
49
  })
71
50
  secure = ( self.choose_protocol(style, options) == 'https' )
72
- return @s3.buckets[@s3_bucket].objects[path(style).gsub(%r{^/}, "")].url_for(options[:action], { :secure => secure, :expires => options[:expires] }).to_s
51
+ return self.s3.buckets[@s3_bucket].objects[path(style).gsub(%r{^/}, "")].url_for(options[:action], { :secure => secure, :expires => options[:expires] }).to_s
73
52
  else
74
53
  if @s3_host_alias.present?
75
54
  url = "#{choose_protocol(style, options)}://#{@s3_host_alias}/#{path(style).gsub(%r{^/}, "")}"
76
55
  else
77
56
  url = "#{choose_protocol(style, options)}://#{@s3_endpoint}/#{@s3_bucket}/#{path(style).gsub(%r{^/}, "")}"
78
- end
57
+ end
79
58
  return URI.escape(url)
80
59
  end
81
60
  end
82
-
83
- def bucket_name
84
- @s3_bucket
85
- end
86
-
87
- def parse_credentials(creds)
88
- creds = find_credentials(creds).stringify_keys
89
- env = Object.const_defined?(:Rails) ? Rails.env : nil
90
- (creds[env] || creds).symbolize_keys
91
- end
92
-
93
- def set_permissions permissions
94
- if permissions.is_a?(Hash)
95
- permissions[:default] = permissions[:default] || :public_read
96
- else
97
- permissions = { :default => permissions || :public_read }
98
- end
99
- permissions
100
- end
101
-
61
+
102
62
  def exists?(style = default_style)
103
63
  if path(style).nil? || path(style).to_s.strip == ""
104
64
  return false
105
65
  end
106
66
  begin
107
- return @s3.buckets[@s3_bucket].objects[path(style)].exists?
67
+ return self.s3.buckets[@s3_bucket].objects[path(style)].exists?
108
68
  rescue AWS::S3::Errors::Base
109
69
  return false
110
70
  end
@@ -127,13 +87,13 @@ module Paperclip
127
87
  basename = File.basename(filename, extname)
128
88
  file = Tempfile.new([basename, extname])
129
89
  file.binmode
130
- file.write(@s3.buckets[@s3_bucket].objects[path(style)].read)
90
+ file.write(self.s3.buckets[@s3_bucket].objects[path(style)].read)
131
91
  file.rewind
132
92
  return file
133
93
  end
134
94
 
135
95
  def create_bucket
136
- @s3.buckets.create(@s3_bucket)
96
+ self.s3.buckets.create(@s3_bucket)
137
97
  end
138
98
 
139
99
  def flush_writes #:nodoc:
@@ -141,14 +101,11 @@ module Paperclip
141
101
  begin
142
102
  log("saving #{path(style)}")
143
103
 
144
- @s3.buckets[@s3_bucket].objects[path(style)].write(
104
+ self.s3.buckets[@s3_bucket].objects[path(style)].write({
145
105
  :file => file.path,
146
106
  :acl => @s3_permissions[:style.to_sym] || @s3_permissions[:default],
147
- :storage_class => @s3_options[:storage_class],
148
- :content_type => file.content_type,
149
- :content_disposition => @s3_options[:content_disposition],
150
- :server_side_encryption => @s3_options[:sse]
151
- )
107
+ :content_type => file.content_type
108
+ }.reverse_merge(@s3_options))
152
109
  rescue AWS::S3::Errors::NoSuchBucket => e
153
110
  create_bucket
154
111
  retry
@@ -163,15 +120,29 @@ module Paperclip
163
120
  @queued_for_delete.each do |path|
164
121
  begin
165
122
  log("deleting #{path}")
166
- @s3.buckets[@s3_bucket].objects[path].delete
123
+ self.s3.buckets[@s3_bucket].objects[path].delete
167
124
  rescue AWS::Errors::Base => e
168
125
  raise
169
126
  end
170
127
  end
171
128
  @queued_for_delete = []
172
129
  end
173
-
174
- def find_credentials creds
130
+
131
+ # PRIVATE METHODS
132
+ def setup_credentials
133
+ if @options[:s3_credentials].present?
134
+ @s3_credentials = self.parse_credentials(@options[:s3_credentials]).stringify_keys
135
+ env = Object.const_defined?(:Rails) ? Rails.env : nil
136
+
137
+ @s3_credentials = (@s3_credentials[env] || @s3_credentials).symbolize_keys
138
+ @s3_endpoint = @s3_credentials[:endpoint] || 's3.amazonaws.com'
139
+ else
140
+ raise ArgumentError, "missing required :s3_credentials option"
141
+ end
142
+ end
143
+ protected :setup_credentials
144
+
145
+ def parse_credentials(creds)
175
146
  case creds
176
147
  when File
177
148
  YAML::load(ERB.new(File.read(creds.path)).result)
@@ -182,9 +153,63 @@ module Paperclip
182
153
  else
183
154
  raise ArgumentError, "Credentials are not a path, file, or hash."
184
155
  end
156
+ end
157
+ protected :parse_credentials
158
+
159
+ def setup_bucket
160
+ if @options[:bucket].present?
161
+ @s3_bucket = @options[:bucket] || @s3_credentials[:bucket]
162
+ @s3_bucket = @s3_bucket.call(self) if @s3_bucket.is_a?(Proc)
163
+ else
164
+ raise ArgumentError, "missing required :bucket option"
165
+ end
185
166
  end
186
- private :find_credentials
187
-
167
+ protected :setup_bucket
168
+
169
+ def setup_permissions
170
+ @s3_permissions = self.parse_permissions(@options[:s3_permissions])
171
+ end
172
+ protected :setup_permissions
173
+
174
+ def parse_permissions(permissions)
175
+ if permissions.is_a?(Hash)
176
+ permissions[:default] = permissions[:default] || :public_read
177
+ else
178
+ permissions = { :default => permissions || :public_read }
179
+ end
180
+ permissions
181
+ end
182
+ protected :parse_permissions
183
+
184
+ def setup_s3_protocol
185
+ @s3_protocol = @options[:s3_protocol] ||
186
+ Proc.new do |style, attachment|
187
+ permission = (@s3_permissions[style.to_sym] || @s3_permissions[:default])
188
+ permission = permission.call(attachment, style) if permission.is_a?(Proc)
189
+ (permission == :public_read) ? 'http' : 'https'
190
+ end
191
+ end
192
+ protected :setup_s3_protocol
193
+
194
+ def setup_s3_options
195
+ @s3_options = (@options[:s3_options] || {}).symbolize_keys
196
+
197
+ # setup Amazon Server Side encryption
198
+ @s3_options.reverse_merge!({
199
+ :sse => false,
200
+ :storage_class => :standard,
201
+ :content_disposition => nil
202
+ })
203
+ @s3_options[:server_side_encryption] ||= @s3_options.delete(:sse)
204
+ end
205
+ protected :setup_s3_options
206
+
207
+
208
+ def setup_s3_host_alias
209
+ @s3_host_alias = @options[:s3_host_alias]
210
+ @s3_host_alias = @s3_host_alias.call(self) if @s3_host_alias.is_a?(Proc)
211
+ end
212
+ protected :setup_s3_host_alias
188
213
  end
189
214
  end
190
215
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "paperclip-aws"
8
- s.version = "1.4.1"
8
+ s.version = "1.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Igor Alexandrov"]
12
- s.date = "2011-12-06"
12
+ s.date = "2012-02-12"
13
13
  s.description = "'paperclip-aws' is a full featured storage module that supports all S3 locations (US, European and Tokio) without any additional hacking."
14
14
  s.email = "igor.alexandrov@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -28,7 +28,6 @@ Gem::Specification.new do |s|
28
28
  "paperclip-aws.gemspec",
29
29
  "test/aws_storage_test.rb",
30
30
  "test/database.yml",
31
- "test/debug.log",
32
31
  "test/fixtures/5k.png",
33
32
  "test/fixtures/spaced file.png",
34
33
  "test/helper.rb"
@@ -43,18 +42,18 @@ Gem::Specification.new do |s|
43
42
  s.specification_version = 3
44
43
 
45
44
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
- s.add_runtime_dependency(%q<paperclip>, [">= 2.4.0"])
45
+ s.add_runtime_dependency(%q<paperclip>, ["~> 2.6.0"])
47
46
  s.add_runtime_dependency(%q<aws-sdk>, [">= 1.2.0"])
48
47
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
49
48
  s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
50
49
  else
51
- s.add_dependency(%q<paperclip>, [">= 2.4.0"])
50
+ s.add_dependency(%q<paperclip>, ["~> 2.6.0"])
52
51
  s.add_dependency(%q<aws-sdk>, [">= 1.2.0"])
53
52
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
54
53
  s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
55
54
  end
56
55
  else
57
- s.add_dependency(%q<paperclip>, [">= 2.4.0"])
56
+ s.add_dependency(%q<paperclip>, ["~> 2.6.0"])
58
57
  s.add_dependency(%q<aws-sdk>, [">= 1.2.0"])
59
58
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
60
59
  s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
@@ -8,7 +8,7 @@ class AwsStorageTest < Test::Unit::TestCase
8
8
  end
9
9
 
10
10
  def default_model_options(options={})
11
- {
11
+ options.reverse_merge!({
12
12
  :storage => :aws,
13
13
  :bucket => "testing",
14
14
  :s3_credentials => {
@@ -16,44 +16,50 @@ class AwsStorageTest < Test::Unit::TestCase
16
16
  :secret_access_key => "SECRET_ACCESS_KEY"
17
17
  },
18
18
  :path => ":attachment/:basename.:extension"
19
- }.deep_merge!(options)
19
+ })
20
20
  end
21
21
 
22
- context "Parsing S3 credentials" do
23
- setup do
24
- rebuild_model default_model_options
25
-
26
- @dummy = Dummy.new
27
- @avatar = @dummy.avatar
28
- end
29
-
22
+ context "Parsing S3 credentials" do
30
23
  should "get the correct credentials when RAILS_ENV is production" do
31
24
  rails_env("production")
32
25
 
33
- assert_equal(
34
- { :access_key_id => "12345" },
35
- @avatar.parse_credentials(
26
+ rebuild_model default_model_options(
27
+ :s3_credentials => {
36
28
  :production => { :access_key_id => '12345' },
37
- :development => { :access_key_id => '54321' }
38
- )
29
+ :development => { :access_key_id => '54321' }
30
+ }
39
31
  )
32
+
33
+ @dummy = Dummy.new
34
+ assert_equal({ :access_key_id => "12345" }, @dummy.avatar.s3_credentials)
40
35
  end
41
36
 
42
37
  should "get the correct credentials when RAILS_ENV is development" do
43
38
  rails_env("development")
44
39
 
45
- assert_equal(
46
- { :access_key_id => "54321" },
47
- @avatar.parse_credentials(
40
+ rebuild_model default_model_options(
41
+ :s3_credentials => {
48
42
  :production => { :access_key_id => '12345' },
49
- :development => { :access_key_id => '54321' }
50
- )
51
- )
43
+ :development => { :access_key_id => '54321' }
44
+ }
45
+ )
46
+
47
+ @dummy = Dummy.new
48
+ assert_equal({ :access_key_id => "54321" }, @dummy.avatar.s3_credentials)
52
49
  end
53
50
 
54
51
  should "return the argument if the key does not exist" do
55
52
  rails_env("not really an env")
56
- assert_equal({:test => "12345"}, @avatar.parse_credentials(:test => "12345"))
53
+
54
+ rebuild_model default_model_options(
55
+ :s3_credentials => {
56
+ :test => "12345"
57
+ }
58
+ )
59
+
60
+ @dummy = Dummy.new
61
+
62
+ assert_equal({:test => "12345"}, @dummy.avatar.s3_credentials)
57
63
  end
58
64
 
59
65
  end
@@ -193,7 +199,10 @@ class AwsStorageTest < Test::Unit::TestCase
193
199
 
194
200
  context "An attachment that uses S3 for storage and has spaces in file name" do
195
201
  setup do
196
- rebuild_model default_model_options(:styles => { :large => ['500x500#', :jpg] })
202
+ rebuild_model default_model_options(
203
+ :styles => { :large => ['500x500#', :jpg] },
204
+ :restricted_characters => /[&$+,\/:;=?@<>\[\]\{\}\|\\\^~%#]/
205
+ )
197
206
 
198
207
  @dummy = Dummy.new
199
208
  @dummy.avatar = File.new(fixture_file('spaced file.png'), 'rb')
@@ -221,4 +230,93 @@ class AwsStorageTest < Test::Unit::TestCase
221
230
  assert ! Dummy.new.avatar.is_a?(Paperclip::Storage::Filesystem)
222
231
  end
223
232
  end
224
- end
233
+
234
+ context "S3 options" do
235
+ setup do
236
+ @writer = mock()
237
+ AWS::S3.expects(:new).returns(
238
+ stub(:buckets =>
239
+ stub(:[] =>
240
+ stub(:objects =>
241
+ stub(:[] => @writer)
242
+ )
243
+ )
244
+ )
245
+ )
246
+ end
247
+
248
+ should "be set to default" do
249
+ rebuild_model default_model_options
250
+
251
+ @writer.expects(:write).with do |value|
252
+ value[:sse] == nil &&
253
+ value[:server_side_encryption] == false &&
254
+ value[:storage_class] == :standard &&
255
+ value[:content_disposition] == nil &&
256
+ value[:expires] == nil
257
+ end
258
+
259
+ @dummy = Dummy.new
260
+ @dummy.avatar = File.new(fixture_file('spaced file.png'), 'rb')
261
+ @dummy.save
262
+ end
263
+
264
+ should "be configured" do
265
+ rebuild_model default_model_options(:s3_options => {
266
+ :sse => 'AES256',
267
+ :storage_class => :reduced_redundancy,
268
+ :content_disposition => :attachment,
269
+ :cache_control => 'max-age=86400'
270
+ })
271
+
272
+ @writer.expects(:write).with do |value|
273
+ value[:sse] == nil &&
274
+ value[:server_side_encryption] == 'AES256' &&
275
+ value[:storage_class] == :reduced_redundancy &&
276
+ value[:content_disposition] == :attachment &&
277
+ value[:cache_control] == 'max-age=86400'
278
+ end
279
+
280
+ @dummy = Dummy.new
281
+ @dummy.avatar = File.new(fixture_file('spaced file.png'), 'rb')
282
+ @dummy.save
283
+ end
284
+
285
+ should "work with string-keyed hash" do
286
+ rebuild_model default_model_options(:s3_options => {
287
+ 'cache_control' => 'max-age=86400'
288
+ })
289
+
290
+ @writer.expects(:write).with do |value|
291
+ value['cache_control'] == nil &&
292
+ value[:cache_control] == 'max-age=86400'
293
+ end
294
+
295
+ @dummy = Dummy.new
296
+ @dummy.avatar = File.new(fixture_file('spaced file.png'), 'rb')
297
+ @dummy.save
298
+ end
299
+
300
+ should "allow to modify options in instance" do
301
+ rebuild_model default_model_options
302
+
303
+ @writer.expects(:write).with do |value|
304
+ value[:sse] == nil &&
305
+ value[:server_side_encryption] == false &&
306
+ value[:storage_class] == :standard &&
307
+ value[:content_disposition] == "attachment; filename=spaced_file.png" &&
308
+ value[:expires] == nil
309
+ end
310
+
311
+ Dummy.class_eval do
312
+ before_save do
313
+ self.avatar.s3_options[:content_disposition] = "attachment; filename=#{self.avatar_file_name}"
314
+ end
315
+ end
316
+
317
+ @dummy = Dummy.new
318
+ @dummy.avatar = File.new(fixture_file('spaced file.png'), 'rb')
319
+ @dummy.save
320
+ end
321
+ end
322
+ end