simple_aws 0.0.1d → 1.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,5 @@ Gemfile.lock
2
2
  doc
3
3
  pkg
4
4
  .bundle
5
+ *.gem
6
+ .yardoc
data/.travis.yml CHANGED
@@ -2,7 +2,7 @@ rvm:
2
2
  - 1.8.7
3
3
  - 1.9.2
4
4
  - 1.9.3
5
- - rbx
5
+ # - rbx
6
6
  - ruby-head
7
7
  - ree
8
- - jruby
8
+ # - jruby
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ gemspec
4
4
 
5
5
  group :development do
6
6
  gem "rake"
7
+ gem "yard"
7
8
  end
8
9
 
9
10
  group :test do
data/MIT_LICENCE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Jason Roelofs
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
20
+
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- SimpleAWS
1
+ SimpleAWS [![Travis CI Build Status](https://secure.travis-ci.org/jameskilton/simple_aws.png)](http://travis-ci.org/jameskilton/simple_aws)
2
2
  =========
3
3
 
4
4
  A thin, simple, forward compatible Ruby wrapper around the various Amazon AWS APIs. Unless otherwise mentioned below, this library will wrap every API available service listed on this page: http://aws.amazon.com/documentation/
@@ -8,23 +8,22 @@ What?! Why?
8
8
 
9
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
10
 
11
- While there are a number of well used libraries, I feel they have all fallen prey to the same two problems, problems that SimpleAWS will not have: complexity and forward incompatibility.
11
+ While there are a number of well used libraries, I feel they have all fallen prey to the same two problems: complexity and forward incompatibility.
12
12
 
13
13
  ### Complexity
14
14
 
15
- Every Ruby AWS library in use today is simply too complex. Every library I've tried to use has ended up hurting my productivity as I often have to dive into the code to find out what the parameter list is, what object I need to work with, or what hash keys map to the actual AWS API parameters. Every library that tries to build an Object Abstraction wrapper on top of AWS's APIs suffers from leaky abstractions, which results in yet more time lost trying to figure out what needs to be called, with what, and what gets returned. Software is supposed to be simple, it's supposed to make your life easier as a developer. I've yet to find an AWS library that does this.
15
+ Every Ruby AWS library in use today is simply too complex. Every library I've tried to use has ended up hurting my productivity as I often have to dive into the code to find out what the parameter list is, what object I need to work with, or what hash keys map to the actual AWS API parameters. Every library that tries to build an Object Abstraction wrapper on top of AWS's APIs suffers from leaky abstractions, which results in yet more time lost trying to figure out what needs to be called, with what, and what gets returned. Software is supposed to be simple, it's supposed to make your life easier. I've yet to find an AWS library that does this.
16
16
 
17
- The name SimpleAWS isn't a wish or hope, it's 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 and you can be more productive doing what you need to do. An AWS library should facilitate using AWS, not abstract it.
17
+ ### Forward Compatibility
18
18
 
19
- It's the Unix philosophy, SimpleAWS does one thing and does it well and nothing else.
19
+ Ignoring the complexity argument above, what finally drove me to create this library is the complete lack of forward compatibility in all existing Ruby AWS libraries. Any time I wanted to use a new parameter, new action, or new API, I would need to jump into the library itself and implement the missing pieces. In the usual OSS circles this doesn't sound that bad, and is in fact praised, but when you're dealing with an API library, this requirement quickly becomes a frustration.
20
20
 
21
- ### Forward Compatibility
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 as you have to keep two things in your mind: what you want to call, and how the library lets you call it. SimpleAWS simply says no, it won't force anything on the user. Use the names of the API methods and parameters and them alone. If a new parameter is added to the API you're using, just use it; SimpleAWS doesn't care, it will just work.
22
22
 
23
- Ignoring the complexity argument above, what finally drove me to create this library is the complete lack of forward compatibility in all existing Ruby AWS libraries. Any time I wanted to use a new parameter, new action, or new API, I would need to jump into the library itself and implement the missing pieces. In general, this doesn't sound that bad, and in many cases expected, but not for an API wrapper library.
23
+ The name SimpleAWS isn't a wish or hope, it's 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.
24
24
 
25
- 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 to a method does is add confusion and more mental disconnects. SimpleAWS simply says no, it won't force anything on the user. Use the names of the API params and them alone. If a new parameter is added to the API you're using, just use it, SimpleAWS doesn't care, it will just work.
25
+ In short, it's the Unix philosophy, SimpleAWS does one thing and does it well and nothing else.
26
26
 
27
- SimpleAWS is the first and (from what I've found) only Ruby AWS library that is forward compatible with nigh any change Amazon could make to their APIs.
28
27
 
29
28
  Surely SimpleAWS isn't just `curl`?
30
29
  -----------------------------------
@@ -35,7 +34,7 @@ What SimpleAWS does do is add some logic to ensure it follows the Principle of L
35
34
 
36
35
  ### Calling
37
36
 
38
- First of all, calling actions are implemented as ruby methods, handled through method_missing. You can call the AWS actions by AWSName or by ruby_name, they both work:
37
+ First of all, calling actions are implemented as ruby methods, handled through mainly through `method_missing` (S3 and CloudFront are the two exceptions). You can call the AWS actions by AWSName or by ruby_name, they both work:
39
38
 
40
39
  ```ruby
41
40
  ec2 = AWS::EC2.new key, secret
@@ -95,34 +94,34 @@ ec2.describe_instances({
95
94
 
96
95
  All requests return an AWS::Response which does a few cleanup tasks on the resulting XML to make it easier to query and to hide some of the worst warts an XML body tends to have.
97
96
 
98
- First of all, whereever AWS returns a list of items, they almost always get wrapped up in an <item> or <member> wrapper tag. Response gets rid of those tags and gives you a flat array to work with.
97
+ First of all, wherever AWS returns a list of items, they almost always get wrapped up in an `<item>` or `<member>` wrapper tag. AWS::Response gets rid of those tags and gives you a flat array to work with.
99
98
 
100
- Second, the resulting XML can have one or two wrapper objects that do nothing but encapsulate the information you're interested in. Response also jumps past these wrapper objects so you have direct access to the data in the response.
99
+ Second, the resulting XML can have one or two wrapper objects that do nothing but encapsulate the information you're interested in. AWS::Response also jumps past these wrapper objects so you have direct access to the data in the response.
101
100
 
102
101
  All response objects are infinitely deep queryable via methods or Hash access. See the samples, tests, and AWS::Response for more details and examples of usage. At all times you can easily inspect the current Response object for the entire response body, or just the rest of the body at the current depth level.
103
102
 
104
103
  Implemented APIs
105
104
  ----------------
106
105
 
107
- These are the following Amazon APIs that SimpleAWS currently handles:
108
-
109
- * S3
110
- * EC2
111
- * ELB
112
- * IAM
113
- * MapReduce
114
- * Auto Scaling
115
- * RDS
116
- * ElastiCache
117
- * Elastic Beanstalk
118
- * CloudFormation
119
- * SNS
120
- * CloudWatch
121
- * Import / Export
122
- * Mechanical Turk
123
- * SQS (Simple Queue Service)
124
- * SES (Simple Email Service)
125
- * CloudFront
106
+ These are the Amazon APIs that SimpleAWS currently handles:
107
+
108
+ * {AWS::S3 S3}
109
+ * {AWS::EC2 EC2}
110
+ * {AWS::ELB ELB}
111
+ * {AWS::IAM IAM}
112
+ * {AWS::MapReduce MapReduce}
113
+ * {AWS::AutoScaling Auto Scaling}
114
+ * {AWS::RDS RDS}
115
+ * {AWS::ElastiCache ElastiCache}
116
+ * {AWS::ElasticBeanstalk Elastic Beanstalk}
117
+ * {AWS::CloudFormation CloudFormation}
118
+ * {AWS::SNS SNS}
119
+ * {AWS::CloudWatch CloudWatch}
120
+ * {AWS::ImportExport ImportExport}
121
+ * {AWS::MechanicalTurk MechanicalTurk}
122
+ * {AWS::SQS SQS}
123
+ * {AWS::SES SES}
124
+ * {AWS::CloudFront CloudFront}
126
125
 
127
126
  Currently Out-Of-Scope
128
127
  ----------------------
@@ -136,11 +135,26 @@ The following API endpoints will not currently be handled in SimpleAWS. These li
136
135
  Project Info
137
136
  ------------
138
137
 
139
- Author: Jason Roelofs (https://github.com/jameskilton)
138
+ ### Rubies
139
+
140
+ SimpleAWS is built to work under all major Ruby versions:
141
+
142
+ * 1.8.7
143
+ * 1.9.2
144
+ * 1.9.3
145
+ * ree
146
+ * HEAD
147
+
148
+ The following are waiting on a release of Nokogiri to fix some issues:
149
+
150
+ * jruby
151
+ * rubinius
152
+
153
+ ### Misc Info
154
+
155
+ Author: Jason Roelofs - [Github](https://github.com/jameskilton) [@jameskilton](http://twitter.com/jameskilton)
140
156
 
141
157
  Source: https://github.com/jameskilton/simple_aws
142
158
 
143
159
  Issues: https://github.com/jameskilton/simple_aws/issues
144
160
 
145
- [![Travis CI Build Status](https://secure.travis-ci.org/jameskilton/simple_aws.png)](http://travis-ci.org/jameskilton/simple_aws)
146
-
@@ -11,6 +11,9 @@ module AWS
11
11
  #
12
12
  # All requests are POST and always through HTTPS. Use the third parameter to
13
13
  # #initialize if you need to talk to a region other than us-east-1.
14
+ #
15
+ # @see AWS::CallTypes::ActionParam Calling rules
16
+ # @see AWS::Response Response handling
14
17
  ##
15
18
  class AutoScaling < API
16
19
  endpoint "autoscaling"
@@ -7,7 +7,14 @@ module AWS
7
7
 
8
8
  ##
9
9
  # Implement call handling to work with the ?Action param, signing the message
10
- # according to whatever Signing module is included along side this module
10
+ # according to whatever Signing module is included along side this module.
11
+ #
12
+ # This module hooks up the `method_missing` functionality as described in the
13
+ # README. To call methods on APIs including this module, simply call a method
14
+ # with either the Ruby-fied name, or the full CamelCase name, and pass in
15
+ # options required as the parameters.
16
+ #
17
+ # All responses will be wrapped up in an {AWS::Response AWS::Response} object.
11
18
  ##
12
19
  module ActionParam
13
20
  ##
@@ -11,6 +11,9 @@ module AWS
11
11
  #
12
12
  # All requests are POST and always through HTTPS. Use the third parameter to
13
13
  # #initialize if you need to talk to a region other than us-east-1.
14
+ #
15
+ # @see AWS::CallTypes::ActionParam Calling rules
16
+ # @see AWS::Response Response handling
14
17
  ##
15
18
  class CloudFormation < API
16
19
  endpoint "cloudformation"
@@ -11,6 +11,9 @@ module AWS
11
11
  #
12
12
  # All requests are POST and always through HTTPS. Use the third parameter to
13
13
  # #initialize if you need to talk to a region other than us-east-1.
14
+ #
15
+ # @see AWS::CallTypes::ActionParam Calling rules
16
+ # @see AWS::Response Response handling
14
17
  ##
15
18
  class CloudWatch < API
16
19
  endpoint "monitoring"
@@ -1,14 +1,36 @@
1
1
  require 'httparty'
2
- require 'httmultiparty'
3
2
  require 'openssl'
4
3
 
5
4
  require 'aws/core/response'
6
5
 
6
+ ##
7
+ # Monkey-patch body_stream usage into HTTParty
8
+ ##
9
+ module HTTParty
10
+ class Request
11
+ private
12
+
13
+ def setup_raw_request
14
+ @raw_request = http_method.new(uri.request_uri)
15
+ if body
16
+ if body.respond_to?(:read)
17
+ @raw_request.body_stream = body
18
+ else
19
+ @raw_request.body = body
20
+ end
21
+ end
22
+ @raw_request.initialize_http_header(options[:headers])
23
+ @raw_request.basic_auth(username, password) if options[:basic_auth]
24
+ setup_digest_auth if options[:digest_auth]
25
+ end
26
+
27
+ end
28
+ end
29
+
7
30
  module AWS
8
31
 
9
32
  class HTTP
10
- include HTTMultiParty
11
- format :xml
33
+ include HTTParty
12
34
  end
13
35
 
14
36
  ##
@@ -181,7 +181,7 @@ module AWS
181
181
  @body = http_response.parsed_response
182
182
  @headers = http_response.headers
183
183
 
184
- if @body
184
+ if @body.is_a?(Hash)
185
185
  inner = @body[@body.keys.first]
186
186
  response_root =
187
187
  if result_key = inner.keys.find {|k| k =~ /Result$/}
@@ -208,7 +208,11 @@ module AWS
208
208
  # Delegate first-level method calls to the root Proxy object
209
209
  ##
210
210
  def method_missing(name, *args)
211
- @request_root.send(name, *args)
211
+ if @request_root
212
+ @request_root.send(name, *args)
213
+ else
214
+ super
215
+ end
212
216
  end
213
217
 
214
218
  ##
data/lib/aws/core/util.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'ox'
1
+ require 'nokogiri'
2
2
 
3
3
  module AWS
4
4
  ##
@@ -40,17 +40,18 @@ module AWS
40
40
  # The hash body can contain symbols, strings, arrays and hashes
41
41
  ##
42
42
  def build_xml_from(hash, namespace = nil)
43
- doc = Ox::Document.new(:version => 1.0, :standalone => true)
43
+ doc = Nokogiri::XML::Document.new
44
+ doc.encoding = "UTF-8"
44
45
 
45
46
  root_key = hash.keys.first
46
- root = Ox::Element.new root_key
47
- root[:xmlns] = namespace if namespace
47
+ root = Nokogiri::XML::Element.new root_key.to_s, doc
48
+ root["xmlns"] = namespace if namespace
48
49
 
49
50
  doc << root
50
51
 
51
52
  build_body root, hash[root_key]
52
53
 
53
- %|<?xml version="1.0" encoding="UTF-8"?>#{Ox.dump doc}|
54
+ doc.to_s
54
55
  end
55
56
 
56
57
  extend self
@@ -61,7 +62,7 @@ module AWS
61
62
  hash.each do |key, value|
62
63
  case value
63
64
  when Hash
64
- node = build_node(key)
65
+ node = build_node(parent, key)
65
66
  build_body node, value
66
67
  parent << node
67
68
  when Array
@@ -69,13 +70,13 @@ module AWS
69
70
  build_body parent, {key => entry}
70
71
  end
71
72
  else
72
- parent << build_node(key, value)
73
+ parent << build_node(parent, key, value)
73
74
  end
74
75
  end
75
76
  end
76
77
 
77
- def build_node(key, value = nil)
78
- child = Ox::Element.new key
78
+ def build_node(parent, key, value = nil)
79
+ child = Nokogiri::XML::Element.new key.to_s, parent
79
80
  child << value.to_s unless value.nil?
80
81
  child
81
82
  end
data/lib/aws/ec2.rb CHANGED
@@ -11,6 +11,9 @@ module AWS
11
11
  #
12
12
  # All requests are POST and always through HTTPS. Use the third parameter to
13
13
  # #initialize if you need to talk to a region other than us-east-1.
14
+ #
15
+ # @see AWS::CallTypes::ActionParam Calling rules
16
+ # @see AWS::Response Response handling
14
17
  ##
15
18
  class EC2 < API
16
19
  endpoint "ec2"
@@ -11,6 +11,9 @@ module AWS
11
11
  #
12
12
  # All requests are POST and always through HTTPS. Use the third parameter to
13
13
  # #initialize if you need to talk to a region other than us-east-1.
14
+ #
15
+ # @see AWS::CallTypes::ActionParam Calling rules
16
+ # @see AWS::Response Response handling
14
17
  ##
15
18
  class ElastiCache < API
16
19
  endpoint "elasticache"
@@ -11,6 +11,9 @@ module AWS
11
11
  #
12
12
  # All requests are POST and always through HTTPS. Use the third parameter to
13
13
  # #initialize if you need to talk to a region other than us-east-1.
14
+ #
15
+ # @see AWS::CallTypes::ActionParam Calling rules
16
+ # @see AWS::Response Response handling
14
17
  ##
15
18
  class ElasticBeanstalk < API
16
19
  endpoint "elasticbeanstalk"
data/lib/aws/elb.rb CHANGED
@@ -11,6 +11,9 @@ module AWS
11
11
  #
12
12
  # All requests are POST and always through HTTPS. Use the third parameter to
13
13
  # #initialize if you need to talk to a region other than us-east-1.
14
+ #
15
+ # @see AWS::CallTypes::ActionParam Calling rules
16
+ # @see AWS::Response Response handling
14
17
  ##
15
18
  class ELB < API
16
19
  endpoint "elasticloadbalancing"
data/lib/aws/iam.rb CHANGED
@@ -11,6 +11,9 @@ module AWS
11
11
  #
12
12
  # All requests are POST and always through HTTPS. Use the third parameter to
13
13
  # #initialize if you need to talk to a region other than us-east-1.
14
+ #
15
+ # @see AWS::CallTypes::ActionParam Calling rules
16
+ # @see AWS::Response Response handling
14
17
  ##
15
18
  class IAM < API
16
19
  endpoint "iam"
@@ -11,6 +11,9 @@ module AWS
11
11
  #
12
12
  # All requests are POST and always through HTTPS.
13
13
  # This API does not support region specifiers
14
+ #
15
+ # @see AWS::CallTypes::ActionParam Calling rules
16
+ # @see AWS::Response Response handling
14
17
  ##
15
18
  class ImportExport < API
16
19
  endpoint "importexport"
@@ -11,6 +11,9 @@ module AWS
11
11
  #
12
12
  # All requests are POST and always through HTTPS. Use the third parameter to
13
13
  # #initialize if you need to talk to a region other than us-east-1.
14
+ #
15
+ # @see AWS::CallTypes::ActionParam Calling rules
16
+ # @see AWS::Response Response handling
14
17
  ##
15
18
  class MapReduce < API
16
19
  endpoint "elasticmapreduce"
@@ -14,6 +14,9 @@ module AWS
14
14
  #
15
15
  # For a more fleshed out object API for interacting with MechanicalTurk, you should
16
16
  # give rturk a try here: https://github.com/mdp/rturk
17
+ #
18
+ # @see AWS::CallTypes::ActionParam Calling rules
19
+ # @see AWS::Response Response handling
17
20
  ##
18
21
  class MechanicalTurk < API
19
22
  endpoint "mechanicalturk"
data/lib/aws/rds.rb CHANGED
@@ -11,6 +11,9 @@ module AWS
11
11
  #
12
12
  # All requests are POST and always through HTTPS. Use the third parameter to
13
13
  # #initialize if you need to talk to a region other than us-east-1.
14
+ #
15
+ # @see AWS::CallTypes::ActionParam Calling rules
16
+ # @see AWS::Response Response handling
14
17
  ##
15
18
  class RDS < API
16
19
  endpoint "rds"
data/lib/aws/s3.rb CHANGED
@@ -44,16 +44,11 @@ module AWS
44
44
  # JSON information, or an object that otherwise response to #read for file
45
45
  # uploads. This API does not build XML or JSON for you right now.
46
46
  #
47
- # s3.put "/object/name.txt", :bucket => "bucket_name", :body => {:file => File.open()}
48
- #
49
- # As this is a common use case, if you don't have any more parameters to send
50
- # with the file being uploaded, just use :file.
51
- #
52
- # s3.put "/object/name.txt", :bucket => "bucket_name", :file => File.open()
47
+ # s3.put "/object/name.txt", :bucket => "bucket_name", :body => File.open()
53
48
  #
54
49
  # This API does ensure that file data is uploaded as efficiently as possible,
55
- # streaming file data from disc to AWS without blowing up memory. All files are
56
- # uploading using multipart/form-data.
50
+ # streaming file data from disc to AWS without blowing up memory. If the
51
+ # Content-Type header is not specified, it will be defaulted to application/octet-stream
57
52
  #
58
53
  # NOTE: Like the other parts of SimpleAWS, this API does NOT try to make the
59
54
  # AWS API better, but simply provides a cleaner, easy to use API for Ruby.
@@ -70,13 +65,52 @@ module AWS
70
65
  use_https true
71
66
  version "2006-03-01"
72
67
 
73
- [:get, :post, :put, :delete, :head].each do |method|
68
+ ##
69
+ # Build a full URL for the resource at +path+.
70
+ # If options includes :expires, this url will be a signed url. :expires
71
+ # needs to be the raw Unix timestamp at which this URL will expire, as
72
+ # defined in the S3 documentation.
73
+ #
74
+ # Otherwise, +options+ can take anything as described above, but it
75
+ # will not use :headers or anything related to :body.
76
+ ##
77
+ def url_for(path, options = {})
78
+ request = build_request(:get, path, options)
79
+
80
+ url = "#{self.uri}#{request.path}"
81
+ sep = url =~ /\?/ ? "&" : "?"
82
+
83
+ if request.params.any?
84
+ params = request.params.map {|k, v| "#{k}=#{v}"}.join("&")
85
+ url += "#{sep}#{params}"
86
+ sep = "&"
87
+ end
88
+
89
+ if expires_at = options[:expires]
90
+ # Small hack, expires is in the Date section of the
91
+ # signing string, so we just do that here
92
+ request.headers["Date"] = expires_at.to_i
93
+
94
+ signature = "Signature=#{build_signature_for(request)}"
95
+ key = "AWSAccessKeyId=#{self.access_key}"
96
+ expires = "Expires=#{expires_at.to_i}"
97
+
98
+ url += "#{sep}#{signature}&#{key}&#{expires}"
99
+ end
100
+
101
+ url
102
+ end
103
+
104
+ [:get, :put, :delete, :head].each do |method|
74
105
  define_method(method) do |*args|
75
- self.call method, *args
106
+ request = self.build_request method, *args
107
+
108
+ connection = AWS::Connection.new
109
+ connection.call finish_and_sign_request(request)
76
110
  end
77
111
  end
78
112
 
79
- def call(method, path, options = {})
113
+ def build_request(method, path, options = {})
80
114
  if options[:bucket]
81
115
  path = path.gsub(/^\//, "/#{options[:bucket]}/")
82
116
  end
@@ -92,18 +126,34 @@ module AWS
92
126
  end
93
127
 
94
128
  if options[:file]
95
- options[:body] = {:file => options[:file]}
129
+ options[:body] = options.delete(:file)
130
+ end
131
+
132
+ signing_params = {}
133
+ request.params.delete_if {|k, v|
134
+ if k =~ /^response-/i
135
+ signing_params[k] = v
136
+ true
137
+ end
138
+ }
139
+
140
+ if signing_params.length > 0
141
+ to_add = signing_params.map {|k, v|
142
+ "#{k}=#{v}"
143
+ }.join("&")
144
+
145
+ request.path = request.path + "?#{to_add}"
96
146
  end
97
147
 
98
148
  request.body = options[:body]
99
149
 
100
- if request.body.is_a?(Hash) && request.body[:file]
101
- request.headers["Content-Type"] =
102
- "multipart/form-data; boundary=-----------RubyMultipartPost"
150
+ if request.body.respond_to?(:read)
151
+ request.headers["Content-Type"] ||= "application/octet-stream"
152
+ request.headers["Content-Length"] = File.size(request.body).to_s
153
+ request.headers["Expect"] = "100-continue"
103
154
  end
104
155
 
105
- connection = AWS::Connection.new
106
- connection.call finish_and_sign_request(request)
156
+ request
107
157
  end
108
158
 
109
159
  ##
@@ -123,16 +173,17 @@ module AWS
123
173
 
124
174
  ##
125
175
  # Build and sign the final request, as per the rules here:
126
- # http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html
176
+ # http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html
127
177
  ##
128
178
  def finish_and_sign_request(request)
129
179
  request.headers["Date"] = Time.now.utc.httpdate
130
180
  request.headers["Authorization"] =
131
- "AWS #{self.access_key}:#{Base64.encode64(build_signature_for(request)).chomp}"
181
+ "AWS #{self.access_key}:#{build_signature_for(request)}"
132
182
 
133
183
  request
134
184
  end
135
185
 
186
+
136
187
  def build_signature_for(request)
137
188
  amazon_headers = request.headers.select {|k, v|
138
189
  k =~ /^x-amz/i
@@ -149,7 +200,9 @@ module AWS
149
200
  request.path
150
201
  ].flatten.join("\n")
151
202
 
152
- OpenSSL::HMAC.digest("sha1", self.secret_key, to_sign)
203
+ Base64.encode64(
204
+ OpenSSL::HMAC.digest("sha1", self.secret_key, to_sign)
205
+ ).chomp
153
206
  end
154
207
 
155
208
  end
data/lib/aws/ses.rb CHANGED
@@ -10,6 +10,9 @@ module AWS
10
10
  # http://docs.amazonwebservices.com/ses/latest/APIReference/
11
11
  #
12
12
  # All requests are POST and always through HTTPS.
13
+ #
14
+ # @see AWS::CallTypes::ActionParam Calling rules
15
+ # @see AWS::Response Response handling
13
16
  ##
14
17
  class SES < API
15
18
  endpoint "email"
data/lib/aws/sns.rb CHANGED
@@ -11,6 +11,9 @@ module AWS
11
11
  #
12
12
  # All requests are POST and always through HTTPS. Use the third parameter to
13
13
  # #initialize if you need to talk to a region other than us-east-1.
14
+ #
15
+ # @see AWS::CallTypes::ActionParam Calling rules
16
+ # @see AWS::Response Response handling
14
17
  ##
15
18
  class SNS < API
16
19
  endpoint "sns"
data/lib/aws/sqs.rb CHANGED
@@ -19,6 +19,9 @@ module AWS
19
19
  #
20
20
  # In accordance with the AWS documentation, SimpleAWS does not try to reconstruct
21
21
  # queue urls, use ListQueues or GetQueueUrl to get the correct queue url when needed.
22
+ #
23
+ # @see AWS::CallTypes::ActionParam Calling rules
24
+ # @see AWS::Response Response handling
22
25
  ##
23
26
  class SQS < API
24
27
  endpoint "sqs"
data/samples/s3.rb CHANGED
@@ -51,6 +51,20 @@ puts "", "Checking that the file now exists...", ""
51
51
 
52
52
  p s3.head("/#{uploaded_file_name}", :bucket => bucket_name)
53
53
 
54
+ puts "", "Getting file again", ""
55
+
56
+ p s3.get("/#{uploaded_file_name}", :bucket => bucket_name,
57
+ :params => {
58
+ "response-content-disposition" => "attachment",
59
+ "response-content-type" => "text/ruby",
60
+ "response-expires" => "never" })
61
+
62
+ puts "", "Signed, expiring URL for this resource: ", ""
63
+
64
+ puts s3.url_for("/#{uploaded_file_name}", :bucket => bucket_name,
65
+ :expires => Time.now.to_i + 120,
66
+ :params => { "response-content-disposition" => "attachment" })
67
+
54
68
  puts "", "Deleting the file from S3", ""
55
69
 
56
70
  p s3.delete("/#{uploaded_file_name}", :bucket => bucket_name)
@@ -62,3 +76,5 @@ begin
62
76
  rescue => ex
63
77
  puts "Not found: #{ex.message}"
64
78
  end
79
+
80
+
data/simple_aws.gemspec CHANGED
@@ -1,18 +1,17 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "simple_aws"
3
- s.version = "0.0.1d"
3
+ s.version = "1.0.0.pre1"
4
4
  s.platform = Gem::Platform::RUBY
5
5
  s.authors = ["Jason Roelofs"]
6
6
  s.email = ["jameskilton@gmail.com"]
7
7
 
8
- # s.homepage = ""
8
+ s.homepage = "http://github.com/jameskilton/simple_aws"
9
9
 
10
10
  s.summary = "The simplest and easiest to use and maintain AWS communication library"
11
- s.description = "The simplest and easiest to use and maintain AWS communication library"
11
+ s.description = "SimpleAWS is a clean, simple, and forward compatible library for talking to Amazon's AWS APIs."
12
12
 
13
- s.add_dependency "ox"
14
- s.add_dependency "httparty"
15
- s.add_dependency "httmultiparty"
13
+ s.add_dependency "nokogiri", "~> 1.5.0"
14
+ s.add_dependency "httparty", "~> 0.8.0"
16
15
 
17
16
  s.add_dependency "jruby-openssl" if RUBY_PLATFORM == 'java'
18
17
 
@@ -3,14 +3,19 @@ require 'aws/core/response'
3
3
 
4
4
  describe AWS::Response do
5
5
 
6
+ def response_stub(success = true, code = 200)
7
+ http_response = stub
8
+ http_response.stubs(:headers).returns(nil)
9
+ http_response.stubs(:success?).returns(success)
10
+ http_response.stubs(:code).returns(code)
11
+ http_response
12
+ end
13
+
6
14
  describe "errors" do
7
15
 
8
16
  before do
9
17
  @error_response = {}
10
- @http_response = stub
11
- @http_response.stubs(:headers).returns(nil)
12
- @http_response.stubs(:success?).returns(false)
13
- @http_response.stubs(:code).returns(401)
18
+ @http_response = response_stub false, 401
14
19
  end
15
20
 
16
21
  it "raises if the response is not a success" do
@@ -111,10 +116,7 @@ describe AWS::Response do
111
116
  }
112
117
  }
113
118
 
114
- @http_response = stub
115
- @http_response.stubs(:headers).returns(nil)
116
- @http_response.stubs(:code).returns(200)
117
- @http_response.stubs(:success?).returns(true)
119
+ @http_response = response_stub
118
120
  @http_response.stubs(:parsed_response).returns(@response_hash)
119
121
 
120
122
  @response = AWS::Response.new @http_response
@@ -170,10 +172,7 @@ describe AWS::Response do
170
172
  }
171
173
  }
172
174
 
173
- @http_response = stub
174
- @http_response.stubs(:headers).returns(nil)
175
- @http_response.stubs(:code).returns(202)
176
- @http_response.stubs(:success?).returns(true)
175
+ @http_response = response_stub true, 202
177
176
  @http_response.stubs(:parsed_response).returns(@response_hash)
178
177
 
179
178
  @response = AWS::Response.new @http_response
@@ -232,10 +231,7 @@ describe AWS::Response do
232
231
  }
233
232
  }
234
233
 
235
- @http_response = stub
236
- @http_response.stubs(:code).returns(202)
237
- @http_response.stubs(:headers).returns(nil)
238
- @http_response.stubs(:success?).returns(true)
234
+ @http_response = response_stub true, 202
239
235
  @http_response.stubs(:parsed_response).returns(@response_hash)
240
236
 
241
237
  @response = AWS::Response.new @http_response
@@ -338,12 +334,32 @@ describe AWS::Response do
338
334
 
339
335
  end
340
336
 
337
+ describe "raw body data" do
338
+
339
+ before do
340
+ @http_response = response_stub true, 200
341
+ @http_response.stubs(:parsed_response).returns("raw string body")
342
+
343
+ @response = AWS::Response.new @http_response
344
+ end
345
+
346
+ it "doesn't try to parse raw body data" do
347
+ @response.body.must_equal "raw string body"
348
+ end
349
+
350
+ it "gives a clear error message on unknown method calls" do
351
+ error = lambda {
352
+ @response.some_value
353
+ }.must_raise NoMethodError
354
+
355
+ error.message.must_match /AWS::Response/
356
+ end
357
+
358
+ end
359
+
341
360
  describe "#request_id" do
342
361
  before do
343
- @http_response = stub
344
- @http_response.stubs(:headers).returns(nil)
345
- @http_response.stubs(:code).returns(200)
346
- @http_response.stubs(:success?).returns(true)
362
+ @http_response = response_stub true, 200
347
363
  end
348
364
 
349
365
  it "finds the top level response id" do
@@ -72,14 +72,9 @@ END
72
72
  :RootNode => { :BoolVal => true, :Number => 12, :BadBool => false }
73
73
  )
74
74
 
75
- response.must_equal <<END
76
- <?xml version="1.0" encoding="UTF-8"?>
77
- <RootNode>
78
- <BoolVal>true</BoolVal>
79
- <Number>12</Number>
80
- <BadBool>false</BadBool>
81
- </RootNode>
82
- END
75
+ response.must_match(%r{<BoolVal>true</BoolVal>})
76
+ response.must_match(%r{<Number>12</Number>})
77
+ response.must_match(%r{<BadBool>false</BadBool>})
83
78
  end
84
79
  end
85
80
 
data/test/aws/s3_test.rb CHANGED
@@ -20,9 +20,45 @@ describe AWS::S3 do
20
20
  @api.version.must_equal "2006-03-01"
21
21
  end
22
22
 
23
+ describe "url_for" do
24
+
25
+ it "can build an unsigned, regular URL for the requested path" do
26
+ url = @api.url_for("/object", :bucket => "johnson")
27
+ url.must_equal "https://s3.amazonaws.com/johnson/object"
28
+ end
29
+
30
+ it "can build a signed, expiring URL" do
31
+ url = @api.url_for("/object", :bucket => "johnson", :expires => Time.now.to_i + 60)
32
+ url.must_match %r[https://s3.amazonaws.com/johnson/object\?]
33
+ url.must_match %r[Expires=#{Time.now.to_i + 60}]
34
+ url.must_match %r[AWSAccessKeyId=key]
35
+ url.must_match %r[Signature=]
36
+ end
37
+
38
+ it "adds parameters to the path" do
39
+ url = @api.url_for("/object", :bucket => "johnson", :params => {"param1" => "14"})
40
+ url.must_equal "https://s3.amazonaws.com/johnson/object?param1=14"
41
+ end
42
+
43
+ it "properly handles response- parameters" do
44
+ url = @api.url_for("/object", :bucket => "johnson",
45
+ :params => {"response-content-type" => "text/xml"})
46
+ url.must_equal "https://s3.amazonaws.com/johnson/object?response-content-type=text/xml"
47
+ end
48
+
49
+ it "combines parameters and signing params properly" do
50
+ url = @api.url_for("/object", :bucket => "johnson",
51
+ :params => {"response-content-type" => "text/xml"},
52
+ :expires => Time.now.to_i
53
+ )
54
+ url.must_match %r{/johnson/object\?response-content-type=text/xml&Signature=}
55
+ end
56
+
57
+ end
58
+
23
59
  describe "API calls" do
24
60
 
25
- [:get, :post, :put, :delete, :head].each do |method|
61
+ [:get, :put, :delete, :head].each do |method|
26
62
  it "supports the #{method} HTTP method" do
27
63
  AWS::Connection.any_instance.expects(:call).with do |request|
28
64
  request.method.must_equal method
@@ -54,6 +90,16 @@ describe AWS::S3 do
54
90
  @api.get "/", :params => { "Parameter1" => "Value2" }
55
91
  end
56
92
 
93
+ it "handles the special response- parameters" do
94
+ AWS::Connection.any_instance.expects(:call).with do |request|
95
+ request.path.must_equal "/?response-content-type=application/xml"
96
+ request.params["response-content-type"].must_be_nil
97
+ true
98
+ end
99
+
100
+ @api.get "/", :params => { "response-content-type" => "application/xml" }
101
+ end
102
+
57
103
  it "takes extra headers" do
58
104
  AWS::Connection.any_instance.expects(:call).with do |request|
59
105
  request.headers["Header14"].must_equal "Out to Lunch"
@@ -72,28 +118,31 @@ describe AWS::S3 do
72
118
  @api.get "/", :body => "This is a body of text"
73
119
  end
74
120
 
75
- it "adds the form-data header if the body has a file in it" do
121
+ it "adds appropriate headers if the body has a file in it" do
122
+ file = File.new("Gemfile")
123
+
76
124
  AWS::Connection.any_instance.expects(:call).with do |request|
77
- request.body.must_equal :file => "This is a body of text"
78
- request.headers["Content-Type"].must_equal(
79
- "multipart/form-data; boundary=-----------RubyMultipartPost"
80
- )
125
+ request.body.must_equal file
126
+
127
+ request.headers["Content-Length"].must_equal File.size(file).to_s
128
+ request.headers["Content-Type"].must_equal "application/octet-stream"
129
+ request.headers["Expect"].must_equal "100-continue"
81
130
  true
82
131
  end
83
132
 
84
- @api.get "/", :body => {:file => "This is a body of text"}
133
+ @api.get "/", :body => file
85
134
  end
86
135
 
87
- it "allows use of :file if :body only contains a file" do
136
+ it "uses previously set content type if given" do
88
137
  AWS::Connection.any_instance.expects(:call).with do |request|
89
- request.body.must_equal :file => "This is a body of text"
90
138
  request.headers["Content-Type"].must_equal(
91
- "multipart/form-data; boundary=-----------RubyMultipartPost"
139
+ "application/pdf"
92
140
  )
93
141
  true
94
142
  end
95
143
 
96
- @api.get "/", :file => "This is a body of text"
144
+ @api.get "/", :body => File.new("Gemfile"),
145
+ :headers => {"Content-Type" => "application/pdf"}
97
146
  end
98
147
 
99
148
  it "signs the given request according to Version 3 rules" do
metadata CHANGED
@@ -1,50 +1,40 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_aws
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1d
5
- prerelease: 5
4
+ version: 1.0.0.pre1
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jason Roelofs
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-09 00:00:00.000000000 Z
12
+ date: 2012-01-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: ox
16
- requirement: &70172190923080 !ruby/object:Gem::Requirement
15
+ name: nokogiri
16
+ requirement: &70160933707720 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ! '>='
19
+ - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '0'
21
+ version: 1.5.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70172190923080
24
+ version_requirements: *70160933707720
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: httparty
27
- requirement: &70172190922660 !ruby/object:Gem::Requirement
27
+ requirement: &70160933701880 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
- - - ! '>='
30
+ - - ~>
31
31
  - !ruby/object:Gem::Version
32
- version: '0'
32
+ version: 0.8.0
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70172190922660
36
- - !ruby/object:Gem::Dependency
37
- name: httmultiparty
38
- requirement: &70172190922220 !ruby/object:Gem::Requirement
39
- none: false
40
- requirements:
41
- - - ! '>='
42
- - !ruby/object:Gem::Version
43
- version: '0'
44
- type: :runtime
45
- prerelease: false
46
- version_requirements: *70172190922220
47
- description: The simplest and easiest to use and maintain AWS communication library
35
+ version_requirements: *70160933701880
36
+ description: SimpleAWS is a clean, simple, and forward compatible library for talking
37
+ to Amazon's AWS APIs.
48
38
  email:
49
39
  - jameskilton@gmail.com
50
40
  executables: []
@@ -54,6 +44,7 @@ files:
54
44
  - .gitignore
55
45
  - .travis.yml
56
46
  - Gemfile
47
+ - MIT_LICENCE
57
48
  - README.md
58
49
  - Rakefile
59
50
  - lib/aws/api.rb
@@ -115,7 +106,7 @@ files:
115
106
  - test/aws/sns_test.rb
116
107
  - test/aws/sqs.rb
117
108
  - test/test_helper.rb
118
- homepage:
109
+ homepage: http://github.com/jameskilton/simple_aws
119
110
  licenses: []
120
111
  post_install_message:
121
112
  rdoc_options: []
@@ -166,3 +157,4 @@ test_files:
166
157
  - test/aws/sns_test.rb
167
158
  - test/aws/sqs.rb
168
159
  - test/test_helper.rb
160
+ has_rdoc: