Commit 75f413da by Prem Sichanugrist

Use AWS::SDK instead of AWS::S3

The original commit came from @amazonwebservices and @trevorrowe in #579.
I had to revise the commit and make sure all the test cases are passing.
All credits still goes to those guys, thanks a lot!
parent 5f3b88d1
PATH
remote: .
specs:
paperclip (2.4.5)
activerecord (>= 2.3.0)
activesupport (>= 2.3.2)
cocaine (>= 0.0.2)
mime-types
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
activemodel (3.1.0) activemodel (3.1.2)
activesupport (= 3.1.0) activesupport (= 3.1.2)
bcrypt-ruby (~> 3.0.0)
builder (~> 3.0.0) builder (~> 3.0.0)
i18n (~> 0.6) i18n (~> 0.6)
activerecord (3.1.0) activerecord (3.1.2)
activemodel (= 3.1.0) activemodel (= 3.1.2)
activesupport (= 3.1.0) activesupport (= 3.1.2)
arel (~> 2.2.1) arel (~> 2.2.1)
tzinfo (~> 0.3.29) tzinfo (~> 0.3.29)
activesupport (3.1.0) activesupport (3.1.2)
multi_json (~> 1.0) multi_json (~> 1.0)
appraisal (0.3.8) appraisal (0.4.0)
bundler bundler
rake rake
arel (2.2.1) arel (2.2.1)
...@@ -23,13 +31,13 @@ GEM ...@@ -23,13 +31,13 @@ GEM
cucumber (>= 1.0.2) cucumber (>= 1.0.2)
rdiscount (>= 1.6.8) rdiscount (>= 1.6.8)
rspec (>= 2.6.0) rspec (>= 2.6.0)
aws-s3 (0.6.2) aws-sdk (1.2.1)
builder httparty (~> 0.7)
mime-types json (~> 1.4)
xml-simple nokogiri (>= 1.4.4)
uuidtools (~> 2.1)
bcat (0.6.2) bcat (0.6.2)
rack (~> 1.0) rack (~> 1.0)
bcrypt-ruby (3.0.1)
builder (3.0.0) builder (3.0.0)
capybara (1.1.1) capybara (1.1.1)
mime-types (>= 1.16) mime-types (>= 1.16)
...@@ -41,7 +49,6 @@ GEM ...@@ -41,7 +49,6 @@ GEM
childprocess (0.2.2) childprocess (0.2.2)
ffi (~> 1.0.6) ffi (~> 1.0.6)
cocaine (0.2.0) cocaine (0.2.0)
coderay (1.0.0)
cucumber (1.0.6) cucumber (1.0.6)
builder (>= 2.1.2) builder (>= 2.1.2)
diff-lcs (>= 1.1.2) diff-lcs (>= 1.1.2)
...@@ -65,31 +72,27 @@ GEM ...@@ -65,31 +72,27 @@ GEM
formatador (0.2.1) formatador (0.2.1)
gherkin (2.4.21) gherkin (2.4.21)
json (>= 1.4.6) json (>= 1.4.6)
httparty (0.8.1)
multi_json
multi_xml
i18n (0.6.0) i18n (0.6.0)
json (1.6.1) json (1.6.1)
json_pure (1.6.1) json_pure (1.6.1)
metaclass (0.0.1) metaclass (0.0.1)
method_source (0.6.5)
ruby_parser (>= 2.0.5)
mime-types (1.16) mime-types (1.16)
mocha (0.10.0) mocha (0.10.0)
metaclass (~> 0.0.1) metaclass (~> 0.0.1)
multi_json (1.0.3) multi_json (1.0.3)
multi_xml (0.4.1)
net-scp (1.0.4) net-scp (1.0.4)
net-ssh (>= 1.99.1) net-ssh (>= 1.99.1)
net-ssh (2.1.4) net-ssh (2.1.4)
nokogiri (1.5.0) nokogiri (1.5.0)
pry (0.9.6)
coderay (>= 0.9.8)
method_source (>= 0.6.5)
ruby_parser (>= 2.0.5)
slop (~> 2.1.0)
rack (1.3.3) rack (1.3.3)
rack-test (0.6.1) rack-test (0.6.1)
rack (>= 1.0) rack (>= 1.0)
rake (0.9.2) rake (0.9.2)
rdiscount (1.6.8) rdiscount (1.6.8)
rdoc (3.9.4)
rspec (2.6.0) rspec (2.6.0)
rspec-core (~> 2.6.0) rspec-core (~> 2.6.0)
rspec-expectations (~> 2.6.0) rspec-expectations (~> 2.6.0)
...@@ -99,21 +102,17 @@ GEM ...@@ -99,21 +102,17 @@ GEM
diff-lcs (~> 1.1.2) diff-lcs (~> 1.1.2)
rspec-mocks (2.6.0) rspec-mocks (2.6.0)
ruby-hmac (0.4.0) ruby-hmac (0.4.0)
ruby_parser (2.3.1)
sexp_processor (~> 3.0)
rubyzip (0.9.4) rubyzip (0.9.4)
selenium-webdriver (2.7.0) selenium-webdriver (2.7.0)
childprocess (>= 0.2.1) childprocess (>= 0.2.1)
ffi (>= 1.0.7) ffi (>= 1.0.7)
json_pure json_pure
rubyzip rubyzip
sexp_processor (3.0.7)
shoulda (2.11.3) shoulda (2.11.3)
slop (2.1.0)
sqlite3 (1.3.4) sqlite3 (1.3.4)
term-ansicolor (1.0.6) term-ansicolor (1.0.6)
tzinfo (0.3.29) tzinfo (0.3.31)
xml-simple (1.1.0) uuidtools (2.1.2)
xpath (0.1.4) xpath (0.1.4)
nokogiri (~> 1.3) nokogiri (~> 1.3)
...@@ -121,10 +120,9 @@ PLATFORMS ...@@ -121,10 +120,9 @@ PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
activerecord appraisal (~> 0.4.0)
appraisal
aruba aruba
aws-s3 aws-sdk
bundler bundler
capybara capybara
cocaine (~> 0.2) cocaine (~> 0.2)
...@@ -132,10 +130,8 @@ DEPENDENCIES ...@@ -132,10 +130,8 @@ DEPENDENCIES
fakeweb fakeweb
fog fog
jruby-openssl jruby-openssl
mime-types
mocha mocha
pry paperclip!
rake rake
rdoc
shoulda shoulda
sqlite3 (~> 1.3.4) sqlite3 (~> 1.3.4)
...@@ -10,7 +10,7 @@ Given /^I generate a new rails application$/ do ...@@ -10,7 +10,7 @@ Given /^I generate a new rails application$/ do
gem "sqlite3" gem "sqlite3"
gem "capybara" gem "capybara"
gem "gherkin" gem "gherkin"
gem "aws-s3" gem "aws-sdk"
""" """
And I configure the application to use "paperclip" from this project And I configure the application to use "paperclip" from this project
And I reset Bundler environment variable And I reset Bundler environment variable
......
When /^I attach the file "([^"]*)" to "([^"]*)" on S3$/ do |file_path, field| When /^I attach the file "([^"]*)" to "([^"]*)" on S3$/ do |file_path, field|
definition = User.attachment_definitions[field.downcase.to_sym] definition = User.attachment_definitions[field.downcase.to_sym]
path = "http://s3.amazonaws.com/paperclip#{definition[:path]}" path = "https://paperclip.s3.amazonaws.com#{definition[:path]}"
path.gsub!(':filename', File.basename(file_path)) path.gsub!(':filename', File.basename(file_path))
path.gsub!(/:([^\/\.]+)/) do |match| path.gsub!(/:([^\/\.]+)/) do |match|
"([^\/\.]+)" "([^\/\.]+)"
......
PATH
remote: ../
specs:
paperclip (2.4.5)
activerecord (>= 2.3.0)
activesupport (>= 2.3.2)
cocaine (>= 0.0.2)
mime-types
GEM
remote: http://rubygems.org/
specs:
actionmailer (2.3.14)
actionpack (= 2.3.14)
actionpack (2.3.14)
activesupport (= 2.3.14)
rack (~> 1.1.0)
activerecord (2.3.14)
activesupport (= 2.3.14)
activeresource (2.3.14)
activesupport (= 2.3.14)
activesupport (2.3.14)
appraisal (0.4.0)
bundler
rake
aruba (0.4.6)
bcat (>= 0.6.1)
childprocess (>= 0.2.0)
cucumber (>= 1.0.2)
rdiscount (>= 1.6.8)
rspec (>= 2.6.0)
aws-s3 (0.6.2)
builder
mime-types
xml-simple
bcat (0.6.2)
rack (~> 1.0)
builder (3.0.0)
capybara (1.1.1)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
selenium-webdriver (~> 2.0)
xpath (~> 0.1.4)
childprocess (0.2.2)
ffi (~> 1.0.6)
cocaine (0.2.0)
cucumber (1.0.6)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
gherkin (~> 2.4.18)
json (>= 1.4.6)
term-ansicolor (>= 1.0.6)
diff-lcs (1.1.3)
excon (0.7.4)
fakeweb (1.3.0)
ffi (1.0.9)
fog (1.0.0)
builder
excon (~> 0.7.3)
formatador (~> 0.2.0)
mime-types
multi_json (~> 1.0.3)
net-scp (~> 1.0.4)
net-ssh (~> 2.1.4)
nokogiri (~> 1.5.0)
ruby-hmac
formatador (0.2.1)
gherkin (2.4.21)
json (>= 1.4.6)
json (1.6.1)
json_pure (1.6.1)
metaclass (0.0.1)
mime-types (1.16)
mocha (0.10.0)
metaclass (~> 0.0.1)
multi_json (1.0.3)
net-scp (1.0.4)
net-ssh (>= 1.99.1)
net-ssh (2.1.4)
nokogiri (1.5.0)
rack (1.1.2)
rack-test (0.6.1)
rack (>= 1.0)
rails (2.3.14)
actionmailer (= 2.3.14)
actionpack (= 2.3.14)
activerecord (= 2.3.14)
activeresource (= 2.3.14)
activesupport (= 2.3.14)
rake (>= 0.8.3)
rake (0.9.2)
rdiscount (1.6.8)
rspec (2.6.0)
rspec-core (~> 2.6.0)
rspec-expectations (~> 2.6.0)
rspec-mocks (~> 2.6.0)
rspec-core (2.6.4)
rspec-expectations (2.6.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.6.0)
ruby-hmac (0.4.0)
rubyzip (0.9.4)
selenium-webdriver (2.7.0)
childprocess (>= 0.2.1)
ffi (>= 1.0.7)
json_pure
rubyzip
shoulda (2.11.3)
sqlite3 (1.3.4)
term-ansicolor (1.0.6)
xml-simple (1.1.0)
xpath (0.1.4)
nokogiri (~> 1.3)
PLATFORMS
ruby
DEPENDENCIES
appraisal (~> 0.4.0)
aruba
aws-s3
bundler
capybara
cocaine (~> 0.2)
cucumber (~> 1.0.0)
fakeweb
fog
jruby-openssl
mocha
paperclip!
rails (~> 2.3.14)
rake
shoulda
sqlite3 (~> 1.3.4)
PATH
remote: ../
specs:
paperclip (2.4.5)
activerecord (>= 2.3.0)
activesupport (>= 2.3.2)
cocaine (>= 0.0.2)
mime-types
GEM
remote: http://rubygems.org/
specs:
abstract (1.0.0)
actionmailer (3.0.10)
actionpack (= 3.0.10)
mail (~> 2.2.19)
actionpack (3.0.10)
activemodel (= 3.0.10)
activesupport (= 3.0.10)
builder (~> 2.1.2)
erubis (~> 2.6.6)
i18n (~> 0.5.0)
rack (~> 1.2.1)
rack-mount (~> 0.6.14)
rack-test (~> 0.5.7)
tzinfo (~> 0.3.23)
activemodel (3.0.10)
activesupport (= 3.0.10)
builder (~> 2.1.2)
i18n (~> 0.5.0)
activerecord (3.0.10)
activemodel (= 3.0.10)
activesupport (= 3.0.10)
arel (~> 2.0.10)
tzinfo (~> 0.3.23)
activeresource (3.0.10)
activemodel (= 3.0.10)
activesupport (= 3.0.10)
activesupport (3.0.10)
appraisal (0.4.0)
bundler
rake
arel (2.0.10)
aruba (0.4.6)
bcat (>= 0.6.1)
childprocess (>= 0.2.0)
cucumber (>= 1.0.2)
rdiscount (>= 1.6.8)
rspec (>= 2.6.0)
aws-s3 (0.6.2)
builder
mime-types
xml-simple
bcat (0.6.2)
rack (~> 1.0)
builder (2.1.2)
capybara (1.1.1)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
selenium-webdriver (~> 2.0)
xpath (~> 0.1.4)
childprocess (0.2.2)
ffi (~> 1.0.6)
cocaine (0.2.0)
cucumber (1.0.6)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
gherkin (~> 2.4.18)
json (>= 1.4.6)
term-ansicolor (>= 1.0.6)
diff-lcs (1.1.3)
erubis (2.6.6)
abstract (>= 1.0.0)
excon (0.7.4)
fakeweb (1.3.0)
ffi (1.0.9)
fog (1.0.0)
builder
excon (~> 0.7.3)
formatador (~> 0.2.0)
mime-types
multi_json (~> 1.0.3)
net-scp (~> 1.0.4)
net-ssh (~> 2.1.4)
nokogiri (~> 1.5.0)
ruby-hmac
formatador (0.2.1)
gherkin (2.4.21)
json (>= 1.4.6)
i18n (0.5.0)
json (1.6.1)
json_pure (1.6.1)
mail (2.2.19)
activesupport (>= 2.3.6)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
metaclass (0.0.1)
mime-types (1.16)
mocha (0.10.0)
metaclass (~> 0.0.1)
multi_json (1.0.3)
net-scp (1.0.4)
net-ssh (>= 1.99.1)
net-ssh (2.1.4)
nokogiri (1.5.0)
polyglot (0.3.2)
rack (1.2.4)
rack-mount (0.6.14)
rack (>= 1.0.0)
rack-test (0.5.7)
rack (>= 1.0)
rails (3.0.10)
actionmailer (= 3.0.10)
actionpack (= 3.0.10)
activerecord (= 3.0.10)
activeresource (= 3.0.10)
activesupport (= 3.0.10)
bundler (~> 1.0)
railties (= 3.0.10)
railties (3.0.10)
actionpack (= 3.0.10)
activesupport (= 3.0.10)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.4)
rake (0.9.2)
rdiscount (1.6.8)
rdoc (3.9.4)
rspec (2.6.0)
rspec-core (~> 2.6.0)
rspec-expectations (~> 2.6.0)
rspec-mocks (~> 2.6.0)
rspec-core (2.6.4)
rspec-expectations (2.6.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.6.0)
ruby-hmac (0.4.0)
rubyzip (0.9.4)
selenium-webdriver (2.7.0)
childprocess (>= 0.2.1)
ffi (>= 1.0.7)
json_pure
rubyzip
shoulda (2.11.3)
sqlite3 (1.3.4)
term-ansicolor (1.0.6)
thor (0.14.6)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.30)
xml-simple (1.1.0)
xpath (0.1.4)
nokogiri (~> 1.3)
PLATFORMS
ruby
DEPENDENCIES
appraisal (~> 0.4.0)
aruba
aws-s3
bundler
capybara
cocaine (~> 0.2)
cucumber (~> 1.0.0)
fakeweb
fog
jruby-openssl
mocha
paperclip!
rails (~> 3.0.10)
rake
shoulda
sqlite3 (~> 1.3.4)
PATH
remote: ../
specs:
paperclip (2.4.5)
activerecord (>= 2.3.0)
activesupport (>= 2.3.2)
cocaine (>= 0.0.2)
mime-types
GEM
remote: http://rubygems.org/
specs:
actionmailer (3.1.0)
actionpack (= 3.1.0)
mail (~> 2.3.0)
actionpack (3.1.0)
activemodel (= 3.1.0)
activesupport (= 3.1.0)
builder (~> 3.0.0)
erubis (~> 2.7.0)
i18n (~> 0.6)
rack (~> 1.3.2)
rack-cache (~> 1.0.3)
rack-mount (~> 0.8.2)
rack-test (~> 0.6.1)
sprockets (~> 2.0.0)
activemodel (3.1.0)
activesupport (= 3.1.0)
bcrypt-ruby (~> 3.0.0)
builder (~> 3.0.0)
i18n (~> 0.6)
activerecord (3.1.0)
activemodel (= 3.1.0)
activesupport (= 3.1.0)
arel (~> 2.2.1)
tzinfo (~> 0.3.29)
activeresource (3.1.0)
activemodel (= 3.1.0)
activesupport (= 3.1.0)
activesupport (3.1.0)
multi_json (~> 1.0)
appraisal (0.4.0)
bundler
rake
arel (2.2.1)
aruba (0.4.6)
bcat (>= 0.6.1)
childprocess (>= 0.2.0)
cucumber (>= 1.0.2)
rdiscount (>= 1.6.8)
rspec (>= 2.6.0)
aws-s3 (0.6.2)
builder
mime-types
xml-simple
bcat (0.6.2)
rack (~> 1.0)
bcrypt-ruby (3.0.1)
builder (3.0.0)
capybara (1.1.1)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
selenium-webdriver (~> 2.0)
xpath (~> 0.1.4)
childprocess (0.2.2)
ffi (~> 1.0.6)
cocaine (0.2.0)
cucumber (1.0.6)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
gherkin (~> 2.4.18)
json (>= 1.4.6)
term-ansicolor (>= 1.0.6)
diff-lcs (1.1.3)
erubis (2.7.0)
excon (0.7.4)
fakeweb (1.3.0)
ffi (1.0.9)
fog (1.0.0)
builder
excon (~> 0.7.3)
formatador (~> 0.2.0)
mime-types
multi_json (~> 1.0.3)
net-scp (~> 1.0.4)
net-ssh (~> 2.1.4)
nokogiri (~> 1.5.0)
ruby-hmac
formatador (0.2.1)
gherkin (2.4.21)
json (>= 1.4.6)
hike (1.2.1)
i18n (0.6.0)
json (1.6.1)
json_pure (1.6.1)
mail (2.3.0)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
metaclass (0.0.1)
mime-types (1.16)
mocha (0.10.0)
metaclass (~> 0.0.1)
multi_json (1.0.3)
net-scp (1.0.4)
net-ssh (>= 1.99.1)
net-ssh (2.1.4)
nokogiri (1.5.0)
polyglot (0.3.2)
rack (1.3.3)
rack-cache (1.0.3)
rack (>= 0.4)
rack-mount (0.8.3)
rack (>= 1.0.0)
rack-ssl (1.3.2)
rack
rack-test (0.6.1)
rack (>= 1.0)
rails (3.1.0)
actionmailer (= 3.1.0)
actionpack (= 3.1.0)
activerecord (= 3.1.0)
activeresource (= 3.1.0)
activesupport (= 3.1.0)
bundler (~> 1.0)
railties (= 3.1.0)
railties (3.1.0)
actionpack (= 3.1.0)
activesupport (= 3.1.0)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.6)
rake (0.9.2)
rdiscount (1.6.8)
rdoc (3.9.4)
rspec (2.6.0)
rspec-core (~> 2.6.0)
rspec-expectations (~> 2.6.0)
rspec-mocks (~> 2.6.0)
rspec-core (2.6.4)
rspec-expectations (2.6.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.6.0)
ruby-hmac (0.4.0)
rubyzip (0.9.4)
selenium-webdriver (2.7.0)
childprocess (>= 0.2.1)
ffi (>= 1.0.7)
json_pure
rubyzip
shoulda (2.11.3)
sprockets (2.0.1)
hike (~> 1.2)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.4)
term-ansicolor (1.0.6)
thor (0.14.6)
tilt (1.3.3)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.30)
xml-simple (1.1.0)
xpath (0.1.4)
nokogiri (~> 1.3)
PLATFORMS
ruby
DEPENDENCIES
appraisal (~> 0.4.0)
aruba
aws-s3
bundler
capybara
cocaine (~> 0.2)
cucumber (~> 1.0.0)
fakeweb
fog
jruby-openssl
mocha
paperclip!
rails (~> 3.1.0)
rake
shoulda
sqlite3 (~> 1.3.4)
...@@ -67,12 +67,21 @@ module Paperclip ...@@ -67,12 +67,21 @@ module Paperclip
# S3 (strictly speaking) does not support directories, you can still use a / to # S3 (strictly speaking) does not support directories, you can still use a / to
# separate parts of your file name. # separate parts of your file name.
# * +s3_host_name+: If you are using your bucket in Tokyo region etc, write host_name. # * +s3_host_name+: If you are using your bucket in Tokyo region etc, write host_name.
# * +s3_metadata+: These key/value pairs will be stored with the
# object. This option works by prefixing each key with
# "x-amz-meta-" before sending it as a header on the object
# upload request.
# * +s3_storage_class+: If this option is set to
# <tt>:reduced_redundancy</tt>, the object will be stored using Reduced
# Redundancy Storage. RRS enables customers to reduce their
# costs by storing non-critical, reproducible data at lower
# levels of redundancy than Amazon S3's standard storage.
module S3 module S3
def self.extended base def self.extended base
begin begin
require 'aws/s3' require 'aws/s3'
rescue LoadError => e rescue LoadError => e
e.message << " (You may need to install the aws-s3 gem)" e.message << " (You may need to install the aws-sdk gem)"
raise e raise e
end unless defined?(AWS::S3) end unless defined?(AWS::S3)
...@@ -85,7 +94,19 @@ module Paperclip ...@@ -85,7 +94,19 @@ module Paperclip
permission = permission.call(attachment, style) if permission.is_a?(Proc) permission = permission.call(attachment, style) if permission.is_a?(Proc)
(permission == :public_read) ? 'http' : 'https' (permission == :public_read) ? 'http' : 'https'
end end
@s3_headers = @options[:s3_headers] || {} @s3_metadata = @options[:s3_metadata] || {}
@s3_headers = (@options[:s3_headers] || {}).inject({}) do |headers,(name,value)|
case name.to_s
when /^x-amz-meta-(.*)/i
@s3_metadata[$1.downcase] = value
else
name = name.to_s.downcase.sub(/^x-amz-/,'').tr("-","_").to_sym
headers[name] = value
end
headers
end
@s3_headers[:storage_class] = @options[:s3_storage_class] if @options[:s3_storage_class]
unless @options[:url].to_s.match(/^:s3.*url$/) || @options[:url] == ":asset_host" unless @options[:url].to_s.match(/^:s3.*url$/) || @options[:url] == ":asset_host"
@options[:path] = @options[:path].gsub(/:url/, @options[:url]).gsub(/^:rails_root\/public\/system/, '') @options[:path] = @options[:path].gsub(/:url/, @options[:url]).gsub(/^:rails_root\/public\/system/, '')
...@@ -94,9 +115,6 @@ module Paperclip ...@@ -94,9 +115,6 @@ module Paperclip
@options[:url] = @options[:url].inspect if @options[:url].is_a?(Symbol) @options[:url] = @options[:url].inspect if @options[:url].is_a?(Symbol)
@http_proxy = @options[:http_proxy] || nil @http_proxy = @options[:http_proxy] || nil
if @http_proxy
@s3_options.merge!({:proxy => @http_proxy})
end
end end
Paperclip.interpolates(:s3_alias_url) do |attachment, style| Paperclip.interpolates(:s3_alias_url) do |attachment, style|
"#{attachment.s3_protocol(style)}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}" "#{attachment.s3_protocol(style)}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
...@@ -113,7 +131,9 @@ module Paperclip ...@@ -113,7 +131,9 @@ module Paperclip
end end
def expiring_url(time = 3600, style_name = default_style) def expiring_url(time = 3600, style_name = default_style)
path.nil? ? nil : s3_object.url_for(path(style_name), bucket_name, :expires_in => time, :use_ssl => (s3_protocol(style_name) == 'https')) if path
s3_object(style_name).url_for(:read, :expires => time, :secure => use_secure_protocol?(style_name)).to_s
end
end end
def s3_credentials def s3_credentials
...@@ -133,7 +153,39 @@ module Paperclip ...@@ -133,7 +153,39 @@ module Paperclip
def bucket_name def bucket_name
@bucket = @options[:bucket] || s3_credentials[:bucket] @bucket = @options[:bucket] || s3_credentials[:bucket]
@bucket = @bucket.call(self) if @bucket.is_a?(Proc) @bucket = @bucket.call(self) if @bucket.is_a?(Proc)
@bucket @bucket or raise ArgumentError, "missing required :bucket option"
end
def s3_interface
@s3_interface ||= begin
config = { :s3_endpoint => s3_host_name }
if using_http_proxy?
proxy_opts = { :host => http_proxy_host }
proxy_opts[:port] = http_proxy_port if http_proxy_port
if http_proxy_user
userinfo = http_proxy_user.to_s
userinfo += ":#{http_proxy_password}" if http_proxy_password
proxy_opts[:userinfo] = userinfo
end
config[:proxy_uri] = URI::HTTP.build(proxy_opts)
end
[:access_key_id, :secret_access_key].each do |opt|
config[opt] = s3_credentials[opt] if s3_credentials[opt]
end
AWS::S3.new(config.merge(@s3_options))
end
end
def s3_bucket
@s3_bucket ||= s3_interface.buckets[bucket_name]
end
def s3_object style_name = default_style
s3_bucket.objects[path(style_name).sub(%r{^/},'')]
end end
def using_http_proxy? def using_http_proxy?
...@@ -173,7 +225,7 @@ module Paperclip ...@@ -173,7 +225,7 @@ module Paperclip
def exists?(style = default_style) def exists?(style = default_style)
if original_filename if original_filename
s3_object.exists?(path(style), bucket_name) s3_object(style).exists?
else else
false false
end end
...@@ -202,30 +254,31 @@ module Paperclip ...@@ -202,30 +254,31 @@ module Paperclip
basename = File.basename(filename, extname) basename = File.basename(filename, extname)
file = Tempfile.new([basename, extname]) file = Tempfile.new([basename, extname])
file.binmode file.binmode
file.write(s3_object.value(path(style), bucket_name)) file.write(s3_object(style).read)
file.rewind file.rewind
return file return file
end end
def create_bucket def create_bucket
s3_bucket.create(bucket_name) s3_interface.buckets.create(bucket_name)
end end
def flush_writes #:nodoc: def flush_writes #:nodoc:
@queued_for_write.each do |style, file| @queued_for_write.each do |style, file|
begin begin
log("saving #{path(style)}") log("saving #{path(style)}")
s3_object.store(path(style), acl = @s3_permissions[style] || @s3_permissions[:default]
file, acl = acl.call(self, style) if acl.respond_to?(:call)
bucket_name, write_options = {
{:content_type => file.content_type.to_s.strip, :content_type => file.content_type.to_s.strip,
:access => s3_permissions(style), :acl => acl
}.merge(@s3_headers)) }
rescue AWS::S3::NoSuchBucket => e write_options[:metadata] = @s3_metadata unless @s3_metadata.empty?
write_options.merge!(@s3_headers)
s3_object(style).write(file, write_options)
rescue AWS::S3::Errors::NoSuchBucket => e
create_bucket create_bucket
retry retry
rescue AWS::S3::ResponseError => e
raise
end end
end end
...@@ -238,8 +291,8 @@ module Paperclip ...@@ -238,8 +291,8 @@ module Paperclip
@queued_for_delete.each do |path| @queued_for_delete.each do |path|
begin begin
log("deleting #{path}") log("deleting #{path}")
s3_object.delete(path, bucket_name) s3_bucket.objects[path.sub(%r{^/},'')].delete
rescue AWS::S3::ResponseError rescue AWS::Errors::Base => e
# Ignore this. # Ignore this.
end end
end end
...@@ -260,18 +313,6 @@ module Paperclip ...@@ -260,18 +313,6 @@ module Paperclip
end end
private :find_credentials private :find_credentials
def s3_object
establish_connection!
AWS::S3::S3Object
end
private :s3_object
def s3_bucket
establish_connection!
AWS::S3::Bucket
end
private :s3_bucket
def establish_connection! def establish_connection!
@connection ||= AWS::S3::Base.establish_connection!( @s3_options.merge( @connection ||= AWS::S3::Base.establish_connection!( @s3_options.merge(
:access_key_id => s3_credentials[:access_key_id], :access_key_id => s3_credentials[:access_key_id],
...@@ -279,6 +320,11 @@ module Paperclip ...@@ -279,6 +320,11 @@ module Paperclip
)) ))
end end
private :establish_connection! private :establish_connection!
def use_secure_protocol?(style_name)
s3_protocol(style_name) == "https"
end
private :use_secure_protocol?
end end
end end
end end
...@@ -28,7 +28,7 @@ Gem::Specification.new do |s| ...@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
s.add_development_dependency('shoulda') s.add_development_dependency('shoulda')
s.add_development_dependency('appraisal', '~> 0.4.0') s.add_development_dependency('appraisal', '~> 0.4.0')
s.add_development_dependency('mocha') s.add_development_dependency('mocha')
s.add_development_dependency('aws-s3') s.add_development_dependency('aws-sdk')
s.add_development_dependency('sqlite3', '~> 1.3.4') s.add_development_dependency('sqlite3', '~> 1.3.4')
s.add_development_dependency('cucumber', '~> 1.0.0') s.add_development_dependency('cucumber', '~> 1.0.0')
s.add_development_dependency('aruba') s.add_development_dependency('aruba')
......
require './test/helper' require './test/helper'
require 'aws/s3' require 'aws'
unless ENV["S3_TEST_BUCKET"].blank? unless ENV["S3_TEST_BUCKET"].blank?
class S3LiveTest < Test::Unit::TestCase class S3LiveTest < Test::Unit::TestCase
...@@ -72,7 +72,7 @@ unless ENV["S3_TEST_BUCKET"].blank? ...@@ -72,7 +72,7 @@ unless ENV["S3_TEST_BUCKET"].blank?
rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" }, rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" },
:storage => :s3, :storage => :s3,
:bucket => ENV["S3_TEST_BUCKET"], :bucket => ENV["S3_TEST_BUCKET"],
:s3_credentials => File.new(File.join(File.dirname(__FILE__), "..", "s3.yml")) :s3_credentials => File.new(File.join(File.dirname(__FILE__), "..", "fixtures", "s3.yml"))
Dummy.delete_all Dummy.delete_all
@dummy = Dummy.new @dummy = Dummy.new
...@@ -120,9 +120,9 @@ unless ENV["S3_TEST_BUCKET"].blank? ...@@ -120,9 +120,9 @@ unless ENV["S3_TEST_BUCKET"].blank?
assert_match /.+\/question\?mark\.png/, @dummy.avatar.path assert_match /.+\/question\?mark\.png/, @dummy.avatar.path
end end
# should "return an escaped version for url" do should "return an escaped version for url" do
# assert_match /.+\/question%3Fmark\.png/, @dummy.avatar.url assert_match /.+\/question%3Fmark\.png/, @dummy.avatar.url
# end end
should "be accessible" do should "be accessible" do
assert_match /200 OK/, `curl -I "#{@dummy.avatar.url}"` assert_match /200 OK/, `curl -I "#{@dummy.avatar.url}"`
......
require './test/helper' require './test/helper'
require 'aws/s3' require 'aws'
AWS.stub!
AWS.config(:access_key_id => "TESTKEY", :secret_access_key => "TESTSECRET")
class S3Test < Test::Unit::TestCase class S3Test < Test::Unit::TestCase
def rails_env(env) def rails_env(env)
...@@ -11,7 +14,6 @@ class S3Test < Test::Unit::TestCase ...@@ -11,7 +14,6 @@ class S3Test < Test::Unit::TestCase
context "Parsing S3 credentials" do context "Parsing S3 credentials" do
setup do setup do
@proxy_settings = {:host => "127.0.0.1", :port => 8888, :user => "foo", :password => "bar"} @proxy_settings = {:host => "127.0.0.1", :port => 8888, :user => "foo", :password => "bar"}
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:bucket => "testing", :bucket => "testing",
:http_proxy => @proxy_settings, :http_proxy => @proxy_settings,
...@@ -51,9 +53,73 @@ class S3Test < Test::Unit::TestCase ...@@ -51,9 +53,73 @@ class S3Test < Test::Unit::TestCase
end end
context "missing :bucket option" do
setup do
rebuild_model :storage => :s3,
#:bucket => "testing", # intentionally left out
:s3_credentials => {:not => :important}
@dummy = Dummy.new
@dummy.avatar = StringIO.new(".")
end
should "raise an argument error" do
exception = assert_raise(ArgumentError) { @dummy.save }
assert_match /missing required :bucket option/, exception.message
end
end
context ":bucket option via :s3_credentials" do
setup do
rebuild_model :storage => :s3, :s3_credentials => {:bucket => 'testing'}
@dummy = Dummy.new
end
should "populate #bucket_name" do
assert_equal @dummy.avatar.bucket_name, 'testing'
end
end
context ":bucket option" do
setup do
rebuild_model :storage => :s3, :bucket => "testing", :s3_credentials => {}
@dummy = Dummy.new
end
should "populate #bucket_name" do
assert_equal @dummy.avatar.bucket_name, 'testing'
end
end
context "missing :bucket option" do
setup do
rebuild_model :storage => :s3,
#:bucket => "testing", # intentionally left out
:http_proxy => @proxy_settings,
:s3_credentials => {:not => :important}
@dummy = Dummy.new
@dummy.avatar = StringIO.new(".")
end
should "raise an argument error" do
exception = assert_raise(ArgumentError) { @dummy.save }
assert_match /missing required :bucket option/, exception.message
end
end
context "" do context "" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:s3_credentials => {}, :s3_credentials => {},
:bucket => "bucket", :bucket => "bucket",
...@@ -66,11 +132,46 @@ class S3Test < Test::Unit::TestCase ...@@ -66,11 +132,46 @@ class S3Test < Test::Unit::TestCase
should "return a url based on an S3 path" do should "return a url based on an S3 path" do
assert_match %r{^http://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url assert_match %r{^http://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url
end end
should "use the correct bucket" do
assert_equal "bucket", @dummy.avatar.s3_bucket.name
end
should "use the correct key" do
assert_equal "avatars/stringio.txt", @dummy.avatar.s3_object.key
end
end
context "An attachment that uses S3 for storage and has the style in the path" do
setup do
rebuild_model :storage => :s3,
:bucket => "testing",
:path => ":attachment/:style/:basename.:extension",
:styles => {
:thumb => "80x80>"
},
:s3_credentials => {
'access_key_id' => "12345",
'secret_access_key' => "54321"
}
@dummy = Dummy.new
@dummy.avatar = StringIO.new(".")
@avatar = @dummy.avatar
end
should "use an S3 object based on the correct path for the default style" do
assert_equal("avatars/original/stringio.txt", @dummy.avatar.s3_object.key)
end
should "use an S3 object based on the correct path for the custom style" do
assert_equal("avatars/thumb/stringio.txt", @dummy.avatar.s3_object(:thumb).key)
end
end end
context "s3_host_name" do context "s3_host_name" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:s3_credentials => {}, :s3_credentials => {},
:bucket => "bucket", :bucket => "bucket",
...@@ -83,11 +184,14 @@ class S3Test < Test::Unit::TestCase ...@@ -83,11 +184,14 @@ class S3Test < Test::Unit::TestCase
should "return a url based on an :s3_host_name path" do should "return a url based on an :s3_host_name path" do
assert_match %r{^http://s3-ap-northeast-1.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url assert_match %r{^http://s3-ap-northeast-1.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url
end end
should "use the S3 bucket with the correct host name" do
assert_equal "s3-ap-northeast-1.amazonaws.com", @dummy.avatar.s3_bucket.config.s3_endpoint
end
end end
context "An attachment that uses S3 for storage and has styles that return different file types" do context "An attachment that uses S3 for storage and has styles that return different file types" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :styles => { :large => ['500x500#', :jpg] }, rebuild_model :styles => { :large => ['500x500#', :jpg] },
:storage => :s3, :storage => :s3,
:bucket => "bucket", :bucket => "bucket",
...@@ -105,14 +209,21 @@ class S3Test < Test::Unit::TestCase ...@@ -105,14 +209,21 @@ class S3Test < Test::Unit::TestCase
assert_match /.+\/5k.png/, @dummy.avatar.url assert_match /.+\/5k.png/, @dummy.avatar.url
end end
should 'use the correct key for the original file mime type' do
assert_match /.+\/5k.png/, @dummy.avatar.s3_object.key
end
should "return a url containing the correct processed file mime type" do should "return a url containing the correct processed file mime type" do
assert_match /.+\/5k.jpg/, @dummy.avatar.url(:large) assert_match /.+\/5k.jpg/, @dummy.avatar.url(:large)
end end
should "use the correct key for the processed file mime type" do
assert_match /.+\/5k.jpg/, @dummy.avatar.s3_object(:large).key
end
end end
context "An attachment that uses S3 for storage and has spaces in file name" do context "An attachment that uses S3 for storage and has spaces in file name" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :styles => { :large => ['500x500#', :jpg] }, rebuild_model :styles => { :large => ['500x500#', :jpg] },
:storage => :s3, :storage => :s3,
:bucket => "bucket", :bucket => "bucket",
...@@ -136,7 +247,6 @@ class S3Test < Test::Unit::TestCase ...@@ -136,7 +247,6 @@ class S3Test < Test::Unit::TestCase
context "" do context "" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:s3_credentials => {}, :s3_credentials => {},
:bucket => "bucket", :bucket => "bucket",
...@@ -153,7 +263,6 @@ class S3Test < Test::Unit::TestCase ...@@ -153,7 +263,6 @@ class S3Test < Test::Unit::TestCase
context "" do context "" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:s3_credentials => { :s3_credentials => {
:production => { :bucket => "prod_bucket" }, :production => { :bucket => "prod_bucket" },
...@@ -173,7 +282,6 @@ class S3Test < Test::Unit::TestCase ...@@ -173,7 +282,6 @@ class S3Test < Test::Unit::TestCase
context "generating a url with a proc as the host alias" do context "generating a url with a proc as the host alias" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:s3_credentials => { :bucket => "prod_bucket" }, :s3_credentials => { :bucket => "prod_bucket" },
:s3_host_alias => Proc.new{|atch| "cdn#{atch.instance.counter % 4}.example.com"}, :s3_host_alias => Proc.new{|atch| "cdn#{atch.instance.counter % 4}.example.com"},
...@@ -203,7 +311,6 @@ class S3Test < Test::Unit::TestCase ...@@ -203,7 +311,6 @@ class S3Test < Test::Unit::TestCase
context "" do context "" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:s3_credentials => {}, :s3_credentials => {},
:bucket => "bucket", :bucket => "bucket",
...@@ -220,7 +327,6 @@ class S3Test < Test::Unit::TestCase ...@@ -220,7 +327,6 @@ class S3Test < Test::Unit::TestCase
context "Generating a secure url with an expiration" do context "Generating a secure url with an expiration" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:s3_credentials => { :s3_credentials => {
:production => { :bucket => "prod_bucket" }, :production => { :bucket => "prod_bucket" },
...@@ -236,7 +342,9 @@ class S3Test < Test::Unit::TestCase ...@@ -236,7 +342,9 @@ class S3Test < Test::Unit::TestCase
@dummy = Dummy.new @dummy = Dummy.new
@dummy.avatar = StringIO.new(".") @dummy.avatar = StringIO.new(".")
AWS::S3::S3Object.expects(:url_for).with("avatars/stringio.txt", "prod_bucket", { :expires_in => 3600, :use_ssl => true }) object = stub
@dummy.avatar.stubs(:s3_object).returns(object)
object.expects(:url_for).with(:read, :expires => 3600, :secure => true)
@dummy.avatar.expiring_url @dummy.avatar.expiring_url
end end
...@@ -246,9 +354,8 @@ class S3Test < Test::Unit::TestCase ...@@ -246,9 +354,8 @@ class S3Test < Test::Unit::TestCase
end end
end end
context "Generating a url with an expiration" do context "Generating a url with an expiration for each style" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:s3_credentials => { :s3_credentials => {
:production => { :bucket => "prod_bucket" }, :production => { :bucket => "prod_bucket" },
...@@ -263,22 +370,25 @@ class S3Test < Test::Unit::TestCase ...@@ -263,22 +370,25 @@ class S3Test < Test::Unit::TestCase
@dummy = Dummy.new @dummy = Dummy.new
@dummy.avatar = StringIO.new(".") @dummy.avatar = StringIO.new(".")
end
AWS::S3::S3Object.expects(:url_for).with("avatars/original/stringio.txt", "prod_bucket", { :expires_in => 3600, :use_ssl => true }) should "should generate a url for the thumb" do
@dummy.avatar.expiring_url object = stub
@dummy.avatar.stubs(:s3_object).with(:thumb).returns(object)
AWS::S3::S3Object.expects(:url_for).with("avatars/thumb/stringio.txt", "prod_bucket", { :expires_in => 1800, :use_ssl => true }) object.expects(:url_for).with(:read, :expires => 1800, :secure => true)
@dummy.avatar.expiring_url(1800, :thumb) @dummy.avatar.expiring_url(1800, :thumb)
end end
should "should succeed" do should "should generate a url for the default style" do
assert true object = stub
@dummy.avatar.stubs(:s3_object).with(:original).returns(object)
object.expects(:url_for).with(:read, :expires => 1800, :secure => true)
@dummy.avatar.expiring_url(1800)
end end
end end
context "Parsing S3 credentials with a bucket in them" do context "Parsing S3 credentials with a bucket in them" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:s3_credentials => { :s3_credentials => {
:production => { :bucket => "prod_bucket" }, :production => { :bucket => "prod_bucket" },
...@@ -290,18 +400,20 @@ class S3Test < Test::Unit::TestCase ...@@ -290,18 +400,20 @@ class S3Test < Test::Unit::TestCase
should "get the right bucket in production" do should "get the right bucket in production" do
rails_env("production") rails_env("production")
assert_equal "prod_bucket", @dummy.avatar.bucket_name assert_equal "prod_bucket", @dummy.avatar.bucket_name
assert_equal "prod_bucket", @dummy.avatar.s3_bucket.name
end end
should "get the right bucket in development" do should "get the right bucket in development" do
rails_env("development") rails_env("development")
assert_equal "dev_bucket", @dummy.avatar.bucket_name assert_equal "dev_bucket", @dummy.avatar.bucket_name
assert_equal "dev_bucket", @dummy.avatar.s3_bucket.name
end end
end end
context "Parsing S3 credentials with a s3_host_name in them" do context "Parsing S3 credentials with a s3_host_name in them" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:bucket => 'testing',
:s3_credentials => { :s3_credentials => {
:production => { :s3_host_name => "s3-world-end.amazonaws.com" }, :production => { :s3_host_name => "s3-world-end.amazonaws.com" },
:development => { :s3_host_name => "s3-ap-northeast-1.amazonaws.com" } :development => { :s3_host_name => "s3-ap-northeast-1.amazonaws.com" }
...@@ -312,16 +424,19 @@ class S3Test < Test::Unit::TestCase ...@@ -312,16 +424,19 @@ class S3Test < Test::Unit::TestCase
should "get the right s3_host_name in production" do should "get the right s3_host_name in production" do
rails_env("production") rails_env("production")
assert_match %r{^s3-world-end.amazonaws.com}, @dummy.avatar.s3_host_name assert_match %r{^s3-world-end.amazonaws.com}, @dummy.avatar.s3_host_name
assert_match %r{^s3-world-end.amazonaws.com}, @dummy.avatar.s3_bucket.config.s3_endpoint
end end
should "get the right s3_host_name in development" do should "get the right s3_host_name in development" do
rails_env("development") rails_env("development")
assert_match %r{^s3-ap-northeast-1.amazonaws.com}, @dummy.avatar.s3_host_name assert_match %r{^s3-ap-northeast-1.amazonaws.com}, @dummy.avatar.s3_host_name
assert_match %r{^s3-ap-northeast-1.amazonaws.com}, @dummy.avatar.s3_bucket.config.s3_endpoint
end end
should "get the right s3_host_name if the key does not exist" do should "get the right s3_host_name if the key does not exist" do
rails_env("test") rails_env("test")
assert_match %r{^s3.amazonaws.com}, @dummy.avatar.s3_host_name assert_match %r{^s3.amazonaws.com}, @dummy.avatar.s3_host_name
assert_match %r{^s3.amazonaws.com}, @dummy.avatar.s3_bucket.config.s3_endpoint
end end
end end
...@@ -361,7 +476,11 @@ class S3Test < Test::Unit::TestCase ...@@ -361,7 +476,11 @@ class S3Test < Test::Unit::TestCase
context "and saved" do context "and saved" do
setup do setup do
AWS::S3::S3Object.stubs(:store).with(@dummy.avatar.path, anything, 'testing', :content_type => 'image/png', :access => :public_read) object = stub
@dummy.avatar.stubs(:s3_object).returns(object)
object.expects(:write).with(anything,
:content_type => "image/png",
:acl => :public_read)
@dummy.save @dummy.save
end end
...@@ -371,7 +490,6 @@ class S3Test < Test::Unit::TestCase ...@@ -371,7 +490,6 @@ class S3Test < Test::Unit::TestCase
end end
should "delete tempfiles" do should "delete tempfiles" do
AWS::S3::S3Object.stubs(:store).with(@dummy.avatar.path, anything, 'testing', :content_type => 'image/png', :access => :public_read)
File.stubs(:exist?).returns(true) File.stubs(:exist?).returns(true)
Paperclip::Tempfile.any_instance.expects(:close).at_least_once() Paperclip::Tempfile.any_instance.expects(:close).at_least_once()
Paperclip::Tempfile.any_instance.expects(:unlink).at_least_once() Paperclip::Tempfile.any_instance.expects(:unlink).at_least_once()
...@@ -381,11 +499,12 @@ class S3Test < Test::Unit::TestCase ...@@ -381,11 +499,12 @@ class S3Test < Test::Unit::TestCase
context "and saved without a bucket" do context "and saved without a bucket" do
setup do setup do
class AWS::S3::NoSuchBucket < AWS::S3::ResponseError AWS::S3::BucketCollection.any_instance.expects(:create).with("testing")
# Force the class to be created as a proper subclass of ResponseError thanks to AWS::S3's autocreation of exceptions AWS::S3::S3Object.any_instance.stubs(:write).
end raises(AWS::S3::Errors::NoSuchBucket.new(stub,
AWS::S3::Bucket.expects(:create).with("testing") stub(:status => 404,
AWS::S3::S3Object.stubs(:store).raises(AWS::S3::NoSuchBucket.new(:message, :response)).then.returns(true) :body => "<foo/>"))).
then.returns(nil)
@dummy.save @dummy.save
end end
...@@ -396,8 +515,8 @@ class S3Test < Test::Unit::TestCase ...@@ -396,8 +515,8 @@ class S3Test < Test::Unit::TestCase
context "and remove" do context "and remove" do
setup do setup do
AWS::S3::S3Object.stubs(:exists?).returns(true) AWS::S3::S3Object.any_instance.stubs(:exists?).returns(true)
AWS::S3::S3Object.stubs(:delete) AWS::S3::S3Object.any_instance.stubs(:delete)
@dummy.destroy_attached_files @dummy.destroy_attached_files
end end
...@@ -410,7 +529,6 @@ class S3Test < Test::Unit::TestCase ...@@ -410,7 +529,6 @@ class S3Test < Test::Unit::TestCase
context "An attachment with S3 storage and bucket defined as a Proc" do context "An attachment with S3 storage and bucket defined as a Proc" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:bucket => lambda { |attachment| "bucket_#{attachment.instance.other}" }, :bucket => lambda { |attachment| "bucket_#{attachment.instance.other}" },
:s3_credentials => {:not => :important} :s3_credentials => {:not => :important}
...@@ -418,13 +536,14 @@ class S3Test < Test::Unit::TestCase ...@@ -418,13 +536,14 @@ class S3Test < Test::Unit::TestCase
should "get the right bucket name" do should "get the right bucket name" do
assert "bucket_a", Dummy.new(:other => 'a').avatar.bucket_name assert "bucket_a", Dummy.new(:other => 'a').avatar.bucket_name
assert "bucket_a", Dummy.new(:other => 'a').avatar.s3_bucket.name
assert "bucket_b", Dummy.new(:other => 'b').avatar.bucket_name assert "bucket_b", Dummy.new(:other => 'b').avatar.bucket_name
assert "bucket_b", Dummy.new(:other => 'b').avatar.s3_bucket.name
end end
end end
context "An attachment with S3 storage and specific s3 headers set" do context "An attachment with S3 storage and specific s3 headers set" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
rebuild_model :storage => :s3, rebuild_model :storage => :s3,
:bucket => "testing", :bucket => "testing",
:path => ":attachment/:style/:basename.:extension", :path => ":attachment/:style/:basename.:extension",
...@@ -446,13 +565,168 @@ class S3Test < Test::Unit::TestCase ...@@ -446,13 +565,168 @@ class S3Test < Test::Unit::TestCase
context "and saved" do context "and saved" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!) object = stub
AWS::S3::S3Object.stubs(:store).with(@dummy.avatar.path, @dummy.avatar.stubs(:s3_object).returns(object)
anything, object.expects(:write).with(anything,
'testing', :content_type => "image/png",
:content_type => 'image/png', :acl => :public_read,
:access => :public_read, :cache_control => 'max-age=31557600')
'Cache-Control' => 'max-age=31557600') @dummy.save
end
should "succeed" do
assert true
end
end
end
end
context "An attachment with S3 storage and metadata set using header names" do
setup do
rebuild_model :storage => :s3,
:bucket => "testing",
:path => ":attachment/:style/:basename.:extension",
:s3_credentials => {
'access_key_id' => "12345",
'secret_access_key' => "54321"
},
:s3_headers => {'x-amz-meta-color' => 'red'}
end
context "when assigned" do
setup do
@file = File.new(File.join(File.dirname(__FILE__), '..', 'fixtures', '5k.png'), 'rb')
@dummy = Dummy.new
@dummy.avatar = @file
end
teardown { @file.close }
context "and saved" do
setup do
object = stub
@dummy.avatar.stubs(:s3_object).returns(object)
object.expects(:write).with(anything,
:content_type => "image/png",
:acl => :public_read,
:metadata => { "color" => "red" })
@dummy.save
end
should "succeed" do
assert true
end
end
end
end
context "An attachment with S3 storage and metadata set using the :s3_metadata option" do
setup do
rebuild_model :storage => :s3,
:bucket => "testing",
:path => ":attachment/:style/:basename.:extension",
:s3_credentials => {
'access_key_id' => "12345",
'secret_access_key' => "54321"
},
:s3_metadata => { "color" => "red" }
end
context "when assigned" do
setup do
@file = File.new(File.join(File.dirname(__FILE__), '..', 'fixtures', '5k.png'), 'rb')
@dummy = Dummy.new
@dummy.avatar = @file
end
teardown { @file.close }
context "and saved" do
setup do
object = stub
@dummy.avatar.stubs(:s3_object).returns(object)
object.expects(:write).with(anything,
:content_type => "image/png",
:acl => :public_read,
:metadata => { "color" => "red" })
@dummy.save
end
should "succeed" do
assert true
end
end
end
end
context "An attachment with S3 storage and storage class set using the header name" do
setup do
rebuild_model :storage => :s3,
:bucket => "testing",
:path => ":attachment/:style/:basename.:extension",
:s3_credentials => {
'access_key_id' => "12345",
'secret_access_key' => "54321"
},
:s3_headers => { "x-amz-storage-class" => "reduced_redundancy" }
end
context "when assigned" do
setup do
@file = File.new(File.join(File.dirname(__FILE__), '..', 'fixtures', '5k.png'), 'rb')
@dummy = Dummy.new
@dummy.avatar = @file
end
teardown { @file.close }
context "and saved" do
setup do
object = stub
@dummy.avatar.stubs(:s3_object).returns(object)
object.expects(:write).with(anything,
:content_type => "image/png",
:acl => :public_read,
:storage_class => "reduced_redundancy")
@dummy.save
end
should "succeed" do
assert true
end
end
end
end
context "An attachment with S3 storage and storage class set using the :storage_class option" do
setup do
rebuild_model :storage => :s3,
:bucket => "testing",
:path => ":attachment/:style/:basename.:extension",
:s3_credentials => {
'access_key_id' => "12345",
'secret_access_key' => "54321"
},
:s3_storage_class => :reduced_redundancy
end
context "when assigned" do
setup do
@file = File.new(File.join(File.dirname(__FILE__), '..', 'fixtures', '5k.png'), 'rb')
@dummy = Dummy.new
@dummy.avatar = @file
end
teardown { @file.close }
context "and saved" do
setup do
object = stub
@dummy.avatar.stubs(:s3_object).returns(object)
object.expects(:write).with(anything,
:content_type => "image/png",
:acl => :public_read,
:storage_class => :reduced_redundancy)
@dummy.save @dummy.save
end end
...@@ -476,13 +750,12 @@ class S3Test < Test::Unit::TestCase ...@@ -476,13 +750,12 @@ class S3Test < Test::Unit::TestCase
Dummy.delete_all Dummy.delete_all
@dummy = Dummy.new @dummy = Dummy.new
@dummy.avatar.send(:establish_connection!)
end end
should "parse the credentials" do should "parse the credentials" do
assert_equal 'pathname_bucket', @dummy.avatar.bucket_name assert_equal 'pathname_bucket', @dummy.avatar.bucket_name
assert_equal 'pathname_key', AWS::S3::Base.connection.options[:access_key_id] assert_equal 'pathname_key', @dummy.avatar.s3_bucket.config.access_key_id
assert_equal 'pathname_secret', AWS::S3::Base.connection.options[:secret_access_key] assert_equal 'pathname_secret', @dummy.avatar.s3_bucket.config.secret_access_key
end end
end end
...@@ -500,13 +773,12 @@ class S3Test < Test::Unit::TestCase ...@@ -500,13 +773,12 @@ class S3Test < Test::Unit::TestCase
Dummy.delete_all Dummy.delete_all
@dummy = Dummy.new @dummy = Dummy.new
@dummy.avatar.send(:establish_connection!)
end end
should "run the file through ERB" do should "run the file through ERB" do
assert_equal 'env_bucket', @dummy.avatar.bucket_name assert_equal 'env_bucket', @dummy.avatar.bucket_name
assert_equal 'env_key', AWS::S3::Base.connection.options[:access_key_id] assert_equal 'env_key', @dummy.avatar.s3_bucket.config.access_key_id
assert_equal 'env_secret', AWS::S3::Base.connection.options[:secret_access_key] assert_equal 'env_secret', @dummy.avatar.s3_bucket.config.secret_access_key
end end
end end
...@@ -533,12 +805,11 @@ class S3Test < Test::Unit::TestCase ...@@ -533,12 +805,11 @@ class S3Test < Test::Unit::TestCase
context "and saved" do context "and saved" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!) object = stub
AWS::S3::S3Object.expects(:store).with(@dummy.avatar.path, @dummy.avatar.stubs(:s3_object).returns(object)
anything, object.expects(:write).with(anything,
'testing', :content_type => "image/png",
:content_type => 'image/png', :acl => :public_read)
:access => :public_read)
@dummy.save @dummy.save
end end
...@@ -572,12 +843,11 @@ class S3Test < Test::Unit::TestCase ...@@ -572,12 +843,11 @@ class S3Test < Test::Unit::TestCase
context "and saved" do context "and saved" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!) object = stub
AWS::S3::S3Object.expects(:store).with(@dummy.avatar.path, @dummy.avatar.stubs(:s3_object).returns(object)
anything, object.expects(:write).with(anything,
'testing', :content_type => "image/png",
:content_type => 'image/png', :acl => :private)
:access => :private)
@dummy.save @dummy.save
end end
...@@ -617,13 +887,12 @@ class S3Test < Test::Unit::TestCase ...@@ -617,13 +887,12 @@ class S3Test < Test::Unit::TestCase
context "and saved" do context "and saved" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
[:thumb, :original].each do |style| [:thumb, :original].each do |style|
AWS::S3::S3Object.expects(:store).with("avatars/#{style}/5k.png", object = stub
anything, @dummy.avatar.stubs(:s3_object).with(style).returns(object)
'testing', object.expects(:write).with(anything,
:content_type => 'image/png', :content_type => "image/png",
:access => style == :thumb ? :public_read : :private) :acl => style == :thumb ? :public_read : :private)
end end
@dummy.save @dummy.save
end end
...@@ -666,15 +935,12 @@ class S3Test < Test::Unit::TestCase ...@@ -666,15 +935,12 @@ class S3Test < Test::Unit::TestCase
context "and saved" do context "and saved" do
setup do setup do
AWS::S3::Base.stubs(:establish_connection!)
[:thumb, :original].each do |style| [:thumb, :original].each do |style|
AWS::S3::S3Object.expects(:store).with( object = stub
"avatars/#{style}/5k.png", @dummy.avatar.stubs(:s3_object).with(style).returns(object)
anything, object.expects(:write).with(anything,
'testing', :content_type => "image/png",
:content_type => 'image/png', :acl => style == :thumb ? :public_read : :private)
:access => style == :thumb ? :public_read : :private
)
end end
@dummy.save @dummy.save
end end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment