simple_aws 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -3,6 +3,5 @@ rvm:
3
3
  - 1.9.2
4
4
  - 1.9.3
5
5
  - rbx
6
- - ruby-head
7
6
  - ree
8
7
  - jruby
data/README.md CHANGED
@@ -1,26 +1,24 @@
1
- SimpleAWS [![Travis CI Build Status](https://secure.travis-ci.org/jameskilton/simple_aws.png)](http://travis-ci.org/jameskilton/simple_aws)
1
+ SimpleAWS [![Travis CI Build Status](https://secure.travis-ci.org/jasonroelofs/simple_aws.png)](http://travis-ci.org/jasonroelofs/simple_aws)
2
2
  =========
3
3
 
4
4
  A thin, simple, forward compatible Ruby wrapper around the various Amazon Web Service APIs.
5
5
 
6
- What? Why?!
6
+ Why?
7
7
  -----------
8
8
 
9
- Do we really need another Ruby library to talk to Amazon Web Services? Aren't there enough libraries out there that could just use some more help to make them better? What about [fog](http://fog.io), or [aws-sdk](http://rubygems.org/gems/aws-sdk), or [aws](http://rubygems.org/gems/aws), or [right_aws](http://rubygems.org/gems/right_aws)?
10
-
11
- While there are a number of well used libraries, I feel they have all fallen prey to the same two problems: they are far too complex for what they are and they all lack forward compatibility.
9
+ I've used almost all of the various major AWS Ruby libraries out there at one point or another, and ever one of them has left me wanting. Each time I pick a different AWS library, I end up in the same situation: the library itself is too complex with tons of abstraction, and when Amazon updates their API, it's a lot of work to update the library to include those new changes. What if there was a library that didn't suffer from either of these issues?
12
10
 
13
11
  ### Complexity
14
12
 
15
- Every Ruby AWS library in use today is simply too complex. Each one I've tried to use has ended up hurting my productivity as I often found myself diving into the library's code to figure out how to call the API method in question. Instead of just working with Amazon's API, I end up fighting the library, constantly having to re-learn whatever abstraction said library is trying to provide. Every library mentioned either hard-codes parameter lists, has it's own mapping from hash keys to AWS parameters, or wraps up an Object API around everything, leading to confusion and more lost productivity when that abstraction leaks. Software is supposed to be simple to use; it's supposed to make your life easier. I've yet to find an AWS library that does this.
13
+ Every Ruby AWS library I've used is simply too complex and has ended up hurting my productivity. I often found myself diving into the library's code to figure out how to call a given AWS API. Instead of just working with Amazon's API, I end up fighting the library, constantly having to re-learn whatever abstraction said library is trying to provide. They all either hard-code parameters, have their own mapping from hash keys to AWS parameters, or wrap up an Object API around everything, leading to confusion and more lost productivity when that abstraction leaks (which all abstractions do). Software is supposed to be simple to use; it's supposed to make your life easier. I've yet to find an AWS library that does this.
16
14
 
17
15
  ### Forward Compatibility
18
16
 
19
- Outside of the pervasive complexity of these libraries, what finally drove me to create this library is the complete lack of forward compatibility in all of them. Any time I wanted to use a new parameter, new action, or new API, I would need to jump into the library itself to implement the missing pieces. In normal OSS fashion, this is of course to be lauded, contributing back to libraries is what makes software better. However, in the case of API wrappers, this very quickly becomes a frustration.
17
+ Outside of the pervasive complexity of these libraries, what finally drove me to create this library is the complete lack of forward compatibility in all of them. Any time I wanted to use a new parameter, new action, or new API, I would need to jump into the library itself to implement the missing pieces. In normal OSS fashion, this is of course to be lauded, as contributing back to libraries is what makes software better. However, in the case of API wrappers, this very quickly becomes a frustration.
20
18
 
21
- Amazon constantly updates AWS APIs, adding parameters and actions, and at times entire new APIs. An AWS library should work *with* the API in question, not fight against it. The only thing a hard-coded parameter list does is add confusion. When you have to figure out how AWS parameters map to library parameters, or hash keys, your productivity drops. When you have to figure out how an object is calling an AWS library, and how you're supposed to use that object, your productivity drops. Likewise when you finally realize that the library does not currently support the action, parameter, or API you're trying to use at the time, your productivity is now at a complete stop.
19
+ Amazon constantly updates their APIs, adding parameters and actions, and at times entire new APIs. An AWS library should work *with* the API in question, not fight against it. The only thing a hard-coded parameter list does is add confusion. When you have to figure out how AWS parameters map to library parameters, or hash keys, your productivity drops. When you have to figure out how an object is calling an AWS library, and how you're supposed to use that object, your productivity drops. Likewise when you finally realize that the library does not currently support the action, parameter, or API you're trying to use at the time, your productivity is now at a complete stop.
22
20
 
23
- SimpleAWS simply says no, no more leaky abstractions and confusing APIs. Just use the names of the API methods and parameters as defined in Amazon's documentation! If a new parameter is added to the API you're using, just use it. The name SimpleAWS isn't a wish or hope, it is the core philosophy. This library focuses on being a very thin communication layer between your Ruby code and Amazon's AWS APIs. Let SimpleAWS handle the messy communication details so your code can do what it needs to do letting you be more productive.
21
+ SimpleAWS simply says no, no more leaky abstractions and confusing APIs. Just use the names of the API methods and parameters as defined in Amazon's documentation! If a new parameter is added to the API you're using, just use it. The name SimpleAWS isn't a wish or hope, it is the core philosophy. SimpleAWS focuses on being a very thin communication layer between your Ruby code and the AWS APIs. Let SimpleAWS handle the messy communication details so your code can do what it needs to do.
24
22
 
25
23
 
26
24
  Surely SimpleAWS isn't just `curl`?
@@ -44,9 +42,9 @@ ec2 = SimpleAWS::EC2.new key, secret
44
42
  ec2.DescribeInstances
45
43
  ```
46
44
 
47
- ### Parameters
45
+ ### Parameters and Headers
48
46
 
49
- Adding parameters to your method calls follows similar rules, with some Quality of Life improvements. The following three are equivalent:
47
+ Parameters and Headers to actions are sent as defined in the official AWS documentation. They must be strings that exactly match the parameters and headers as defined for each action. For example, the "InstanceId" parameter of EC2's "DescribeInstances" action must be named "InstanceId", not :instance_id. That said, there are some Quality of Life improvements to parameter handling. The following three are equivalent:
50
48
 
51
49
  #### Just Call It
52
50
 
@@ -148,15 +146,14 @@ SimpleAWS is built to work under all major Ruby versions:
148
146
  * 1.9.2
149
147
  * 1.9.3
150
148
  * ree
151
- * HEAD
152
149
  * jruby
153
150
  * rubinius
154
151
 
155
152
  ### Misc Info
156
153
 
157
- Author: Jason Roelofs - [Github](https://github.com/jameskilton) [@jameskilton](http://twitter.com/jameskilton)
154
+ Author: Jason Roelofs - [Github](https://github.com/jasonroelofs) [@jasonroelofs](http://twitter.com/jasonroelofs)
158
155
 
159
- Source: https://github.com/jameskilton/simple_aws
156
+ Source: https://github.com/jasonroelofs/simple_aws
160
157
 
161
- Issues: https://github.com/jameskilton/simple_aws/issues
158
+ Issues: https://github.com/jasonroelofs/simple_aws/issues
162
159
 
@@ -56,7 +56,7 @@ module SimpleAWS
56
56
 
57
57
  end
58
58
 
59
- attr_reader :access_key, :secret_key, :region, :version
59
+ attr_reader :access_key, :secret_key, :region, :version, :debug_to
60
60
 
61
61
  ##
62
62
  # Construct a new access object for the API in question.
@@ -75,6 +75,14 @@ module SimpleAWS
75
75
  @version = self.class.instance_variable_get("@version")
76
76
  end
77
77
 
78
+ ##
79
+ # Flag this API to render debugging information to an IO location.
80
+ # Default is $stdout
81
+ ##
82
+ def debug!(location = $stdout)
83
+ @debug_to = location
84
+ end
85
+
78
86
  ##
79
87
  # Get the full host name for the current API
80
88
  #
@@ -42,7 +42,7 @@ module SimpleAWS
42
42
  protected
43
43
 
44
44
  def send_request(request)
45
- connection = SimpleAWS::Connection.new
45
+ connection = SimpleAWS::Connection.new self
46
46
  connection.call finish_and_sign_request(request)
47
47
  end
48
48
 
@@ -151,7 +151,7 @@ module SimpleAWS
151
151
  request.body = options[:body]
152
152
  end
153
153
 
154
- connection = SimpleAWS::Connection.new
154
+ connection = SimpleAWS::Connection.new self
155
155
  connection.call finish_and_sign_request(request)
156
156
  end
157
157
 
@@ -58,6 +58,10 @@ module SimpleAWS
58
58
  ##
59
59
  class Connection
60
60
 
61
+ def initialize(api)
62
+ @api = api
63
+ end
64
+
61
65
  ##
62
66
  # Send an SimpleAWS::Request to AWS proper, returning an SimpleAWS::Response.
63
67
  # Will raise if the request has an error
@@ -68,7 +72,8 @@ module SimpleAWS
68
72
  request.uri,
69
73
  :query => request.params,
70
74
  :headers => request.headers,
71
- :body => request.body
75
+ :body => request.body,
76
+ :debug_output => @api.debug_to
72
77
  )
73
78
  )
74
79
  end
@@ -1,3 +1,6 @@
1
+ require 'date'
2
+ require 'time'
3
+
1
4
  module SimpleAWS
2
5
 
3
6
  ##
@@ -57,6 +60,10 @@ module SimpleAWS
57
60
  process_array key, value
58
61
  when Hash
59
62
  process_hash key, value
63
+ when Time
64
+ super(key, value.iso8601)
65
+ when Date
66
+ super(key, value.strftime("%Y-%m-%d"))
60
67
  else
61
68
  super
62
69
  end
@@ -239,7 +239,7 @@ module SimpleAWS
239
239
  protected
240
240
 
241
241
  def parse_and_throw_error_from(http_response)
242
- if http_response.parsed_response
242
+ if has_parsed_response? http_response
243
243
  error = parse_error_from http_response.parsed_response
244
244
  else
245
245
  error = { "Message" => http_response.response }
@@ -252,6 +252,11 @@ module SimpleAWS
252
252
  )
253
253
  end
254
254
 
255
+ def has_parsed_response?(response)
256
+ !response.parsed_response.nil? &&
257
+ response.parsed_response != ""
258
+ end
259
+
255
260
  def parse_error_from(body)
256
261
  if body.has_key? "ErrorResponse"
257
262
  body["ErrorResponse"]["Error"]
@@ -1,5 +1,4 @@
1
1
  require 'simple_aws/api'
2
- require 'simple_aws/signing/version3'
3
2
  require 'simple_aws/sts'
4
3
  require 'multi_json'
5
4
 
@@ -80,7 +79,7 @@ module SimpleAWS
80
79
  "Algorithm=HmacSHA256," +
81
80
  "Signature=#{build_signature_for(request)}"
82
81
 
83
- connection = SimpleAWS::Connection.new
82
+ connection = SimpleAWS::Connection.new self
84
83
  connection.call request
85
84
  end
86
85
 
@@ -20,6 +20,10 @@ module SimpleAWS
20
20
  use_https true
21
21
  version "2010-05-08"
22
22
 
23
+ def initialize(key, secret)
24
+ super(key, secret)
25
+ end
26
+
23
27
  include CallTypes::ActionParam
24
28
  include Signing::Version2
25
29
  end
data/lib/simple_aws/s3.rb CHANGED
@@ -128,6 +128,20 @@ module SimpleAWS
128
128
  call :get, path, options
129
129
  end
130
130
 
131
+ ##
132
+ # Send a request using HTTP POST
133
+ #
134
+ # @param path [String] The path of the resource at hand
135
+ # @param options [Hash] Options as defined above
136
+ #
137
+ # @return [SimpleAWS::Response] The results of the request
138
+ #
139
+ # @raise [SimpleAWS::UnsuccessfulResponse, SimpleAWS::UnknownErrorResponse] on response errors
140
+ ##
141
+ def post(path, options = {})
142
+ call :post, path, options
143
+ end
144
+
131
145
  ##
132
146
  # Send a request using HTTP PUT
133
147
  #
@@ -184,7 +198,7 @@ module SimpleAWS
184
198
  def call(method, path, options = {})
185
199
  request = self.build_request method, path, options
186
200
 
187
- connection = SimpleAWS::Connection.new
201
+ connection = SimpleAWS::Connection.new self
188
202
  connection.call finish_and_sign_request(request)
189
203
  end
190
204
 
@@ -234,10 +248,15 @@ module SimpleAWS
234
248
 
235
249
  request.body = options[:body]
236
250
 
237
- if request.body.respond_to?(:read)
238
- request.headers["Content-Type"] ||= "application/octet-stream"
239
- request.headers["Content-Length"] = File.size(request.body).to_s
240
- request.headers["Expect"] = "100-continue"
251
+ if request.body
252
+ request.headers["Content-Length"] = calculate_size_of(request.body).to_s
253
+
254
+ if request.body.respond_to?(:read)
255
+ request.headers["Content-Type"] ||= "application/octet-stream"
256
+ request.headers["Expect"] = "100-continue"
257
+ end
258
+
259
+ request.headers["Content-Type"] ||= "application/x-www-form-urlencoded"
241
260
  end
242
261
 
243
262
  request
@@ -255,6 +274,10 @@ module SimpleAWS
255
274
 
256
275
  protected
257
276
 
277
+ def calculate_size_of(body)
278
+ body.respond_to?(:size) ? body.size : File.size(body)
279
+ end
280
+
258
281
  ##
259
282
  # Build and sign the final request, as per the rules here:
260
283
  # http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html
data/samples/s3.rb CHANGED
@@ -22,6 +22,7 @@ def bad_usage
22
22
  end
23
23
 
24
24
  s3 = SimpleAWS::S3.new ENV["AWS_KEY"], ENV["AWS_SECRET"]
25
+ s3.debug!
25
26
 
26
27
  bucket_name = ARGV[0]
27
28
  file_name = ARGV[1]
@@ -36,8 +37,16 @@ puts "", "First 10 files in #{bucket_name}:", ""
36
37
 
37
38
  bad_usage unless bucket_name
38
39
 
39
- s3.get("/", :bucket => bucket_name, :params => {"max-keys" => 10}).contents.each do |entry|
40
- puts entry.key
40
+ s3.get("/", :bucket => bucket_name, :params => {"max-keys" => 10}).tap do |response|
41
+ # Amazon doesn't include Contents if there are no files
42
+ # Amazon also includes just one entry if there's only one file to be found,
43
+ # where as if there's > 1 then SimpleAWS will be given a proper array.
44
+ # Gotta love XML!
45
+ if response["Contents"]
46
+ [response.contents].flatten.each do |entry|
47
+ puts entry.key
48
+ end
49
+ end
41
50
  end
42
51
 
43
52
  puts "", "Uploading #{file_name} to #{bucket_name}:", ""
data/simple_aws.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "simple_aws"
3
- s.version = "1.2.0"
3
+ s.version = "1.2.1"
4
4
  s.platform = Gem::Platform::RUBY
5
5
  s.authors = ["Jason Roelofs"]
6
6
  s.email = ["jasongroelofs@gmail.com"]
@@ -49,6 +49,20 @@ describe SimpleAWS::API do
49
49
  end
50
50
  end
51
51
 
52
+ describe "#debug" do
53
+ it "allows turning on debugging for the api instance" do
54
+ obj = TestAPI.new "access_key", "secret_key"
55
+ obj.debug!
56
+ obj.debug_to.must_equal $stdout
57
+ end
58
+
59
+ it "allows changing the output location of the debugging output" do
60
+ obj = TestAPI.new "access_key", "secret_key"
61
+ obj.debug! $stderr
62
+ obj.debug_to.must_equal $stderr
63
+ end
64
+ end
65
+
52
66
  describe "#region" do
53
67
  it "uses default region if none given on constructor" do
54
68
  TestAPI.default_region "us-west-1"
@@ -1,4 +1,5 @@
1
1
  require 'test_helper'
2
+ require 'simple_aws/api'
2
3
  require 'simple_aws/core/request'
3
4
  require 'simple_aws/core/response'
4
5
  require 'simple_aws/core/connection'
@@ -8,7 +9,8 @@ describe SimpleAWS::Connection do
8
9
  describe "#call" do
9
10
 
10
11
  before do
11
- @connection = SimpleAWS::Connection.new
12
+ @api = SimpleAWS::API.new "access", "secret"
13
+ @connection = SimpleAWS::Connection.new @api
12
14
  @http_response = stub_everything(:success? => true, :parsed_response => {"value" => {}})
13
15
  end
14
16
 
@@ -68,5 +70,17 @@ describe SimpleAWS::Connection do
68
70
  @connection.call request
69
71
  end
70
72
 
73
+ it "forwards debugging settings from the API to the backend" do
74
+ @api.debug!
75
+
76
+ request = SimpleAWS::Request.new(:get, "host.com", "/")
77
+
78
+ SimpleAWS::HTTP.expects(:get).with {|uri, options|
79
+ options[:debug_output].must_equal $stdout
80
+ }.returns(@http_response)
81
+
82
+ @connection.call request
83
+ end
71
84
  end
85
+
72
86
  end
@@ -104,4 +104,20 @@ describe SimpleAWS::Request do
104
104
  })
105
105
  end
106
106
  end
107
+
108
+ describe "date / time objects" do
109
+ it "converts date objects to ISO8601 format" do
110
+ @request.params["Date"] = Date.parse("2012-02-14")
111
+ @request.params.must_equal({
112
+ "Date" => "2012-02-14"
113
+ })
114
+ end
115
+
116
+ it "converts time objects to ISO8601 format" do
117
+ @request.params["Time"] = Time.utc(2012, 04, 16, 10, 30, 25)
118
+ @request.params.must_equal({
119
+ "Time" => "2012-04-16T10:30:25Z"
120
+ })
121
+ end
122
+ end
107
123
  end
@@ -142,6 +142,19 @@ describe SimpleAWS::Response do
142
142
  error.code.must_equal 404
143
143
  error.message.must_equal " (404): This is a response ok?"
144
144
  end
145
+
146
+ it "handles errors that have an empty parsed_response" do
147
+ @http_response.stubs(:code).returns(500)
148
+ @http_response.stubs(:parsed_response).returns("")
149
+ @http_response.stubs(:response).returns("Not valid")
150
+
151
+ error = lambda {
152
+ response = SimpleAWS::Response.new @http_response
153
+ }.must_raise SimpleAWS::UnsuccessfulResponse
154
+
155
+ error.code.must_equal 500
156
+ error.message.must_equal " (500): Not valid"
157
+ end
145
158
  end
146
159
 
147
160
  describe "successful response parsing and mapping" do
@@ -15,6 +15,12 @@ describe SimpleAWS::IAM do
15
15
  @api.version.must_equal "2010-05-08"
16
16
  end
17
17
 
18
+ it "does not support region selection" do
19
+ lambda {
20
+ SimpleAWS::IAM.new "key", "secret", "us-east-1"
21
+ }.must_raise ArgumentError
22
+ end
23
+
18
24
  describe "API calls" do
19
25
 
20
26
  it "builds and signs calls with ActionParam rules" do
@@ -58,7 +58,7 @@ describe SimpleAWS::S3 do
58
58
 
59
59
  describe "API calls" do
60
60
 
61
- [:get, :put, :delete, :head].each do |method|
61
+ [:get, :post, :put, :delete, :head].each do |method|
62
62
  it "supports the #{method} HTTP method" do
63
63
  SimpleAWS::Connection.any_instance.expects(:call).with do |request|
64
64
  request.method.must_equal method
@@ -141,6 +141,17 @@ describe SimpleAWS::S3 do
141
141
  @api.get "/", :body => file
142
142
  end
143
143
 
144
+ it "calculates size of body that isn't a File (responds to read)" do
145
+ raw_body = StringIO.new "raw data"
146
+
147
+ SimpleAWS::Connection.any_instance.expects(:call).with do |request|
148
+ request.headers["Content-Length"].must_equal "8"
149
+ true
150
+ end
151
+
152
+ @api.get "/", :body => raw_body
153
+ end
154
+
144
155
  it "uses previously set content type if given" do
145
156
  SimpleAWS::Connection.any_instance.expects(:call).with do |request|
146
157
  request.headers["Content-Type"].must_equal(
@@ -153,6 +164,15 @@ describe SimpleAWS::S3 do
153
164
  :headers => {"Content-Type" => "application/pdf"}
154
165
  end
155
166
 
167
+ it "sets the default content-type on post / put if none explicitly given" do
168
+ SimpleAWS::Connection.any_instance.expects(:call).with do |request|
169
+ request.headers["Content-Type"].must_equal "application/x-www-form-urlencoded"
170
+ true
171
+ end
172
+
173
+ @api.put "/", :body => "some random body"
174
+ end
175
+
156
176
  it "signs the given request according to Version 3 rules" do
157
177
  SimpleAWS::Connection.any_instance.expects(:call).with do |request|
158
178
  header = request.headers["Authorization"]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_aws
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-26 00:00:00.000000000 Z
12
+ date: 2012-10-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -148,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
148
  version: '0'
149
149
  requirements: []
150
150
  rubyforge_project:
151
- rubygems_version: 1.8.18
151
+ rubygems_version: 1.8.24
152
152
  signing_key:
153
153
  specification_version: 3
154
154
  summary: The simplest and easiest to use AWS communication library