swift_client 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +36 -17
- data/lib/swift_client/version.rb +1 -1
- data/lib/swift_client.rb +106 -45
- data/test/swift_client_test.rb +61 -1
- metadata +2 -2
data/README.md
CHANGED
@@ -23,13 +23,29 @@ Or install it yourself as:
|
|
23
23
|
First, connect to a Swift cluster:
|
24
24
|
|
25
25
|
```ruby
|
26
|
-
swift_client = SwiftClient.new(:auth_url => "https://example.com/auth/v1.0", :username => "account:username", :api_key => "
|
26
|
+
swift_client = SwiftClient.new(:auth_url => "https://example.com/auth/v1.0", :username => "account:username", :api_key => "api key", :temp_url_key => "temp url key", :storage_url => "https://example.com/v1/AUTH_account")
|
27
27
|
```
|
28
28
|
|
29
|
+
To connect via v2 you have to add version and method specific details:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
swift_client = SwiftClient.new(:auth_url => "https://auth.example.com/v2.0", :storage_url => "https://storage.example.com/v1/AUTH_account", :tenant_name => "tenant", :username => "username", :password => "password")
|
33
|
+
|
34
|
+
# OR
|
35
|
+
|
36
|
+
swift_client = SwiftClient.new(:auth_url => "https://auth.example.com/v2.0", :storage_url => "https://storage.example.com/v1/AUTH_account", :tenant_name => "tenant", :access_key => "access key", :secret_key => "secret key")
|
37
|
+
```
|
38
|
+
|
39
|
+
where `temp_url_key` and `storage_url` are optional.
|
40
|
+
|
29
41
|
SwiftClient will automatically reconnect in case the endpoint responds with 401
|
30
|
-
Unauthorized to one of your requests using the provided credentials.
|
31
|
-
|
32
|
-
SwiftClient
|
42
|
+
Unauthorized to one of your requests using the provided credentials. In case
|
43
|
+
the endpoint does not respond with 2xx to any of SwiftClient's requests,
|
44
|
+
SwiftClient will raise a `SwiftClient::ResponseError`. Otherwise, SwiftClient
|
45
|
+
responds with an `HTTParty::Response` object, such that you can call `#headers`
|
46
|
+
to access the response headers or `#body` as well as `#parsed_response` to
|
47
|
+
access the response body and JSON response. Checkout the
|
48
|
+
[HTTParty](https://github.com/jnunemaker/httparty) gem to learn more.
|
33
49
|
|
34
50
|
SwiftClient offers the following requests:
|
35
51
|
|
@@ -37,19 +53,22 @@ SwiftClient offers the following requests:
|
|
37
53
|
* post_account(headers = {}) -> HTTParty::Response
|
38
54
|
* head_containers -> HTTParty::Response
|
39
55
|
* get_containers(query = {}) -> HTTParty::Response
|
40
|
-
*
|
41
|
-
*
|
42
|
-
*
|
43
|
-
*
|
44
|
-
*
|
45
|
-
*
|
46
|
-
*
|
47
|
-
*
|
48
|
-
*
|
49
|
-
*
|
50
|
-
*
|
51
|
-
*
|
52
|
-
*
|
56
|
+
* paginate_containers(query = {}) -> Enumerator
|
57
|
+
* get_container(container_name, query = {}) -> HTTParty::Response
|
58
|
+
* paginate_container(container_name, query = {}) -> Enumerator
|
59
|
+
* head_container(container_name) -> HTTParty::Response
|
60
|
+
* put_container(container_name, headers = {}) -> HTTParty::Response
|
61
|
+
* post_container(container_name, headers = {}) -> HTTParty::Response
|
62
|
+
* delete_container(container_name) -> HTTParty::Response
|
63
|
+
* put_object(object_name, data_or_io, container_name, headers = {}) -> HTTParty::Response
|
64
|
+
* post_object(object_name, container_name, headers = {}) -> HTTParty::Response
|
65
|
+
* get_object(object_name, container_name) -> HTTParty::Response
|
66
|
+
* head_object(object_name, container_name) -> HTTParty::Response
|
67
|
+
* delete_object(object_name, container_name) -> HTTParty::Response
|
68
|
+
* get_objects(container_name, query = {}) -> HTTParty::Response
|
69
|
+
* paginate_objetcs(container_name, query = {}) -> Enumerator
|
70
|
+
* public_url(object_name, container_name) -> HTTParty::Response
|
71
|
+
* temp_url(object_name, container_name) -> HTTParty::Response
|
53
72
|
|
54
73
|
## Contributing
|
55
74
|
|
data/lib/swift_client/version.rb
CHANGED
data/lib/swift_client.rb
CHANGED
@@ -27,10 +27,6 @@ class SwiftClient
|
|
27
27
|
attr_accessor :options, :auth_token, :storage_url
|
28
28
|
|
29
29
|
def initialize(options = {})
|
30
|
-
[:auth_url, :username, :api_key].each do |key|
|
31
|
-
raise(OptionError, "#{key} is missing") unless options.key?(key)
|
32
|
-
end
|
33
|
-
|
34
30
|
self.options = options
|
35
31
|
|
36
32
|
authenticate
|
@@ -52,93 +48,105 @@ class SwiftClient
|
|
52
48
|
request :get, "/", :query => query
|
53
49
|
end
|
54
50
|
|
55
|
-
def
|
56
|
-
|
51
|
+
def paginate_containers(query = {}, &block)
|
52
|
+
paginate :get_containers, query, &block
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_container(container_name, query = {})
|
56
|
+
raise(EmptyNameError) if container_name.empty?
|
57
57
|
|
58
|
-
request :get, "/#{
|
58
|
+
request :get, "/#{container_name}", :query => query
|
59
59
|
end
|
60
60
|
|
61
|
-
def
|
62
|
-
|
61
|
+
def paginate_container(container_name, query = {}, &block)
|
62
|
+
paginate :get_container, container_name, query, &block
|
63
|
+
end
|
64
|
+
|
65
|
+
def head_container(container_name)
|
66
|
+
raise(EmptyNameError) if container_name.empty?
|
63
67
|
|
64
|
-
request :head, "/#{
|
68
|
+
request :head, "/#{container_name}"
|
65
69
|
end
|
66
70
|
|
67
|
-
def put_container(
|
68
|
-
raise(EmptyNameError) if
|
71
|
+
def put_container(container_name, headers = {})
|
72
|
+
raise(EmptyNameError) if container_name.empty?
|
69
73
|
|
70
|
-
request :put, "/#{
|
74
|
+
request :put, "/#{container_name}", :headers => headers
|
71
75
|
end
|
72
76
|
|
73
|
-
def post_container(
|
74
|
-
raise(EmptyNameError) if
|
77
|
+
def post_container(container_name, headers = {})
|
78
|
+
raise(EmptyNameError) if container_name.empty?
|
75
79
|
|
76
|
-
request :post, "/#{
|
80
|
+
request :post, "/#{container_name}", :headers => headers
|
77
81
|
end
|
78
82
|
|
79
|
-
def delete_container(
|
80
|
-
raise(EmptyNameError) if
|
83
|
+
def delete_container(container_name)
|
84
|
+
raise(EmptyNameError) if container_name.empty?
|
81
85
|
|
82
|
-
request :delete, "/#{
|
86
|
+
request :delete, "/#{container_name}"
|
83
87
|
end
|
84
88
|
|
85
|
-
def put_object(
|
86
|
-
raise(EmptyNameError) if
|
89
|
+
def put_object(object_name, data_or_io, container_name, headers = {})
|
90
|
+
raise(EmptyNameError) if object_name.empty? || container_name.empty?
|
87
91
|
|
88
|
-
mime_type = MIME::Types.of(
|
92
|
+
mime_type = MIME::Types.of(object_name).first
|
89
93
|
|
90
94
|
extended_headers = headers.dup
|
91
95
|
extended_headers["Content-Type"] ||= mime_type.content_type if mime_type
|
92
96
|
|
93
|
-
request :put, "/#{
|
97
|
+
request :put, "/#{container_name}/#{object_name}", :body => data_or_io.respond_to?(:read) ? data_or_io.read : data_or_io, :headers => extended_headers
|
94
98
|
end
|
95
99
|
|
96
|
-
def post_object(
|
97
|
-
raise(EmptyNameError) if
|
100
|
+
def post_object(object_name, container_name, headers = {})
|
101
|
+
raise(EmptyNameError) if object_name.empty? || container_name.empty?
|
98
102
|
|
99
|
-
request :post, "/#{
|
103
|
+
request :post, "/#{container_name}/#{object_name}", :headers => headers
|
100
104
|
end
|
101
105
|
|
102
|
-
def get_object(
|
103
|
-
raise(EmptyNameError) if
|
106
|
+
def get_object(object_name, container_name)
|
107
|
+
raise(EmptyNameError) if object_name.empty? || container_name.empty?
|
104
108
|
|
105
|
-
request :get, "/#{
|
109
|
+
request :get, "/#{container_name}/#{object_name}"
|
106
110
|
end
|
107
111
|
|
108
|
-
def head_object(
|
109
|
-
raise(EmptyNameError) if
|
112
|
+
def head_object(object_name, container_name)
|
113
|
+
raise(EmptyNameError) if object_name.empty? || container_name.empty?
|
110
114
|
|
111
|
-
request :head, "/#{
|
115
|
+
request :head, "/#{container_name}/#{object_name}"
|
112
116
|
end
|
113
117
|
|
114
|
-
def delete_object(
|
115
|
-
raise(EmptyNameError) if
|
118
|
+
def delete_object(object_name, container_name)
|
119
|
+
raise(EmptyNameError) if object_name.empty? || container_name.empty?
|
116
120
|
|
117
|
-
request :delete, "/#{
|
121
|
+
request :delete, "/#{container_name}/#{object_name}"
|
118
122
|
end
|
119
123
|
|
120
|
-
def get_objects(
|
121
|
-
raise(EmptyNameError) if
|
124
|
+
def get_objects(container_name, query = {})
|
125
|
+
raise(EmptyNameError) if container_name.empty?
|
126
|
+
|
127
|
+
request :get, "/#{container_name}", :query => query
|
128
|
+
end
|
122
129
|
|
123
|
-
|
130
|
+
def paginate_objects(container_name, query = {}, &block)
|
131
|
+
paginate :get_objects, container_name, query, &block
|
124
132
|
end
|
125
133
|
|
126
|
-
def public_url(
|
127
|
-
raise(EmptyNameError) if
|
134
|
+
def public_url(object_name, container_name)
|
135
|
+
raise(EmptyNameError) if object_name.empty? || container_name.empty?
|
128
136
|
|
129
|
-
"#{storage_url}/#{
|
137
|
+
"#{storage_url}/#{container_name}/#{object_name}"
|
130
138
|
end
|
131
139
|
|
132
|
-
def temp_url(
|
133
|
-
raise(EmptyNameError) if
|
140
|
+
def temp_url(object_name, container_name, opts = {})
|
141
|
+
raise(EmptyNameError) if object_name.empty? || container_name.empty?
|
134
142
|
raise(TempUrlKeyMissing) unless options[:temp_url_key]
|
135
143
|
|
136
144
|
expires = (Time.now + (options[:expires_in] || 3600).to_i).to_i
|
137
|
-
path = URI.parse("#{storage_url}/#{
|
145
|
+
path = URI.parse("#{storage_url}/#{container_name}/#{object_name}").path
|
138
146
|
|
139
147
|
signature = OpenSSL::HMAC.hexdigest("sha1", options[:temp_url_key], "GET\n#{expires}\n#{path}")
|
140
148
|
|
141
|
-
"#{storage_url}/#{
|
149
|
+
"#{storage_url}/#{container_name}/#{object_name}?temp_url_sig=#{signature}&temp_url_expires=#{expires}"
|
142
150
|
end
|
143
151
|
|
144
152
|
private
|
@@ -162,6 +170,14 @@ class SwiftClient
|
|
162
170
|
end
|
163
171
|
|
164
172
|
def authenticate
|
173
|
+
options[:auth_url] =~ /v2/ ? authenticate_v2 : authenticate_v1
|
174
|
+
end
|
175
|
+
|
176
|
+
def authenticate_v1
|
177
|
+
[:auth_url, :username, :api_key].each do |key|
|
178
|
+
raise(AuthenticationError, "#{key} missing") unless options[key]
|
179
|
+
end
|
180
|
+
|
165
181
|
response = HTTParty.get(options[:auth_url], :headers => { "X-Auth-User" => options[:username], "X-Auth-Key" => options[:api_key] })
|
166
182
|
|
167
183
|
raise(AuthenticationError, "#{response.code}: #{response.message}") unless response.success?
|
@@ -169,5 +185,50 @@ class SwiftClient
|
|
169
185
|
self.auth_token = response.headers["X-Auth-Token"]
|
170
186
|
self.storage_url = options[:storage_url] || response.headers["X-Storage-Url"]
|
171
187
|
end
|
188
|
+
|
189
|
+
def authenticate_v2
|
190
|
+
[:auth_url, :storage_url].each do |key|
|
191
|
+
raise(AuthenticationError, "#{key} missing") unless options[key]
|
192
|
+
end
|
193
|
+
|
194
|
+
auth = { "auth" => {} }
|
195
|
+
|
196
|
+
if options[:tenant_name]
|
197
|
+
auth["auth"]["tenantName"] = options[:tenant_name]
|
198
|
+
else
|
199
|
+
raise AuthenticationError, "No tenant specified"
|
200
|
+
end
|
201
|
+
|
202
|
+
if options[:username] && options[:password]
|
203
|
+
auth["auth"]["passwordCredentials"] = { "username" => options[:username], "password" => options[:password] }
|
204
|
+
elsif options[:access_key] && options[:secret_key]
|
205
|
+
auth["auth"]["apiAccessKeyCredentials"] = { "accessKey" => options[:access_key], "secretKey" => options[:secret_key] }
|
206
|
+
else
|
207
|
+
raise AuthenticationError, "Unknown authentication method"
|
208
|
+
end
|
209
|
+
|
210
|
+
response = HTTParty.post("#{options[:auth_url].gsub(/\/+$/, "")}/tokens", :body => JSON.dump(auth), :headers => { "Content-Type" => "application/json" })
|
211
|
+
|
212
|
+
raise(AuthenticationError, "#{response.code}: #{response.message}") unless response.success?
|
213
|
+
|
214
|
+
self.auth_token = response.parsed_response["access"]["token"]["id"]
|
215
|
+
self.storage_url = options[:storage_url]
|
216
|
+
end
|
217
|
+
|
218
|
+
def paginate(method, *args, query)
|
219
|
+
return enum_for(:paginate, method, *args, query) unless block_given?
|
220
|
+
|
221
|
+
marker = nil
|
222
|
+
|
223
|
+
loop do
|
224
|
+
response = send(method, *args, marker ? query.merge(:marker => marker) : query)
|
225
|
+
|
226
|
+
return if response.parsed_response.empty?
|
227
|
+
|
228
|
+
yield response
|
229
|
+
|
230
|
+
marker = response.parsed_response.last["name"]
|
231
|
+
end
|
232
|
+
end
|
172
233
|
end
|
173
234
|
|
data/test/swift_client_test.rb
CHANGED
@@ -11,6 +11,24 @@ class SwiftClientTest < MiniTest::Test
|
|
11
11
|
assert_equal "https://example.com/v1/AUTH_account", @swift_client.storage_url
|
12
12
|
end
|
13
13
|
|
14
|
+
def test_v2_authentication_with_password
|
15
|
+
stub_request(:post, "https://auth.example.com/v2.0/tokens").with(:body => JSON.dump("auth" => { "tenantName" => "Tenant", "passwordCredentials" => { "username" => "Username", :password => "Password" }})).to_return(:status => 200, :body => JSON.dump("access" => { "token" => { "id" => "Token" }}), :headers => { "Content-Type" => "application/json" })
|
16
|
+
|
17
|
+
@swift_client = SwiftClient.new(:storage_url => "https://example.com/v1/AUTH_account", :auth_url => "https://auth.example.com/v2.0", :tenant_name => "Tenant", :username => "Username", :password => "Password")
|
18
|
+
|
19
|
+
assert_equal "Token", @swift_client.auth_token
|
20
|
+
assert_equal "https://example.com/v1/AUTH_account", @swift_client.storage_url
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_v2_authentication_with_key
|
24
|
+
stub_request(:post, "https://auth.example.com/v2.0/tokens").with(:body => JSON.dump("auth" => { "tenantName" => "Tenant", "apiAccessKeyCredentials" => { "accessKey" => "AccessKey", :secretKey => "SecretKey" }})).to_return(:status => 200, :body => JSON.dump("access" => { "token" => { "id" => "Token" }}), :headers => { "Content-Type" => "application/json" })
|
25
|
+
|
26
|
+
@swift_client = SwiftClient.new(:storage_url => "https://example.com/v1/AUTH_account", :auth_url => "https://auth.example.com/v2.0", :tenant_name => "Tenant", :access_key => "AccessKey", :secret_key => "SecretKey")
|
27
|
+
|
28
|
+
assert_equal "Token", @swift_client.auth_token
|
29
|
+
assert_equal "https://example.com/v1/AUTH_account", @swift_client.storage_url
|
30
|
+
end
|
31
|
+
|
14
32
|
def test_storage_url
|
15
33
|
stub_request(:get, "https://example.com/auth/v1.0").with(:headers => { "X-Auth-Key" => "secret", "X-Auth-User" => "account:username" }).to_return(:status => 200, :body => "", :headers => { "X-Auth-Token" => "Token", "X-Storage-Url" => "https://example.com/v1/AUTH_account" })
|
16
34
|
|
@@ -49,6 +67,20 @@ class SwiftClientTest < MiniTest::Test
|
|
49
67
|
assert_equal containers, @swift_client.get_containers.parsed_response
|
50
68
|
end
|
51
69
|
|
70
|
+
def test_paginate_containers
|
71
|
+
containers = [
|
72
|
+
{ "count" => 1, "bytes" => 1, "name" => "container-1" },
|
73
|
+
{ "count" => 1, "bytes" => 1, "name" => "container-2" },
|
74
|
+
{ "count" => 1, "bytes" => 1, "name" => "container-3" }
|
75
|
+
]
|
76
|
+
|
77
|
+
stub_request(:get, "https://example.com/v1/AUTH_account/?limit=2").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => JSON.dump(containers[0 .. 1]), :headers => { "Content-Type" => "application/json" })
|
78
|
+
stub_request(:get, "https://example.com/v1/AUTH_account/?limit=2&marker=container-2").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => JSON.dump(containers[2 .. 3]), :headers => { "Content-Type" => "application/json" })
|
79
|
+
stub_request(:get, "https://example.com/v1/AUTH_account/?limit=2&marker=container-3").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => JSON.dump([]), :headers => { "Content-Type" => "application/json" })
|
80
|
+
|
81
|
+
assert_equal containers, @swift_client.paginate_containers(:limit => 2).collect(&:parsed_response).flatten
|
82
|
+
end
|
83
|
+
|
52
84
|
def test_get_container
|
53
85
|
objects = [
|
54
86
|
{ "hash" => "Hash", "last_modified" => "Last modified", "bytes" => 1, "name" => "object-2", "content_type" => "Content type" },
|
@@ -60,6 +92,20 @@ class SwiftClientTest < MiniTest::Test
|
|
60
92
|
assert_equal objects, @swift_client.get_container("container-1", :limit => 2, :marker => "object-2").parsed_response
|
61
93
|
end
|
62
94
|
|
95
|
+
def test_paginate_container
|
96
|
+
objects = [
|
97
|
+
{ "hash" => "Hash", "last_modified" => "Last modified", "bytes" => 1, "name" => "object-1", "content_type" => "Content type" },
|
98
|
+
{ "hash" => "Hash", "last_modified" => "Last modified", "bytes" => 1, "name" => "object-2", "content_type" => "Content type" },
|
99
|
+
{ "hash" => "Hash", "last_modified" => "Last modified", "bytes" => 1, "name" => "object-3", "content_type" => "Content type" },
|
100
|
+
]
|
101
|
+
|
102
|
+
stub_request(:get, "https://example.com/v1/AUTH_account/container-1?limit=2").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => JSON.dump(objects[0 .. 1]), :headers => { "Content-Type" => "application/json" })
|
103
|
+
stub_request(:get, "https://example.com/v1/AUTH_account/container-1?limit=2&marker=object-2").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => JSON.dump(objects[2 .. 3]), :headers => { "Content-Type" => "application/json" })
|
104
|
+
stub_request(:get, "https://example.com/v1/AUTH_account/container-1?limit=2&marker=object-3").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => JSON.dump([]), :headers => { "Content-Type" => "application/json" })
|
105
|
+
|
106
|
+
assert_equal objects, @swift_client.paginate_container("container-1", :limit => 2).collect(&:parsed_response).flatten
|
107
|
+
end
|
108
|
+
|
63
109
|
def test_head_container
|
64
110
|
stub_request(:head, "https://example.com/v1/AUTH_account/container-1").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 204, :body => "", :headers => { "Content-Type" => "application/json" })
|
65
111
|
|
@@ -128,7 +174,21 @@ class SwiftClientTest < MiniTest::Test
|
|
128
174
|
|
129
175
|
stub_request(:get, "https://example.com/v1/AUTH_account/container-1?limit=2&marker=object-2").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => JSON.dump(objects), :headers => { "Content-Type" => "application/json" })
|
130
176
|
|
131
|
-
assert_equal objects, @swift_client.
|
177
|
+
assert_equal objects, @swift_client.get_objects("container-1", :limit => 2, :marker => "object-2").parsed_response
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_paginate_objects
|
181
|
+
objects = [
|
182
|
+
{ "hash" => "Hash", "last_modified" => "Last modified", "bytes" => 1, "name" => "object-1", "content_type" => "Content type" },
|
183
|
+
{ "hash" => "Hash", "last_modified" => "Last modified", "bytes" => 1, "name" => "object-2", "content_type" => "Content type" },
|
184
|
+
{ "hash" => "Hash", "last_modified" => "Last modified", "bytes" => 1, "name" => "object-3", "content_type" => "Content type" }
|
185
|
+
]
|
186
|
+
|
187
|
+
stub_request(:get, "https://example.com/v1/AUTH_account/container-1?limit=2").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => JSON.dump(objects[0 .. 1]), :headers => { "Content-Type" => "application/json" })
|
188
|
+
stub_request(:get, "https://example.com/v1/AUTH_account/container-1?limit=2&marker=object-2").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => JSON.dump(objects[2 .. 3]), :headers => { "Content-Type" => "application/json" })
|
189
|
+
stub_request(:get, "https://example.com/v1/AUTH_account/container-1?limit=2&marker=object-3").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => JSON.dump([]), :headers => { "Content-Type" => "application/json" })
|
190
|
+
|
191
|
+
assert_equal objects, @swift_client.paginate_objects("container-1", :limit => 2).collect(&:parsed_response).flatten
|
132
192
|
end
|
133
193
|
|
134
194
|
def test_not_found
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: swift_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-01-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: httparty
|