swift_client 0.1.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: cb7d0291c3fcc0d03fd4aa4711c8c7c4a06b46bf
4
- data.tar.gz: 8caa6d7f8e4e21ce1567f7d968e6197fd83760f4
2
+ SHA256:
3
+ metadata.gz: 9703134ee4b5656b16c32f49305288723eb7f66d6942c2f6392ac9f663803c96
4
+ data.tar.gz: 8f97e98bc8c83e549e4359b53891e0d12b4673a8990426704658a3c78289e546
5
5
  SHA512:
6
- metadata.gz: 88d70e38c7d901e1625c03d12e7a9e016f7d26a3687c1ad3282cc7c29847a3cdfc168ce275453cfd69aff8e27a7ece108c25d2438c3e989beae693d8c17688dd
7
- data.tar.gz: eada405a14079e11075ba3a378461c2975da0ef2107609dcf706dca2373da5fde6eea263376873bab01455e38d84d2dd512feacc9a15b84c9395ef55ae0b2bbc
6
+ metadata.gz: b06d3358d7d40de924ccdaff5e1de219e1d4882ea5e607f9d9fdff47b260ce57338dbdc92d19f80c5b9908257d3397ca37b6ab38e6a0353ead8f63124e983659
7
+ data.tar.gz: 1c5fa028f57c0f460d4f4a28b52534033cf4045212ba64df9bd32d58fa2291377a81d5b815249b79e51db71c526405123b23991e94c09daeed2043bb2c9ee624
data/.gitignore CHANGED
@@ -12,3 +12,4 @@
12
12
  *.o
13
13
  *.a
14
14
  mkmf.log
15
+ .ruby-version
@@ -1,9 +1,8 @@
1
1
 
2
2
  rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - 2.1.7
6
- - 2.2.3
3
+ - 2.1.10
4
+ - 2.2.5
5
+ - 2.3.1
7
6
 
8
7
  install:
9
8
  - "travis_retry bundle install"
data/README.md CHANGED
@@ -101,7 +101,8 @@ swift_client = SwiftClient.new(
101
101
  :auth_url => "https://auth.example.com/v3",
102
102
  :storage_url => "https://storage.example.com/v1/AUTH_account",
103
103
  :user_id => "user id",
104
- :password => "password"
104
+ :password => "password",
105
+ :interface => "internal"
105
106
  )
106
107
 
107
108
  # OR
@@ -126,33 +127,83 @@ access the response body and JSON response. Checkout the
126
127
 
127
128
  SwiftClient offers the following requests:
128
129
 
