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 +2 -0
- data/.travis.yml +2 -2
- data/Gemfile +1 -0
- data/MIT_LICENCE +20 -0
- data/README.md +48 -34
- data/lib/aws/auto_scaling.rb +3 -0
- data/lib/aws/call_types/action_param.rb +8 -1
- data/lib/aws/cloud_formation.rb +3 -0
- data/lib/aws/cloud_watch.rb +3 -0
- data/lib/aws/core/connection.rb +25 -3
- data/lib/aws/core/response.rb +6 -2
- data/lib/aws/core/util.rb +10 -9
- data/lib/aws/ec2.rb +3 -0
- data/lib/aws/elasti_cache.rb +3 -0
- data/lib/aws/elastic_beanstalk.rb +3 -0
- data/lib/aws/elb.rb +3 -0
- data/lib/aws/iam.rb +3 -0
- data/lib/aws/import_export.rb +3 -0
- data/lib/aws/map_reduce.rb +3 -0
- data/lib/aws/mechanical_turk.rb +3 -0
- data/lib/aws/rds.rb +3 -0
- data/lib/aws/s3.rb +73 -20
- data/lib/aws/ses.rb +3 -0
- data/lib/aws/sns.rb +3 -0
- data/lib/aws/sqs.rb +3 -0
- data/samples/s3.rb +16 -0
- data/simple_aws.gemspec +5 -6
- data/test/aws/core/response_test.rb +36 -20
- data/test/aws/core/util_test.rb +3 -8
- data/test/aws/s3_test.rb +60 -11
- metadata +17 -25
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
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
|
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
|
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
|
-
|
17
|
+
### Forward Compatibility
|
18
18
|
|
19
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
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
|
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
|
-
*
|
122
|
-
*
|
123
|
-
* SQS
|
124
|
-
* SES
|
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
|
-
|
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
|
-
|
data/lib/aws/auto_scaling.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 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
|
##
|
data/lib/aws/cloud_formation.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 CloudFormation < API
|
16
19
|
endpoint "cloudformation"
|
data/lib/aws/cloud_watch.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 CloudWatch < API
|
16
19
|
endpoint "monitoring"
|
data/lib/aws/core/connection.rb
CHANGED
@@ -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
|
11
|
-
format :xml
|
33
|
+
include HTTParty
|
12
34
|
end
|
13
35
|
|
14
36
|
##
|
data/lib/aws/core/response.rb
CHANGED
@@ -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
|
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 '
|
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 =
|
43
|
+
doc = Nokogiri::XML::Document.new
|
44
|
+
doc.encoding = "UTF-8"
|
44
45
|
|
45
46
|
root_key = hash.keys.first
|
46
|
-
root =
|
47
|
-
root[
|
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
|
-
|
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 =
|
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"
|
data/lib/aws/elasti_cache.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 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"
|
data/lib/aws/import_export.rb
CHANGED
@@ -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"
|
data/lib/aws/map_reduce.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 MapReduce < API
|
16
19
|
endpoint "elasticmapreduce"
|
data/lib/aws/mechanical_turk.rb
CHANGED
@@ -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 =>
|
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.
|
56
|
-
#
|
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
|
-
|
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.
|
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
|
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] =
|
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.
|
101
|
-
request.headers["Content-Type"]
|
102
|
-
|
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
|
-
|
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://
|
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}:#{
|
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
|
-
|
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.
|
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
|
-
|
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 = "
|
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 "
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
data/test/aws/core/util_test.rb
CHANGED
@@ -72,14 +72,9 @@ END
|
|
72
72
|
:RootNode => { :BoolVal => true, :Number => 12, :BadBool => false }
|
73
73
|
)
|
74
74
|
|
75
|
-
response.
|
76
|
-
|
77
|
-
<
|
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, :
|
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
|
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
|
78
|
-
|
79
|
-
|
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 =>
|
133
|
+
@api.get "/", :body => file
|
85
134
|
end
|
86
135
|
|
87
|
-
it "
|
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
|
-
"
|
139
|
+
"application/pdf"
|
92
140
|
)
|
93
141
|
true
|
94
142
|
end
|
95
143
|
|
96
|
-
@api.get "/", :
|
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.
|
5
|
-
prerelease:
|
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-
|
12
|
+
date: 2012-01-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
16
|
-
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:
|
21
|
+
version: 1.5.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70160933707720
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: httparty
|
27
|
-
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:
|
32
|
+
version: 0.8.0
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
36
|
-
|
37
|
-
|
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:
|