swirl 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/README.md +67 -0
- data/bin/swirl +11 -0
- data/lib/swirl.rb +1 -0
- data/lib/swirl/ec2.rb +114 -0
- data/lib/swirl/helpers.rb +108 -0
- data/list-types +36 -0
- data/test/compactor_test.rb +73 -0
- data/test/expander_test.rb +46 -0
- data/test/slop_test.rb +29 -0
- metadata +101 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2007, 2008, 2009 Blake Mizerany
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
Swirl
|
2
|
+
=====
|
3
|
+
|
4
|
+
Swirl is an EC2 version agnostic client for EC2 writtin in Ruby. It gets
|
5
|
+
out of your way.
|
6
|
+
|
7
|
+
The secret is it's simple input extraction and output compacting. Your
|
8
|
+
input parameters and `expand`ed and EC2's (terrible) xml output is
|
9
|
+
`compact`ed.
|
10
|
+
|
11
|
+
|
12
|
+
Some simple examples:
|
13
|
+
|
14
|
+
# Input
|
15
|
+
{ "InstanceId" => ["i-123k2h1", "i-0234d3"] }
|
16
|
+
|
17
|
+
is `expand`ed to:
|
18
|
+
|
19
|
+
{ "InstanceId.0" => "i-123k2h1", "InstanceId.1" => "i-0234d3" }
|
20
|
+
|
21
|
+
and
|
22
|
+
|
23
|
+
# Output
|
24
|
+
{
|
25
|
+
"reservationSet" => {
|
26
|
+
"item" => {
|
27
|
+
"instancesSet" => { "item" => [ ... ] }
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
and it's varations are now `compact`ed to:
|
33
|
+
|
34
|
+
{
|
35
|
+
"reservationSet" => {
|
36
|
+
"instancesSet" => [ { ... }, { ... } ]
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
|
41
|
+
Some things worth noteing is that compact ignores Symbols. This
|
42
|
+
allows you to pass the params into `call` and use them later
|
43
|
+
without affecting the API call (i.e. chain of responsiblity); a
|
44
|
+
nifty trick we use in (Rack)[http://github.com/rack/rack]
|
45
|
+
|
46
|
+
Use
|
47
|
+
---
|
48
|
+
|
49
|
+
ec2 = Swirl::EC2.new
|
50
|
+
|
51
|
+
# Describe all instances
|
52
|
+
ec2.call "DescribeInstances"
|
53
|
+
|
54
|
+
# Describe specific instances
|
55
|
+
ec2.call "DescribeInstances", "InstanceId" => ["i-38hdk2f", "i-93nndch"]
|
56
|
+
|
57
|
+
|
58
|
+
Shell
|
59
|
+
---
|
60
|
+
|
61
|
+
$ swirl
|
62
|
+
>> c
|
63
|
+
<Swirl::EC2 ... >
|
64
|
+
>> c.call "DescribeInstances"
|
65
|
+
...
|
66
|
+
|
67
|
+
The shell respects your ~/.swirl file for configuration
|
data/bin/swirl
ADDED
data/lib/swirl.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'swirl/ec2'
|
data/lib/swirl/ec2.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'cgi'
|
3
|
+
require 'base64'
|
4
|
+
require 'net/https'
|
5
|
+
|
6
|
+
require 'crack/xml'
|
7
|
+
require 'hmac-sha2'
|
8
|
+
|
9
|
+
require 'swirl/helpers'
|
10
|
+
|
11
|
+
module Swirl
|
12
|
+
|
13
|
+
## Errors
|
14
|
+
class InvalidRequest < StandardError ; end
|
15
|
+
|
16
|
+
|
17
|
+
class EC2
|
18
|
+
include Helpers::Compactor
|
19
|
+
include Helpers::Expander
|
20
|
+
|
21
|
+
|
22
|
+
def self.options(name=:default, file="~/.swirl")
|
23
|
+
YAML.load_file(File.expand_path(file))[name]
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(options={}, file="~/.swirl")
|
27
|
+
account = options.fetch(:account) { :default }
|
28
|
+
options = self.class.options(account, file).merge(options)
|
29
|
+
|
30
|
+
@aws_access_key_id = options[:aws_access_key_id]
|
31
|
+
@aws_secret_access_key = options[:aws_secret_access_key]
|
32
|
+
@hmac = HMAC::SHA256.new(@aws_secret_access_key)
|
33
|
+
@version = options[:version] || "2009-11-30"
|
34
|
+
@url = URI(options[:url] || "https://ec2.amazonaws.com")
|
35
|
+
end
|
36
|
+
|
37
|
+
def escape(value)
|
38
|
+
CGI.escape(value).gsub(/\+/, "%20")
|
39
|
+
end
|
40
|
+
|
41
|
+
def compile_sorted_form_data(query)
|
42
|
+
valid = query.reject {|_, v| v.nil? }
|
43
|
+
valid.sort.map {|k,v| [k, escape(v)] * "=" } * "&"
|
44
|
+
end
|
45
|
+
|
46
|
+
def compile_signature(method, body)
|
47
|
+
string_to_sign = [method, @url.host, "/", body] * "\n"
|
48
|
+
hmac = @hmac.update(string_to_sign)
|
49
|
+
encoded_sig = Base64.encode64(hmac.digest).chomp
|
50
|
+
escape(encoded_sig)
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Execute an EC2 command, expand the input,
|
55
|
+
# and compact the output
|
56
|
+
#
|
57
|
+
# Examples:
|
58
|
+
# ec2.call("DescribeInstances")
|
59
|
+
# ec2.call("TerminateInstances", "InstanceId" => ["i-1234", "i-993j"]
|
60
|
+
#
|
61
|
+
def call(action, query={})
|
62
|
+
code, data = call!(action, expand(query))
|
63
|
+
|
64
|
+
case code
|
65
|
+
when 200
|
66
|
+
compact(data)
|
67
|
+
when 400...500
|
68
|
+
messages = Array(data["Response"]["Errors"]).map {|_, e| e["Message"] }
|
69
|
+
raise InvalidRequest, messages.join(",")
|
70
|
+
else
|
71
|
+
msg = "unexpected response #{response.code} -> #{data.inspect}"
|
72
|
+
raise InvalidRequest, msg
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def call!(action, query={})
|
77
|
+
# Hard coding this here until otherwise needed
|
78
|
+
method = "POST"
|
79
|
+
|
80
|
+
query["Action"] = action
|
81
|
+
query["AWSAccessKeyId"] = @aws_access_key_id
|
82
|
+
query["SignatureMethod"] = "HmacSHA256"
|
83
|
+
query["SignatureVersion"] = "2"
|
84
|
+
query["Timestamp"] = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
85
|
+
query["Version"] = @version
|
86
|
+
|
87
|
+
body = compile_sorted_form_data(query)
|
88
|
+
body += "&" + ["Signature", compile_signature(method, body)].join("=")
|
89
|
+
|
90
|
+
response = post(body)
|
91
|
+
data = Crack::XML.parse(response.body)
|
92
|
+
[response.code.to_i, data]
|
93
|
+
end
|
94
|
+
|
95
|
+
def post(body)
|
96
|
+
headers = { "Content-Type" => "application/x-www-form-urlencoded" }
|
97
|
+
|
98
|
+
http = Net::HTTP.new(@url.host, @url.port)
|
99
|
+
http.use_ssl = true
|
100
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
101
|
+
|
102
|
+
request = Net::HTTP::Post.new(@url.request_uri, headers)
|
103
|
+
request.body = body
|
104
|
+
|
105
|
+
http.request(request)
|
106
|
+
end
|
107
|
+
|
108
|
+
def inspect
|
109
|
+
"<#{self.class.name} version: #{@version} url: #{@url} aws_access_key_id: #{@aws_access_key_id}>"
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Swirl
|
2
|
+
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
module Compactor
|
6
|
+
|
7
|
+
Lists = [
|
8
|
+
"keySet",
|
9
|
+
"groupSet",
|
10
|
+
"blockDeviceMapping",
|
11
|
+
"instancesSet",
|
12
|
+
"reservationSet",
|
13
|
+
"imagesSet",
|
14
|
+
"ownersSet",
|
15
|
+
"executableBySet",
|
16
|
+
"securityGroupSet",
|
17
|
+
"ipPermissions",
|
18
|
+
"ipRanges",
|
19
|
+
"groups",
|
20
|
+
"securityGroupInfo",
|
21
|
+
"add",
|
22
|
+
"remove",
|
23
|
+
"launchPermission",
|
24
|
+
"productCodes",
|
25
|
+
"availabilityZoneSet",
|
26
|
+
"availabilityZoneInfo",
|
27
|
+
"publicIpsSet",
|
28
|
+
"addressesSet"
|
29
|
+
]
|
30
|
+
|
31
|
+
def compact(response)
|
32
|
+
root_key = response.keys.first
|
33
|
+
base = response[root_key]
|
34
|
+
compact!(base)
|
35
|
+
end
|
36
|
+
module_function :compact
|
37
|
+
|
38
|
+
def compact!(data)
|
39
|
+
data.inject({}) do |com, (key, value)|
|
40
|
+
if Lists.include?(key)
|
41
|
+
converted = if value && value.has_key?("item")
|
42
|
+
items = value["item"]
|
43
|
+
items ||= []
|
44
|
+
items = items.is_a?(Array) ? items : [items]
|
45
|
+
items.map {|item| compact!(item) }
|
46
|
+
else
|
47
|
+
[]
|
48
|
+
end
|
49
|
+
com[key] = converted
|
50
|
+
elsif key == "xmlns"
|
51
|
+
next(com)
|
52
|
+
else
|
53
|
+
com[key] = value
|
54
|
+
end
|
55
|
+
com
|
56
|
+
end
|
57
|
+
end
|
58
|
+
module_function :compact!
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
module Expander
|
63
|
+
def expand(request)
|
64
|
+
request.inject({}) do |exp, (key, value)|
|
65
|
+
next(exp) if !key.is_a?(String)
|
66
|
+
|
67
|
+
case value
|
68
|
+
when Array
|
69
|
+
value.each_with_index do |val, n|
|
70
|
+
exp["#{key}.#{n}"] = val
|
71
|
+
end
|
72
|
+
when Range
|
73
|
+
exp["From#{key}"] = value.min
|
74
|
+
exp["To#{key}"] = value.max
|
75
|
+
else
|
76
|
+
exp[key] = value
|
77
|
+
end
|
78
|
+
exp
|
79
|
+
end
|
80
|
+
end
|
81
|
+
module_function :expand
|
82
|
+
end
|
83
|
+
|
84
|
+
module Slop
|
85
|
+
class InvalidKey < StandardError ; end
|
86
|
+
|
87
|
+
def self.new(response)
|
88
|
+
sloppy = Hash.new do |hash, key|
|
89
|
+
camalized = Slop.camalize(key)
|
90
|
+
raise InvalidKey, key if !response.has_key?(camalized)
|
91
|
+
response[camalized]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.camalize(stringish)
|
96
|
+
head, tail = stringish.to_s.split("_")
|
97
|
+
rest = Array(tail).map! {|part| part.capitalize }
|
98
|
+
[head, *rest].join
|
99
|
+
end
|
100
|
+
|
101
|
+
def slopify(response)
|
102
|
+
Slop.new(response)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
data/list-types
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'nokogiri'
|
5
|
+
rescue LoadError
|
6
|
+
abort <<-MSG
|
7
|
+
Unable to load nokogiri.
|
8
|
+
Ensure you have it installed and/or
|
9
|
+
export RUBYOPT=rubygems
|
10
|
+
MSG
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'open-uri'
|
14
|
+
require 'stringio'
|
15
|
+
require 'pp'
|
16
|
+
|
17
|
+
DefaultWSDL = "http://s3.amazonaws.com/ec2-downloads/2008-02-01.ec2.wsdl"
|
18
|
+
|
19
|
+
wsdl_url = ARGV.shift || DefaultWSDL
|
20
|
+
Wsdl = Nokogiri::XML.parse(open(wsdl_url))
|
21
|
+
|
22
|
+
##__END__
|
23
|
+
|
24
|
+
list_keys = []
|
25
|
+
item_types = Wsdl.search(%Q|//xs:complexType//xs:element[@name="item"]|)
|
26
|
+
item_types.each do |item_type|
|
27
|
+
ns, name = item_type["type"].split(":")
|
28
|
+
next if ns != "tns"
|
29
|
+
complex = item_type.parent.parent
|
30
|
+
uses = Wsdl.search(%Q|//xs:element[@type="#{ns}:#{complex["name"]}"]|)
|
31
|
+
uses.each do |u|
|
32
|
+
list_keys << u["name"]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
pp list_keys.uniq
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'contest'
|
2
|
+
require 'swirl/helpers'
|
3
|
+
|
4
|
+
class CompactorTest < Test::Unit::TestCase
|
5
|
+
include Swirl::Helpers
|
6
|
+
|
7
|
+
test "pivots on root" do
|
8
|
+
response = { "Foo" => { "requestId" => "abc123" } }
|
9
|
+
expected = { "requestId" => "abc123" }
|
10
|
+
|
11
|
+
assert_equal expected, Compactor.compact(response)
|
12
|
+
end
|
13
|
+
|
14
|
+
test "ignores xmlns" do
|
15
|
+
response = { "Foo" => { "requestId" => "abc123", "xmlns" => "suckit" } }
|
16
|
+
expected = { "requestId" => "abc123" }
|
17
|
+
|
18
|
+
assert_equal expected, Compactor.compact(response)
|
19
|
+
end
|
20
|
+
|
21
|
+
test "pivots list keys on item" do
|
22
|
+
response = { "Foo" => { "groupSet" => { "item" => [{ "foo" => "bar" }] } } }
|
23
|
+
expected = { "groupSet" => [ { "foo" => "bar" } ] }
|
24
|
+
|
25
|
+
assert_equal expected, Compactor.compact(response)
|
26
|
+
end
|
27
|
+
|
28
|
+
test "pivots list keys item and converts to Array not already an Array" do
|
29
|
+
response = { "Foo" => { "groupSet" => { "item" => { "foo" => "bar" } } } }
|
30
|
+
expected = { "groupSet" => [ { "foo" => "bar" } ] }
|
31
|
+
|
32
|
+
assert_equal expected, Compactor.compact(response)
|
33
|
+
end
|
34
|
+
|
35
|
+
test "pivots list keys item and makes empty Array if nil" do
|
36
|
+
response = { "Foo" => { "groupSet" => { "item" => nil } } }
|
37
|
+
expected = { "groupSet" => [] }
|
38
|
+
|
39
|
+
assert_equal expected, Compactor.compact(response)
|
40
|
+
end
|
41
|
+
|
42
|
+
test "makes value empty Array if nil" do
|
43
|
+
response = { "Foo" => { "groupSet" => nil } }
|
44
|
+
expected = { "groupSet" => [] }
|
45
|
+
|
46
|
+
assert_equal expected, Compactor.compact(response)
|
47
|
+
end
|
48
|
+
|
49
|
+
test "traverses list values and compacts" do
|
50
|
+
response = {
|
51
|
+
"Foo" => {
|
52
|
+
"groupSet" => {
|
53
|
+
"item" => {
|
54
|
+
"ipPermissions" => {
|
55
|
+
"item" => {
|
56
|
+
"proto" => "tcp"
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
expected = {
|
65
|
+
"groupSet" => [
|
66
|
+
{ "ipPermissions" => [ { "proto" => "tcp" } ] }
|
67
|
+
]
|
68
|
+
}
|
69
|
+
|
70
|
+
assert_equal expected, Compactor.compact(response)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'contest'
|
2
|
+
require 'swirl/helpers'
|
3
|
+
|
4
|
+
class ExpanderTest < Test::Unit::TestCase
|
5
|
+
include Swirl::Helpers
|
6
|
+
|
7
|
+
test "leaves params as is by default" do
|
8
|
+
request = { "foo" => "bar" }
|
9
|
+
|
10
|
+
assert_equal request, Expander.expand(request)
|
11
|
+
end
|
12
|
+
|
13
|
+
test "ignores non-String keys" do
|
14
|
+
request = { "foo" => "bar", :ignore => "test" }
|
15
|
+
expected = { "foo" => "bar" }
|
16
|
+
|
17
|
+
assert_equal expected, Expander.expand(request)
|
18
|
+
end
|
19
|
+
|
20
|
+
test "expands Array values to .n key-values" do
|
21
|
+
request = { "group" => ["foo", "bar", "baz"] }
|
22
|
+
|
23
|
+
expected = {
|
24
|
+
"group.0" => "foo",
|
25
|
+
"group.1" => "bar",
|
26
|
+
"group.2" => "baz"
|
27
|
+
}
|
28
|
+
|
29
|
+
assert_equal expected, Expander.expand(request)
|
30
|
+
end
|
31
|
+
|
32
|
+
test "ignores empty Array values" do
|
33
|
+
request = { "group" => [] }
|
34
|
+
expected = {}
|
35
|
+
|
36
|
+
assert_equal expected, Expander.expand(request)
|
37
|
+
end
|
38
|
+
|
39
|
+
test "converts Key of Range to FromKey ToKey" do
|
40
|
+
request = { "Port" => 1..3 }
|
41
|
+
expected = { "FromPort" => 1, "ToPort" => 3 }
|
42
|
+
|
43
|
+
assert_equal expected, Expander.expand(request)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
data/test/slop_test.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'contest'
|
2
|
+
require 'swirl/helpers'
|
3
|
+
|
4
|
+
class SlopTest < Test::Unit::TestCase
|
5
|
+
include Swirl::Helpers
|
6
|
+
|
7
|
+
test "camalize" do
|
8
|
+
assert_equal "foo", Slop.camalize(:foo)
|
9
|
+
assert_equal "fooBar", Slop.camalize(:foo_bar)
|
10
|
+
end
|
11
|
+
|
12
|
+
test "gets sloppy" do
|
13
|
+
slop = Slop.new({"fooBar" => "baz"})
|
14
|
+
assert_equal "baz", slop[:foo_bar]
|
15
|
+
end
|
16
|
+
|
17
|
+
test "honors keys already camalized" do
|
18
|
+
slop = Slop.new({"fooBar" => "baz"})
|
19
|
+
assert_equal "baz", slop["fooBar"]
|
20
|
+
end
|
21
|
+
|
22
|
+
test "raise InvalidKey if key not found" do
|
23
|
+
slop = Slop.new({})
|
24
|
+
assert_raises Slop::InvalidKey do
|
25
|
+
slop[:not_here]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: swirl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1.0"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Blake Mizerany
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-26 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: crack
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.1.4
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: ruby-hmac
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.3.2
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: contest
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.1.2
|
44
|
+
version:
|
45
|
+
description: A version agnostic EC2 ruby driver
|
46
|
+
email:
|
47
|
+
executables:
|
48
|
+
- swirl
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files:
|
52
|
+
- README.md
|
53
|
+
- LICENSE
|
54
|
+
files:
|
55
|
+
- LICENSE
|
56
|
+
- README.md
|
57
|
+
- list-types
|
58
|
+
- lib/swirl/ec2.rb
|
59
|
+
- lib/swirl/helpers.rb
|
60
|
+
- lib/swirl.rb
|
61
|
+
- test/compactor_test.rb
|
62
|
+
- test/expander_test.rb
|
63
|
+
- test/slop_test.rb
|
64
|
+
- bin/swirl
|
65
|
+
has_rdoc: true
|
66
|
+
homepage: http://github.com/bmizerany/swirl
|
67
|
+
licenses: []
|
68
|
+
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options:
|
71
|
+
- --line-numbers
|
72
|
+
- --inline-source
|
73
|
+
- --title
|
74
|
+
- Sinatra
|
75
|
+
- --main
|
76
|
+
- README.rdoc
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: "0"
|
84
|
+
version:
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: "0"
|
90
|
+
version:
|
91
|
+
requirements: []
|
92
|
+
|
93
|
+
rubyforge_project: swirl
|
94
|
+
rubygems_version: 1.3.5
|
95
|
+
signing_key:
|
96
|
+
specification_version: 2
|
97
|
+
summary: A version agnostic EC2 ruby driver
|
98
|
+
test_files:
|
99
|
+
- test/compactor_test.rb
|
100
|
+
- test/expander_test.rb
|
101
|
+
- test/slop_test.rb
|