129
- * head_account -> HTTParty::Response
130
- * post_account(headers = {}) -> HTTParty::Response
131
- * head_containers -> HTTParty::Response
132
- * get_containers(query = {}) -> HTTParty::Response
133
- * paginate_containers(query = {}) -> Enumerator
134
- * get_container(container_name, query = {}) -> HTTParty::Response
135
- * paginate_container(container_name, query = {}) -> Enumerator
136
- * head_container(container_name) -> HTTParty::Response
137
- * put_container(container_name, headers = {}) -> HTTParty::Response
138
- * post_container(container_name, headers = {}) -> HTTParty::Response
139
- * delete_container(container_name) -> HTTParty::Response
140
- * put_object(object_name, data_or_io, container_name, headers = {}) -> HTTParty::Response
141
- * post_object(object_name, container_name, headers = {}) -> HTTParty::Response
142
- * get_object(object_name, container_name) -> HTTParty::Response
143
- * head_object(object_name, container_name) -> HTTParty::Response
144
- * delete_object(object_name, container_name) -> HTTParty::Response
145
- * get_objects(container_name, query = {}) -> HTTParty::Response
146
- * paginate_objects(container_name, query = {}) -> Enumerator
147
- * public_url(object_name, container_name) -> HTTParty::Response
148
- * temp_url(object_name, container_name, options = {}) -> HTTParty::Response
149
- * bulk_delete(entries) -> entries
130
+ * `head_account(options = {}) # => HTTParty::Response`
131
+ * `post_account(headers = {}, options = {}) # => HTTParty::Response`
132
+ * `head_containers(options = {}) # => HTTParty::Response`
133
+ * `get_containers(query = {}, options = {}) # => HTTParty::Response`
134
+ * `paginate_containers(query = {}, options = {}) # => Enumerator`
135
+ * `get_container(container_name, query = {}, options = {}) # => HTTParty::Response`
136
+ * `paginate_container(container_name, query = {}, options = {}) # => Enumerator`
137
+ * `head_container(container_name, options = {}) # => HTTParty::Response`
138
+ * `put_container(container_name, headers = {}, options = {}) # => HTTParty::Response`
139
+ * `post_container(container_name, headers = {}, options = {}) # => HTTParty::Response`
140
+ * `delete_container(container_name, options = {}) # => HTTParty::Response`
141
+ * `put_object(object_name, data_or_io, container_name, headers = {}, options = {}) # => HTTParty::Response`
142
+ * `post_object(object_name, container_name, headers = {}, options = {}) # => HTTParty::Response`
143
+ * `get_object(object_name, container_name, options = {}) -> HTTParty::Response`
144
+ * `get_object(object_name, container_name, options = {}) { |chunk| save chunk } # => HTTParty::Response`
145
+ * `head_object(object_name, container_name, options = {}) # => HTTParty::Response`
146
+ * `delete_object(object_name, container_name, options = {}) # => HTTParty::Response`
147
+ * `get_objects(container_name, query = {}, options = {}) # => HTTParty::Response`
148
+ * `paginate_objects(container_name, query = {}, options = {}) # => Enumerator`
149
+ * `public_url(object_name, container_name) # => HTTParty::Response`
150
+ * `temp_url(object_name, container_name, options = {}) # => HTTParty::Response`
151
+ * `bulk_delete(entries, options = {}) # => entries`
152
+ * `post_head(object_name, container_name, _headers = {}, options = {}) # => HTTParty::Response`
153
+
154
+ By default, the client instructs the Swift server to return JSON via an HTTP Accept header; to disable this pass `:json => false` in `options`. The rest of the `options` are passed directly to the internal [HTTParty](https://rubygems.org/gems/httparty) client.
155
+
156
+ ### Getting large objects
157
+ The `get_object` method with out a block is suitable for small objects that easily fit in memory. For larger objects, specify a block to process chunked data as it comes in.
158
+
159
+ ```ruby
160
+ File.open("/tmp/output", "wb") do |file_io|
161
+ swift_client.get_object("/large/object", "container") do |chunk|
162
+ file_io.write(chunk)
163
+ end
164
+ end
165
+ ```
166
+
167
+ ## Re-Using/Sharing/Caching Auth Tokens
168
+
169
+ Certain OpenStack/Swift providers have limits in place regarding token
170
+ generation. To re-use auth tokens by caching them via memcached, install dalli
171
+
172
+ `gem install dalli`
173
+
174
+ and provide an instance of Dalli::Client to SwiftClient:
175
+
176
+ ```ruby
177
+ swift_client = SwiftClient.new(
178
+ :auth_url => "https://example.com/auth/v1.0",
179
+ ...
180
+ :cache_store => Dalli::Client.new
181
+ )
182
+ ```
183
+
184
+ The cache key used to store the auth token will include all neccessary details
185
+ to ensure the auth token won't be used for a different swift account erroneously.
186
+
187
+ The cache implementation of SwiftClient is not restricted to memcached. To use
188
+ a different one, simply implement a driver for your favorite cache store. See
189
+ [null_cache.rb](https://github.com/mrkamel/swift_client/blob/master/lib/swift_client/null_cache.rb)
190
+ for more info.
150
191
 
151
192
  ## bulk_delete
152
193
 
153
194
  Takes an array containing container_name/object_name entries.
154
195
  Automatically slices and sends 1_000 items per request.
155
196
 
197
+ ## Non-chunked uploads
198
+
199
+ By default files are uploaded in chunks and using a `Transfer-Encoding:
200
+ chunked` header. You can override this by passing a `Transfer-Encoding:
201
+ identity` header:
202
+
203
+ ```ruby
204
+ put_object(object_name, data_or_io, container_name, "Transfer-Encoding" => "identity")
205
+ ```
206
+
156
207
  ## Contributing
157
208
 
158
209
  1. Fork it ( https://github.com/mrkamel/swift_client/fork )
@@ -160,3 +211,8 @@ Automatically slices and sends 1_000 items per request.
160
211
  3. Commit your changes (`git commit -am 'Add some feature'`)
161
212
  4. Push to the branch (`git push origin my-new-feature`)
162
213
  5. Create a new Pull Request
214
+
215
+ ## Semantic Versioning
216
+
217
+ Starting with version 0.2.0, SwiftClient uses Semantic Versioning:
218
+ [SemVer](http://semver.org/)
@@ -37,61 +37,61 @@ class SwiftClient
37
37
  authenticate
38
38
  end
39
39
 
40
- def head_account
41
- request :head, "/"
40
+ def head_account(options = {})
41
+ request :head, "/", options
42
42
  end
43
43
 
44
- def post_account(headers = {})
45
- request :post, "/", :headers => headers
44
+ def post_account(headers = {}, options = {})
45
+ request :post, "/", options.merge(:headers => headers)
46
46
  end
47
47
 
48
- def head_containers
49
- request :head, "/"
48
+ def head_containers(options = {})
49
+ request :head, "/", options
50
50
  end
51
51
 
52
- def get_containers(query = {})
53
- request :get, "/", :query => query
52
+ def get_containers(query = {}, options = {})
53
+ request :get, "/", options.merge(:query => query)
54
54
  end
55
55
 
56
- def paginate_containers(query = {}, &block)
57
- paginate :get_containers, query, &block
56
+ def paginate_containers(query = {}, options = {}, &block)
57
+ paginate(:get_containers, query, options, &block)
58
58
  end
59
59
 
60
- def get_container(container_name, query = {})
60
+ def get_container(container_name, query = {}, options = {})
61
61
  raise(EmptyNameError) if container_name.empty?
62
62
 
63
- request :get, "/#{container_name}", :query => query
63
+ request :get, "/#{container_name}", options.merge(:query => query)
64
64
  end
65
65
 
66
- def paginate_container(container_name, query = {}, &block)
67
- paginate :get_container, container_name, query, &block
66
+ def paginate_container(container_name, query = {}, options = {}, &block)
67
+ paginate(:get_container, container_name, query, options, &block)
68
68
  end
69
69
 
70
- def head_container(container_name)
70
+ def head_container(container_name, options = {})
71
71
  raise(EmptyNameError) if container_name.empty?
72
72
 
73
- request :head, "/#{container_name}"
73
+ request :head, "/#{container_name}", options
74
74
  end
75
75
 
76
- def put_container(container_name, headers = {})
76
+ def put_container(container_name, headers = {}, options = {})
77
77
  raise(EmptyNameError) if container_name.empty?
78
78
 
79
- request :put, "/#{container_name}", :headers => headers
79
+ request :put, "/#{container_name}", options.merge(:headers => headers)
80
80
  end
81
81
 
82
- def post_container(container_name, headers = {})
82
+ def post_container(container_name, headers = {}, options = {})
83
83
  raise(EmptyNameError) if container_name.empty?
84
84
 
85
- request :post, "/#{container_name}", :headers => headers
85
+ request :post, "/#{container_name}", options.merge(:headers => headers)
86
86
  end
87
87
 
88
- def delete_container(container_name)
88
+ def delete_container(container_name, options = {})
89
89
  raise(EmptyNameError) if container_name.empty?
90
90
 
91
- request :delete, "/#{container_name}"
91
+ request :delete, "/#{container_name}", options
92
92
  end
93
93
 
94
- def put_object(object_name, data_or_io, container_name, headers = {})
94
+ def put_object(object_name, data_or_io, container_name, headers = {}, options = {})
95
95
  raise(EmptyNameError) if object_name.empty? || container_name.empty?
96
96
 
97
97
  mime_type = MIME::Types.of(object_name).first
@@ -103,43 +103,52 @@ class SwiftClient
103
103
  extended_headers["Content-Type"] ||= "application/octet-stream"
104
104
  end
105
105
 
106
- extended_headers["Transfer-Encoding"] = "chunked"
106
+ if extended_headers["Transfer-Encoding"] == "identity"
107
+ request :put, "/#{container_name}/#{object_name}", options.merge(:body => data_or_io.respond_to?(:read) ? data_or_io.read : data_or_io, :headers => extended_headers)
108
+ else
109
+ extended_headers["Transfer-Encoding"] = "chunked"
110
+ request :put, "/#{container_name}/#{object_name}", options.merge(:body_stream => data_or_io.respond_to?(:read) ? data_or_io : StringIO.new(data_or_io), :headers => extended_headers)
111
+ end
112
+ end
107
113
 
108
- request :put, "/#{container_name}/#{object_name}", :body_stream => data_or_io.respond_to?(:read) ? data_or_io : StringIO.new(data_or_io), :headers => extended_headers
114
+ def post_object(object_name, container_name, headers = {}, options = {})
115
+ raise(EmptyNameError) if object_name.empty? || container_name.empty?
116
+
117
+ request :post, "/#{container_name}/#{object_name}", options.merge(:headers => headers)
109
118
  end
110
119
 
111
- def post_object(object_name, container_name, headers = {})
120
+ def get_object(object_name, container_name, options = {}, &block)
112
121
  raise(EmptyNameError) if object_name.empty? || container_name.empty?
113
122
 
114
- request :post, "/#{container_name}/#{object_name}", :headers => headers
123
+ request(:get, "/#{container_name}/#{object_name}", options.merge(block ? { :stream_body => true } : {}), &block)
115
124
  end
116
125
 
117
- def get_object(object_name, container_name)
126
+ def head_object(object_name, container_name, options = {})
118
127
  raise(EmptyNameError) if object_name.empty? || container_name.empty?
119
128
 
120
- request :get, "/#{container_name}/#{object_name}"
129
+ request :head, "/#{container_name}/#{object_name}", options
121
130
  end
122
131
 
123
- def head_object(object_name, container_name)
132
+ def post_head(object_name, container_name, _headers = {}, options = {})
124
133
  raise(EmptyNameError) if object_name.empty? || container_name.empty?
125
134
 
126
- request :head, "/#{container_name}/#{object_name}"
135
+ request :post, "/#{container_name}/#{object_name}", options.merge(headers: _headers)
127
136
  end
128
137
 
129
- def delete_object(object_name, container_name)
138
+ def delete_object(object_name, container_name, options = {})
130
139
  raise(EmptyNameError) if object_name.empty? || container_name.empty?
131
140
 
132
- request :delete, "/#{container_name}/#{object_name}"
141
+ request :delete, "/#{container_name}/#{object_name}", options
133
142
  end
134
143
 
135
- def get_objects(container_name, query = {})
144
+ def get_objects(container_name, query = {}, options = {})
136
145
  raise(EmptyNameError) if container_name.empty?
137
146
 
138
- request :get, "/#{container_name}", :query => query
147
+ request :get, "/#{container_name}", options.merge(:query => query)
139
148
  end
140
149
 
141
- def paginate_objects(container_name, query = {}, &block)
142
- paginate :get_objects, container_name, query, &block
150
+ def paginate_objects(container_name, query = {}, options = {}, &block)
151
+ paginate(:get_objects, container_name, query, options, &block)
143
152
  end
144
153
 
145
154
  def public_url(object_name, container_name)
@@ -160,9 +169,9 @@ class SwiftClient
160
169
  "#{storage_url}/#{container_name}/#{object_name}?temp_url_sig=#{signature}&temp_url_expires=#{expires}"
161
170
  end
162
171
 
163
- def bulk_delete(items)
172
+ def bulk_delete(items, options = {})
164
173
  items.each_slice(1_000) do |slice|
165
- request :delete, "/?bulk-delete", :body => slice.join("\n"), :headers => { "Content-Type" => "text/plain" }
174
+ request :delete, "/?bulk-delete", options.merge(:body => slice.join("\n"), :headers => { "Content-Type" => "text/plain" })
166
175
  end
167
176
 
168
177
  items
@@ -183,21 +192,21 @@ class SwiftClient
183
192
  headers.keys.detect { |k| k.downcase == key.downcase }
184
193
  end
185
194
 
186
- def request(method, path, opts = {})
195
+ def request(method, path, opts = {}, &block)
187
196
  headers = (opts[:headers] || {}).dup
188
197
  headers["X-Auth-Token"] = auth_token
189
- headers["Accept"] = "application/json"
198
+ headers["Accept"] ||= "application/json" unless opts.delete(:json) == false
190
199
 
191
200
  stream_pos = opts[:body_stream].pos if opts[:body_stream]
192
201
 
193
- response = HTTParty.send(method, "#{storage_url}#{path}", opts.merge(:headers => headers))
202
+ response = HTTParty.send(method, "#{storage_url}#{path}", opts.merge(:headers => headers), &block)
194
203
 
195
204
  if response.code == 401
196
205
  authenticate
197
206
 
198
207
  opts[:body_stream].pos = stream_pos if opts[:body_stream]
199
208
 
200
- return request(method, path, opts)
209
+ return request(method, path, opts, &block)
201
210
  end
202
211
 
203
212
  raise(ResponseError.new(response.code, response.message)) unless response.success?
@@ -333,7 +342,8 @@ class SwiftClient
333
342
 
334
343
  return unless swift_services.size == 1
335
344
 
336
- swift_endpoints = swift_service["endpoints"].select { |endpoint| endpoint["interface"] == "public" }
345
+ interface = options[:interface] || "public"
346
+ swift_endpoints = swift_service["endpoints"].select { |endpoint| endpoint["interface"] == interface }
337
347
  swift_endpoint = swift_endpoints.first
338
348
 
339
349
  return unless swift_endpoints.size == 1
@@ -341,13 +351,13 @@ class SwiftClient
341
351
  swift_endpoint["url"]
342
352
  end
343
353
 
344
- def paginate(method, *args, query)
345
- return enum_for(:paginate, method, *args, query) unless block_given?
354
+ def paginate(method, *args, query, options)
355
+ return enum_for(:paginate, method, *args, query, options) unless block_given?
346
356
 
347
357
  marker = nil
348
358
 
349
359
  loop do
350
- response = send(method, *args, marker ? query.merge(:marker => marker) : query)
360
+ response = send(method, *args, marker ? query.merge(:marker => marker) : query, options)
351
361
 
352
362
  return if response.parsed_response.empty?
353
363
 
@@ -357,4 +367,3 @@ class SwiftClient
357
367
  end
358
368
  end
359
369
  end
360
-
@@ -1,5 +1,4 @@
1
1
 
2
2
  class SwiftClient
3
- VERSION = "0.1.4"
3
+ VERSION = "0.3.0"
4
4
  end
5
-
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency "httparty"
22
- spec.add_dependency "mime-types", "< 3.0"
22
+ spec.add_dependency "mime-types"
23
23
 
24
24
  spec.add_development_dependency "bundler"
25
25
  spec.add_development_dependency "rake"
@@ -77,6 +77,15 @@ class SwiftClientTest < MiniTest::Test
77
77
  assert_equal "https://example.com/v1/AUTH_account", @swift_client.storage_url
78
78
  end
79
79
 
80
+ def test_v3_authentication_storage_url_from_catalog_with_multiple_endpoints
81
+ stub_request(:post, "https://auth.example.com/v3/auth/tokens").with(:body => JSON.dump("auth" => { "identity" => { "methods" => ["password"], "password" => { "user" => { "name" => "username", "password" => "secret", "domain" => { "name" => "example.com" }}}}})).to_return(:status => 200, :body => JSON.dump("token" => { "catalog"=> [{ "type" => "object-store", "endpoints" => [{ "interface"=>"public", "url"=> "https://example.com/v1/AUTH_account" }, { "interface"=>"internal", "url"=> "https://example.com/v2/AUTH_account" }] }] }), :headers => { "X-Subject-Token" => "Token", "Content-Type" => "application/json" })
82
+
83
+ @swift_client = SwiftClient.new(:auth_url => "https://auth.example.com/v3", :username => "username", :user_domain => "example.com", :password => "secret", :interface => "internal")
84
+
85
+ assert_equal "Token", @swift_client.auth_token
86
+ assert_equal "https://example.com/v2/AUTH_account", @swift_client.storage_url
87
+ end
88
+
80
89
  def test_v3_authentication_without_storage_url_and_multiple_swifts
81
90
  stub_request(:post, "https://auth.example.com/v3/auth/tokens").with(:body => JSON.dump("auth" => { "identity" => { "methods" => ["password"], "password" => { "user" => { "name" => "username", "password" => "secret", "domain" => { "name" => "example.com" }}}}})).to_return(:status => 200, :body => JSON.dump("token" => { "catalog"=> [{ "type" => "object-store", "endpoints" => [{ "interface"=>"public", "url"=> "https://example.com/v1/AUTH_account" }] }, { "type" => "object-store", "endpoints" => [{ "interface"=>"public", "url"=> "https://example.com/v1/AUTH_account" }] }] }), :headers => { "X-Subject-Token" => "Token", "Content-Type" => "application/json" })
82
91
 
@@ -245,6 +254,12 @@ class SwiftClientTest < MiniTest::Test
245
254
  assert_equal 201, @swift_client.put_object("object", "data", "container", "X-Object-Meta-Test" => "Test").code
246
255
  end
247
256
 
257
+ def test_put_object_nonchunked
258
+ stub_request(:put, "https://example.com/v1/AUTH_account/container/object").with(:body => "data", :headers => { "Transfer-Encoding" => "identity", "Content-Type" => "application/octet-stream", "Accept" => "application/json", "X-Auth-Token" => "Token", "X-Object-Meta-Test" => "Test" }).to_return(:status => 201, :body => "", :headers => {})
259
+
260
+ assert_equal 201, @swift_client.put_object("object", "data", "container", "X-Object-Meta-Test" => "Test", "Transfer-Encoding" => "identity").code
261
+ end
262
+
248
263
  def test_put_object_with_renewed_authorization
249
264
  stub_request(:put, "https://example.com/v1/AUTH_account/container/object").with(:body => "data", :headers => { "Transfer-Encoding" => "chunked", "Content-Type" => "application/octet-stream", "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return({ :status => 401, :body => "", :headers => {}}, { :status => 201, :body => "", :headers => {}})
250
265
 
@@ -269,16 +284,38 @@ class SwiftClientTest < MiniTest::Test
269
284
  assert_equal 201, @swift_client.put_object("object", StringIO.new("data"), "container", "X-Object-Meta-Test" => "Test").code
270
285
  end
271
286
 
287
+ def test_put_object_with_io_nonchunked
288
+ stub_request(:put, "https://example.com/v1/AUTH_account/container/object").with(:body => "data", :headers => { "Transfer-Encoding" => "identity", "Accept" => "application/json", "X-Auth-Token" => "Token", "X-Object-Meta-Test" => "Test" }).to_return(:status => 201, :body => "", :headers => {})
289
+
290
+ assert_equal 201, @swift_client.put_object("object", StringIO.new("data"), "container", "X-Object-Meta-Test" => "Test", "Transfer-Encoding" => "identity").code
291
+ end
292
+
272
293
  def test_post_object
273
294
  stub_request(:post, "https://example.com/v1/AUTH_account/container/object").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token", "X-Object-Meta-Test" => "Test" }).to_return(:status => 201, :body => "", :headers => {})
274
295
 
275
296
  assert_equal 201, @swift_client.post_object("object", "container", "X-Object-Meta-Test" => "Test").code
276
297
  end
277
298
 
299
+ def test_post_head
300
+ stub_request(:post, 'https://example.com/v1/AUTH_account/container/object').with(headers: { 'Accept' => 'application/json', 'X-Auth-Token' => 'Token', 'X-Delete-At' => '1553524860' }).to_return(status: 201, body: '', headers: {})
301
+
302
+ assert_equal 201, @swift_client.post_head('object', 'container', 'X-Delete-At' => '1553524860').code
303
+ end
304
+
278
305
  def test_get_object
279
306
  stub_request(:get, "https://example.com/v1/AUTH_account/container/object").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => "Body", :headers => {})
307
+ block_res = 0
308
+
309
+ large_body = "Body" * 16384
310
+ stub_request(:get, "https://example.com/v1/AUTH_account/container/large_object").with(:headers => { "Accept" => "application/json", "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => large_body, :headers => {})
280
311
 
281
312
  assert_equal "Body", @swift_client.get_object("object", "container").body
313
+
314
+ @swift_client.get_object("large_object", "container") do |chunk|
315
+ block_res += chunk.length
316
+ end
317
+
318
+ assert_equal block_res, large_body.length
282
319
  end
283
320
 
284
321
  def test_head_object
@@ -304,6 +341,14 @@ class SwiftClientTest < MiniTest::Test
304
341
  assert_equal objects, @swift_client.get_objects("container-1", :limit => 2, :marker => "object-2").parsed_response
305
342
  end
306
343
 
344
+ def test_get_objects_no_json
345
+ response = "object-2\nobject-3\n"
346
+
347
+ stub_request(:get, "https://example.com/v1/AUTH_account/container-1?limit=2&marker=object-2").with(:headers => { "X-Auth-Token" => "Token" }).to_return(:status => 200, :body => response, :headers => { "Content-Type" => "text/html" })
348
+
349
+ assert_equal response, @swift_client.get_objects("container-1", { :limit => 2, :marker => "object-2" }, :json => false).body
350
+ end
351
+
307
352
  def test_paginate_objects
308
353
  objects = [
309
354
  { "hash" => "Hash", "last_modified" => "Last modified", "bytes" => 1, "name" => "object-1", "content_type" => "Content type" },
@@ -340,4 +385,3 @@ class SwiftClientTest < MiniTest::Test
340
385
  assert @swift_client.temp_url("object", "container", :expires_in => 86400) =~ %r{https://example.com/v1/AUTH_account/container/object\?temp_url_sig=[a-f0-9]{40}&temp_url_expires=1086400}
341
386
  end
342
387
  end
343
-
@@ -4,4 +4,4 @@ require "minitest"
4
4
  require "minitest/autorun"
5
5
  require "webmock/minitest"
6
6
  require "minitest/unit"
7
- require "mocha/mini_test"
7
+ require "mocha/minitest"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swift_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Vetter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-05 00:00:00.000000000 Z
11
+ date: 2020-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -28,16 +28,16 @@ dependencies:
28
28
  name: mime-types
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "<"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '3.0'
33
+ version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "<"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '3.0'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -146,8 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
146
  - !ruby/object:Gem::Version
147
147
  version: '0'
148
148
  requirements: []
149
- rubyforge_project:
150
- rubygems_version: 2.2.2
149
+ rubygems_version: 3.0.3
151
150
  signing_key:
152
151
  specification_version: 4
153
152
  summary: Small but powerful client to interact with OpenStack Swift