hugs 2.1.0 → 2.2.0
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/Gemfile +1 -1
- data/Gemfile.lock +2 -2
- data/README.md +9 -2
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/hugs.gemspec +10 -6
- data/lib/hugs.rb +1 -133
- data/lib/hugs/client.rb +152 -0
- data/lib/hugs/errors.rb +80 -0
- data/test/lib/hugs/client_test.rb +241 -0
- data/test/lib/hugs/errors_test.rb +74 -0
- data/test/{support.rb → test_helper.rb} +1 -0
- metadata +11 -7
- data/test/test_hugs.rb +0 -228
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -4,11 +4,11 @@ GEM
|
|
4
4
|
addressable (2.2.2)
|
5
5
|
crack (0.1.8)
|
6
6
|
git (1.2.5)
|
7
|
-
jeweler (1.5.
|
7
|
+
jeweler (1.5.2)
|
8
8
|
bundler (~> 1.0.0)
|
9
9
|
git (>= 1.2.5)
|
10
10
|
rake
|
11
|
-
minitest (2.0.
|
11
|
+
minitest (2.0.2)
|
12
12
|
multipart-post (1.0.1)
|
13
13
|
net-http-persistent (1.4.1)
|
14
14
|
nokogiri (1.4.4)
|
data/README.md
CHANGED
@@ -10,7 +10,8 @@ Opted to write this gem for four reasons:
|
|
10
10
|
* Wanted a [fast](http://blog.segment7.net/articles/2010/05/07/net-http-is-not-slow),
|
11
11
|
thread-safe, and persistent client.
|
12
12
|
* [Excon](https://github.com/geemus/excon) does most everything right, but is not
|
13
|
-
compatible with [VCR](https://github.com/myronmarston/vcr)
|
13
|
+
compatible with [VCR](https://github.com/myronmarston/vcr) (more specifically
|
14
|
+
[webmock](https://github.com/bblimke/webmock) and [fakeweb](https://github.com/chrisk/fakeweb)).
|
14
15
|
* Wanted to learn how to handle this pattern.
|
15
16
|
|
16
17
|
The XML usage of this gem will probably change. In the next couple of weeks Hugs
|
@@ -19,7 +20,7 @@ will be implemented against an XML OCCI API.
|
|
19
20
|
## Assumptions
|
20
21
|
|
21
22
|
* The webservice returns JSON or XML.
|
22
|
-
*
|
23
|
+
* To objectify the returned JSON or hand back a Nokogiri::XML::Document.
|
23
24
|
|
24
25
|
## Usage
|
25
26
|
|
@@ -33,4 +34,10 @@ See the 'Examples' section in the [wiki](http://github.com/retr0h/hugs/wiki/).
|
|
33
34
|
|
34
35
|
## Testing
|
35
36
|
|
37
|
+
Tests can run offline thanks to [webmock](https://github.com/bblimke/webmock).
|
38
|
+
|
36
39
|
$ bundle exec rake
|
40
|
+
|
41
|
+
or
|
42
|
+
|
43
|
+
$ rake
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.2.0
|
data/hugs.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{hugs}
|
8
|
-
s.version = "2.
|
8
|
+
s.version = "2.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["retr0h"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-01-04}
|
13
13
|
s.email = %q{john@dewey.ws}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"LICENSE",
|
@@ -25,16 +25,20 @@ Gem::Specification.new do |s|
|
|
25
25
|
"VERSION",
|
26
26
|
"hugs.gemspec",
|
27
27
|
"lib/hugs.rb",
|
28
|
-
"
|
29
|
-
"
|
28
|
+
"lib/hugs/client.rb",
|
29
|
+
"lib/hugs/errors.rb",
|
30
|
+
"test/lib/hugs/client_test.rb",
|
31
|
+
"test/lib/hugs/errors_test.rb",
|
32
|
+
"test/test_helper.rb"
|
30
33
|
]
|
31
34
|
s.homepage = %q{http://github.com/retr0h/hugs}
|
32
35
|
s.require_paths = ["lib"]
|
33
36
|
s.rubygems_version = %q{1.3.7}
|
34
37
|
s.summary = %q{Hugs net-http-persistent with convenient get, delete, post, and put methods.}
|
35
38
|
s.test_files = [
|
36
|
-
"test/
|
37
|
-
"test/
|
39
|
+
"test/lib/hugs/client_test.rb",
|
40
|
+
"test/lib/hugs/errors_test.rb",
|
41
|
+
"test/test_helper.rb"
|
38
42
|
]
|
39
43
|
|
40
44
|
if s.respond_to? :specification_version then
|
data/lib/hugs.rb
CHANGED
@@ -1,133 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
class Hugs
|
4
|
-
Headers = {
|
5
|
-
:json => "application/json",
|
6
|
-
:xml => "application/xml",
|
7
|
-
}.freeze
|
8
|
-
|
9
|
-
Classes = [
|
10
|
-
Net::HTTP::Get,
|
11
|
-
Net::HTTP::Delete,
|
12
|
-
Net::HTTP::Post,
|
13
|
-
Net::HTTP::Put,
|
14
|
-
].freeze
|
15
|
-
|
16
|
-
##
|
17
|
-
# Required options:
|
18
|
-
# +host+: A String with the host to connect.
|
19
|
-
# Optional:
|
20
|
-
# +user+: A String containing the username for use in HTTP Basic auth.
|
21
|
-
# +password+: A String containing the password for use in HTTP Basic auth.
|
22
|
-
# +port+: An Integer containing the port to connect.
|
23
|
-
# +scheme+: A String containing the HTTP scheme.
|
24
|
-
|
25
|
-
def initialize options
|
26
|
-
@user = options[:user]
|
27
|
-
@password = options[:password]
|
28
|
-
@host = options[:host]
|
29
|
-
@port = options[:port] || 80
|
30
|
-
@scheme = options[:scheme] || "https"
|
31
|
-
@type = options[:type] || :json
|
32
|
-
end
|
33
|
-
|
34
|
-
##
|
35
|
-
# Perform an HTTP get, delete, post, or put.
|
36
|
-
# +path+: A String with the path to the HTTP resource.
|
37
|
-
# +params+: A Hash with the following keys:
|
38
|
-
# - +:query+: Query String in the format "foo=bar"
|
39
|
-
# - +:body+: A sub Hash to be JSON encoded, and posted in
|
40
|
-
# the message body.
|
41
|
-
|
42
|
-
Classes.each do |clazz|
|
43
|
-
verb = clazz.to_s.split("::")[-1].tr 'A-Z', 'a-z'
|
44
|
-
|
45
|
-
define_method verb do |*args|
|
46
|
-
path = args[0]
|
47
|
-
params = args[1] || {}
|
48
|
-
|
49
|
-
response = response_for(clazz, path, params)
|
50
|
-
response.body = parse response.body
|
51
|
-
response
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
##
|
56
|
-
# :method: get
|
57
|
-
|
58
|
-
##
|
59
|
-
# :method: delete
|
60
|
-
|
61
|
-
##
|
62
|
-
# :method: post
|
63
|
-
|
64
|
-
##
|
65
|
-
# :method: put
|
66
|
-
|
67
|
-
private
|
68
|
-
##
|
69
|
-
# Worker method to be called by #get, #delete, #post, #put.
|
70
|
-
# Method arguments have been documented in the callers.
|
71
|
-
|
72
|
-
def response_for request, path, params
|
73
|
-
query = params[:query] && params.delete(:query)
|
74
|
-
body = params[:body] && params.delete(:body)
|
75
|
-
upload = params[:upload] && params.delete(:upload)
|
76
|
-
|
77
|
-
@http ||= Net::HTTP::Persistent.new
|
78
|
-
@url ||= URI.parse "#{@scheme}://#{@host}:#{@port}"
|
79
|
-
|
80
|
-
if upload && request.class === Net::HTTP::Post
|
81
|
-
parts = upload[:parts] || {}
|
82
|
-
parts[:file] = UploadIO.new(parts[:file], upload[:content_type]) if parts[:file]
|
83
|
-
|
84
|
-
request = Net::HTTP::Post::Multipart.new path_with_query(path, query), parts
|
85
|
-
else
|
86
|
-
request = request.new path_with_query path, query
|
87
|
-
request.body = encode(body) if body
|
88
|
-
|
89
|
-
common_headers request
|
90
|
-
end
|
91
|
-
|
92
|
-
request.basic_auth(@user, @password) if requires_authentication?
|
93
|
-
@http.request(@url, request)
|
94
|
-
end
|
95
|
-
|
96
|
-
def path_with_query path, query
|
97
|
-
[path, query].compact.join "?"
|
98
|
-
end
|
99
|
-
|
100
|
-
def common_headers request
|
101
|
-
case request
|
102
|
-
when Net::HTTP::Get, Net::HTTP::Delete
|
103
|
-
request.add_field "Accept", Headers[@type]
|
104
|
-
when Net::HTTP::Post, Net::HTTP::Put
|
105
|
-
request.add_field "Accept", Headers[@type]
|
106
|
-
request.add_field "Content-Type", Headers[@type]
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def parse data
|
111
|
-
if is_json?
|
112
|
-
Yajl::Parser.parse data
|
113
|
-
elsif is_xml?
|
114
|
-
Nokogiri::XML.parse data
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def encode body
|
119
|
-
is_json? ? (Yajl::Encoder.encode body) : body
|
120
|
-
end
|
121
|
-
|
122
|
-
def requires_authentication?
|
123
|
-
@user && @password
|
124
|
-
end
|
125
|
-
|
126
|
-
def is_xml?
|
127
|
-
@type == :xml
|
128
|
-
end
|
129
|
-
|
130
|
-
def is_json?
|
131
|
-
@type == :json
|
132
|
-
end
|
133
|
-
end
|
1
|
+
require "hugs/client"
|
data/lib/hugs/client.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
%w(hugs/errors net/http/persistent net/http/post/multipart yajl nokogiri).each { |r| require r }
|
2
|
+
|
3
|
+
module Hugs
|
4
|
+
class Client
|
5
|
+
attr_accessor :headers
|
6
|
+
attr_accessor :raise_4xx, :raise_5xx
|
7
|
+
attr_writer :raise_4xx, :raise_5xx
|
8
|
+
|
9
|
+
Headers = {
|
10
|
+
:json => "application/json",
|
11
|
+
:xml => "application/xml",
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
Classes = [
|
15
|
+
Net::HTTP::Get,
|
16
|
+
Net::HTTP::Delete,
|
17
|
+
Net::HTTP::Post,
|
18
|
+
Net::HTTP::Put,
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
##
|
22
|
+
# Required options:
|
23
|
+
# +host+: A String with the host to connect.
|
24
|
+
# Optional:
|
25
|
+
# +user+: A String containing the username for use in HTTP Basic auth.
|
26
|
+
# +password+: A String containing the password for use in HTTP Basic auth.
|
27
|
+
# +port+: An Integer containing the port to connect.
|
28
|
+
# +scheme+: A String containing the HTTP scheme.
|
29
|
+
|
30
|
+
def initialize options
|
31
|
+
@user = options[:user]
|
32
|
+
@password = options[:password]
|
33
|
+
@host = options[:host]
|
34
|
+
@port = options[:port] || 80
|
35
|
+
@scheme = options[:scheme] || "http"
|
36
|
+
@type = options[:type] || :json
|
37
|
+
@headers = options[:headers] || {}
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Perform an HTTP get, delete, post, or put.
|
42
|
+
# +path+: A String with the path to the HTTP resource.
|
43
|
+
# +params+: A Hash with the following keys:
|
44
|
+
# - +:query+: Query String in the format "foo=bar"
|
45
|
+
# - +:body+: A sub Hash to be JSON encoded, and posted in
|
46
|
+
# the message body.
|
47
|
+
|
48
|
+
Classes.each do |clazz|
|
49
|
+
verb = clazz.to_s.split("::").last.downcase
|
50
|
+
|
51
|
+
define_method verb do |*args|
|
52
|
+
path = args[0]
|
53
|
+
params = args[1] || {}
|
54
|
+
|
55
|
+
response_for clazz, path, params
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# :method: get
|
61
|
+
|
62
|
+
##
|
63
|
+
# :method: delete
|
64
|
+
|
65
|
+
##
|
66
|
+
# :method: post
|
67
|
+
|
68
|
+
##
|
69
|
+
# :method: put
|
70
|
+
|
71
|
+
private
|
72
|
+
##
|
73
|
+
# Worker method to be called by #get, #delete, #post, #put.
|
74
|
+
# Method arguments have been documented in the callers.
|
75
|
+
|
76
|
+
def response_for request, path, params
|
77
|
+
query = params[:query] && params.delete(:query)
|
78
|
+
body = params[:body] && params.delete(:body)
|
79
|
+
upload = params[:upload] && params.delete(:upload)
|
80
|
+
|
81
|
+
@http ||= Net::HTTP::Persistent.new
|
82
|
+
@url ||= URI.parse "#{@scheme}://#{@host}:#{@port}"
|
83
|
+
|
84
|
+
full_path = path_with_query path, query
|
85
|
+
|
86
|
+
if upload && request.class === Net::HTTP::Post
|
87
|
+
parts = upload[:parts] || {}
|
88
|
+
parts[:file] = UploadIO.new(parts[:file], upload[:content_type]) if parts[:file]
|
89
|
+
|
90
|
+
request = Net::HTTP::Post::Multipart.new full_path, parts
|
91
|
+
else
|
92
|
+
request = request.new full_path
|
93
|
+
request.body = encode body
|
94
|
+
|
95
|
+
add_headers request
|
96
|
+
end
|
97
|
+
|
98
|
+
request.basic_auth(@user, @password) if requires_authentication?
|
99
|
+
handle_response request
|
100
|
+
end
|
101
|
+
|
102
|
+
def handle_response request
|
103
|
+
resp = @http.request @url, request
|
104
|
+
Hugs::Errors::status_error resp, @raise_4xx, @raise_5xx
|
105
|
+
resp.body = parse resp.body
|
106
|
+
|
107
|
+
resp
|
108
|
+
end
|
109
|
+
|
110
|
+
def path_with_query path, query
|
111
|
+
[path, query].compact.join "?"
|
112
|
+
end
|
113
|
+
|
114
|
+
def add_headers request
|
115
|
+
request.add_field "Accept", Headers[@type]
|
116
|
+
if [Net::HTTP::Post, Net::HTTP::Put].include? request.class
|
117
|
+
request.add_field "Content-Type", Headers[@type]
|
118
|
+
end
|
119
|
+
|
120
|
+
@headers.each do |header, value|
|
121
|
+
request.add_field header, value
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def parse data
|
126
|
+
if is_json?
|
127
|
+
Yajl::Parser.parse data
|
128
|
+
elsif is_xml?
|
129
|
+
Nokogiri::XML.parse data
|
130
|
+
else
|
131
|
+
data
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def encode body
|
136
|
+
return unless body
|
137
|
+
is_json? ? (Yajl::Encoder.encode body) : body
|
138
|
+
end
|
139
|
+
|
140
|
+
def requires_authentication?
|
141
|
+
@user && @password
|
142
|
+
end
|
143
|
+
|
144
|
+
def is_xml?
|
145
|
+
@type == :xml
|
146
|
+
end
|
147
|
+
|
148
|
+
def is_json?
|
149
|
+
@type == :json
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
data/lib/hugs/errors.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
module Hugs
|
2
|
+
module Errors
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
class HTTPStatusError < Error
|
6
|
+
def initialize msg
|
7
|
+
super msg
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# Taken from geemus:excon/lib/excon/errors.rb
|
13
|
+
|
14
|
+
class BadRequest < HTTPStatusError; end # 400
|
15
|
+
class Unauthorized < HTTPStatusError; end # 401
|
16
|
+
class PaymentRequired < HTTPStatusError; end # 402
|
17
|
+
class Forbidden < HTTPStatusError; end # 403
|
18
|
+
class NotFound < HTTPStatusError; end # 404
|
19
|
+
class MethodNotAllowed < HTTPStatusError; end # 405
|
20
|
+
class NotAcceptable < HTTPStatusError; end # 406
|
21
|
+
class ProxyAuthenticationRequired < HTTPStatusError; end # 407
|
22
|
+
class RequestTimeout < HTTPStatusError; end # 408
|
23
|
+
class Conflict < HTTPStatusError; end # 409
|
24
|
+
class Gone < HTTPStatusError; end # 410
|
25
|
+
class LengthRequired < HTTPStatusError; end # 411
|
26
|
+
class PreconditionFailed < HTTPStatusError; end # 412
|
27
|
+
class RequestEntityTooLarge < HTTPStatusError; end # 413
|
28
|
+
class RequestURITooLong < HTTPStatusError; end # 414
|
29
|
+
class UnsupportedMediaType < HTTPStatusError; end # 415
|
30
|
+
class RequestedRangeNotSatisfiable < HTTPStatusError; end # 416
|
31
|
+
class ExpectationFailed < HTTPStatusError; end # 417
|
32
|
+
class UnprocessableEntity < HTTPStatusError; end # 422
|
33
|
+
class InternalServerError < HTTPStatusError; end # 500
|
34
|
+
class NotImplemented < HTTPStatusError; end # 501
|
35
|
+
class BadGateway < HTTPStatusError; end # 502
|
36
|
+
class ServiceUnavailable < HTTPStatusError; end # 503
|
37
|
+
class GatewayTimeout < HTTPStatusError; end # 504
|
38
|
+
|
39
|
+
|
40
|
+
def self.status_error response, raise_4xx, raise_5xx
|
41
|
+
@errors ||= {
|
42
|
+
400 => [Hugs::Errors::BadRequest, 'Bad Request'],
|
43
|
+
401 => [Hugs::Errors::Unauthorized, 'Unauthorized'],
|
44
|
+
402 => [Hugs::Errors::PaymentRequired, 'Payment Required'],
|
45
|
+
403 => [Hugs::Errors::Forbidden, 'Forbidden'],
|
46
|
+
404 => [Hugs::Errors::NotFound, 'Not Found'],
|
47
|
+
405 => [Hugs::Errors::MethodNotAllowed, 'Method Not Allowed'],
|
48
|
+
406 => [Hugs::Errors::NotAcceptable, 'Not Acceptable'],
|
49
|
+
407 => [Hugs::Errors::ProxyAuthenticationRequired, 'Proxy Authentication Required'],
|
50
|
+
408 => [Hugs::Errors::RequestTimeout, 'Request Timeout'],
|
51
|
+
409 => [Hugs::Errors::Conflict, 'Conflict'],
|
52
|
+
410 => [Hugs::Errors::Gone, 'Gone'],
|
53
|
+
411 => [Hugs::Errors::LengthRequired, 'Length Required'],
|
54
|
+
412 => [Hugs::Errors::PreconditionFailed, 'Precondition Failed'],
|
55
|
+
413 => [Hugs::Errors::RequestEntityTooLarge, 'Request Entity Too Large'],
|
56
|
+
414 => [Hugs::Errors::RequestURITooLong, 'Request-URI Too Long'],
|
57
|
+
415 => [Hugs::Errors::UnsupportedMediaType, 'Unsupported Media Type'],
|
58
|
+
416 => [Hugs::Errors::RequestedRangeNotSatisfiable, 'Request Range Not Satisfiable'],
|
59
|
+
417 => [Hugs::Errors::ExpectationFailed, 'Expectation Failed'],
|
60
|
+
422 => [Hugs::Errors::UnprocessableEntity, 'Unprocessable Entity'],
|
61
|
+
500 => [Hugs::Errors::InternalServerError, 'InternalServerError'],
|
62
|
+
501 => [Hugs::Errors::NotImplemented, 'Not Implemented'],
|
63
|
+
502 => [Hugs::Errors::BadGateway, 'Bad Gateway'],
|
64
|
+
503 => [Hugs::Errors::ServiceUnavailable, 'Service Unavailable'],
|
65
|
+
504 => [Hugs::Errors::GatewayTimeout, 'Gateway Timeout']
|
66
|
+
}
|
67
|
+
|
68
|
+
case response.code
|
69
|
+
when raise_4xx && %r{^4[0-9]{2}$} ; raise_for(response.code)
|
70
|
+
when raise_5xx && %r{^5[0-9]{2}$} ; raise_for(response.code)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def self.raise_for code
|
76
|
+
error, message = @errors[code.to_i]
|
77
|
+
raise error.new message
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
%w(test_helper base64).each { |r| require r }
|
2
|
+
|
3
|
+
describe Hugs::Client do
|
4
|
+
before do
|
5
|
+
@scheme = "https"
|
6
|
+
@host = "example.com"
|
7
|
+
@port = 80
|
8
|
+
@base = "#{@host}:#{@port}"
|
9
|
+
@valid_options = {
|
10
|
+
:host => @host,
|
11
|
+
:port => @port,
|
12
|
+
:scheme => @scheme,
|
13
|
+
}
|
14
|
+
|
15
|
+
WebMock.reset!
|
16
|
+
@instance = Hugs::Client.new @valid_options
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#response_for" do
|
20
|
+
before do
|
21
|
+
@request = Net::HTTP::Get
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "path" do
|
25
|
+
it "is valid" do
|
26
|
+
stub_request :get, "#{@scheme}://#{@base}/"
|
27
|
+
|
28
|
+
@instance.send :response_for, @request, "/", {}
|
29
|
+
|
30
|
+
assert_requested :get, "#{@scheme}://#{@base}/"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "is valid when an invalid :query is supplied" do
|
34
|
+
stub_request :get, "#{@scheme}://#{@base}/"
|
35
|
+
|
36
|
+
@instance.send :response_for, @request, "/", :query => nil
|
37
|
+
|
38
|
+
assert_requested :get, "#{@scheme}://#{@base}/"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "also has a query string" do
|
42
|
+
stub_request(:get, "#{@scheme}://#{@base}/").with:query => {"foo" => "bar"}
|
43
|
+
|
44
|
+
@instance.send :response_for, @request, "/", :query => "foo=bar"
|
45
|
+
|
46
|
+
assert_requested :get, "#{@scheme}://#{@base}/", :query => {"foo" => "bar"}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "multi-part" do
|
51
|
+
Content_Type_Matcher = %r{multipart/form-data}
|
52
|
+
|
53
|
+
before do
|
54
|
+
@request = Net::HTTP::Post
|
55
|
+
end
|
56
|
+
|
57
|
+
it "uploads a file" do
|
58
|
+
stub_request :post, "#{@scheme}://#{@base}/"
|
59
|
+
upload = {
|
60
|
+
:upload => {
|
61
|
+
:parts => { :file => "/dev/null" },
|
62
|
+
:content_type => "type/subtype"
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
@instance.send :response_for, @request, "/", upload
|
67
|
+
|
68
|
+
assert_requested :post, "#{@scheme}://#{@base}/", :body => %r{Content-Type: type/subtype}, :headers => {
|
69
|
+
"Content-Type" => Content_Type_Matcher
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
it "has parts" do
|
74
|
+
stub_request :post, "#{@scheme}://#{@base}/"
|
75
|
+
upload = {
|
76
|
+
:upload => {
|
77
|
+
:parts => { :foo => :bar, :baz => :xyzzy },
|
78
|
+
:content_type => "foo/bar"
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
@instance.send :response_for, @request, "/", upload
|
83
|
+
|
84
|
+
### wtf can't use mx together.
|
85
|
+
content_disposition_matcher = %r{^Content-Disposition: form-data; name="foo".*^bar.*^Content-Disposition: form-data; name="baz".*^xyzzy.*}m
|
86
|
+
assert_requested :post, "#{@scheme}://#{@base}/", :body => content_disposition_matcher, :headers => {
|
87
|
+
"Content-Type" => Content_Type_Matcher
|
88
|
+
}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "body" do
|
93
|
+
describe "parses response" do
|
94
|
+
describe "json" do
|
95
|
+
it "objectifies and returns a hash" do
|
96
|
+
stub_request(:get, "#{@scheme}://#{@base}/").to_return :body => '{"foo":"bar"}'
|
97
|
+
instance = Hugs::Client.new @valid_options.merge(:type => :json)
|
98
|
+
|
99
|
+
response = instance.get "/"
|
100
|
+
|
101
|
+
response.body.must_be_kind_of Hash
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "xml" do
|
106
|
+
it "parses and returns a Nokogiri object" do
|
107
|
+
stub_request(:get, "#{@scheme}://#{@base}/").to_return :body => "<STORAGE></STORAGE>"
|
108
|
+
instance = Hugs::Client.new @valid_options.merge(:type => :xml)
|
109
|
+
|
110
|
+
response = instance.get "/"
|
111
|
+
|
112
|
+
response.body.must_be_kind_of Nokogiri::XML::Document
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "encodes request" do
|
118
|
+
before do
|
119
|
+
stub_request :get, "#{@scheme}://#{@base}/"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "is not set when :body invalid" do
|
123
|
+
@instance.send :response_for, @request, "/", :body => nil
|
124
|
+
|
125
|
+
assert_requested :get, "#{@scheme}://#{@base}/", :body => nil
|
126
|
+
end
|
127
|
+
|
128
|
+
it "is not set when :body is missing" do
|
129
|
+
@instance.send :response_for, @request, "/", {}
|
130
|
+
|
131
|
+
assert_requested :get, "#{@scheme}://#{@base}/", {}
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "json" do
|
135
|
+
it "is valid" do
|
136
|
+
instance = Hugs::Client.new @valid_options.merge(:type => :json)
|
137
|
+
|
138
|
+
instance.send :response_for, @request, "/", :body => {:foo => :bar}
|
139
|
+
|
140
|
+
assert_requested :get, "#{@scheme}://#{@base}/", :body => '{"foo":"bar"}'
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "xml" do
|
145
|
+
it "is valid" do
|
146
|
+
instance = Hugs::Client.new @valid_options.merge(:type => :xml)
|
147
|
+
|
148
|
+
instance.send :response_for, @request, "/", :body => "foo bar"
|
149
|
+
|
150
|
+
assert_requested :get, "#{@scheme}://#{@base}/", :body => "foo bar"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
describe "headers" do
|
158
|
+
describe "authentication" do
|
159
|
+
it "uses basic auth when providing user and password" do
|
160
|
+
stub_request :get, "#{@scheme}://user:credentials@#{@base}/"
|
161
|
+
instance = Hugs::Client.new @valid_options.merge(:user => "user", :password => "credentials")
|
162
|
+
|
163
|
+
instance.send :response_for, @request, "/", {}
|
164
|
+
assert_requested :get, "#{@scheme}://user:credentials@#{@base}/"
|
165
|
+
end
|
166
|
+
|
167
|
+
it "doesn't use basic auth without a user" do
|
168
|
+
assert_doesnt_use_basic_auth_without :user
|
169
|
+
end
|
170
|
+
|
171
|
+
it "doesn't use basic auth without a password" do
|
172
|
+
assert_doesnt_use_basic_auth_without :password
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "JSON" do
|
177
|
+
it "supports json GET" do
|
178
|
+
assert_supports_http_verb :json, :get
|
179
|
+
end
|
180
|
+
|
181
|
+
it "supports json DELETE" do
|
182
|
+
assert_supports_http_verb :json, :delete
|
183
|
+
end
|
184
|
+
|
185
|
+
it "supports json POST" do
|
186
|
+
assert_supports_http_verb :json, :post
|
187
|
+
end
|
188
|
+
|
189
|
+
it "supports json PUT" do
|
190
|
+
assert_supports_http_verb :json, :put
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "XML" do
|
195
|
+
it "supports xml GET" do
|
196
|
+
assert_supports_http_verb :xml, :get
|
197
|
+
end
|
198
|
+
|
199
|
+
it "supports xml DELETE" do
|
200
|
+
assert_supports_http_verb :xml, :delete
|
201
|
+
end
|
202
|
+
|
203
|
+
it "supports xml POST" do
|
204
|
+
assert_supports_http_verb :xml, :post
|
205
|
+
end
|
206
|
+
|
207
|
+
it "supports xml PUT" do
|
208
|
+
assert_supports_http_verb :xml, :put
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def assert_supports_http_verb type, verb
|
214
|
+
mimetype = {:xml => 'application/xml',
|
215
|
+
:json => 'application/json'}[type]
|
216
|
+
|
217
|
+
assert mimetype, "Unsupported Mimetype '#{type}'"
|
218
|
+
|
219
|
+
clazz = eval "Net::HTTP::#{verb.capitalize}"
|
220
|
+
|
221
|
+
stub_request verb, "#{@scheme}://#{@base}/"
|
222
|
+
instance = Hugs::Client.new @valid_options.merge(:type => type)
|
223
|
+
|
224
|
+
instance.send :response_for, clazz, "/", {}
|
225
|
+
|
226
|
+
headers = { "Accept" => ["*/*", mimetype] }
|
227
|
+
headers["Content-Type"] = mimetype if [:put, :put].include? type
|
228
|
+
|
229
|
+
assert_requested verb, "#{@scheme}://#{@base}/", :headers => headers
|
230
|
+
end
|
231
|
+
|
232
|
+
def assert_doesnt_use_basic_auth_without option
|
233
|
+
stub_request :get, "#{@scheme}://#{@base}/"
|
234
|
+
instance = Hugs::Client.new @valid_options.merge(option => "value")
|
235
|
+
|
236
|
+
instance.send :response_for, @request, "/", {}
|
237
|
+
|
238
|
+
assert_requested :get, "#{@scheme}://#{@base}/"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
describe Hugs::Errors do
|
4
|
+
before do
|
5
|
+
@scheme = "https"
|
6
|
+
@host = "example.com"
|
7
|
+
@port = 80
|
8
|
+
@base = "#{@host}:#{@port}"
|
9
|
+
@valid_options = {
|
10
|
+
:host => @host,
|
11
|
+
:port => @port,
|
12
|
+
:scheme => @scheme,
|
13
|
+
}
|
14
|
+
|
15
|
+
@instance = Hugs::Client.new @valid_options
|
16
|
+
WebMock.reset!
|
17
|
+
end
|
18
|
+
|
19
|
+
Error_4xx = {
|
20
|
+
400 => [Hugs::Errors::BadRequest, 'Bad Request'],
|
21
|
+
401 => [Hugs::Errors::Unauthorized, 'Unauthorized'],
|
22
|
+
402 => [Hugs::Errors::PaymentRequired, 'Payment Required'],
|
23
|
+
403 => [Hugs::Errors::Forbidden, 'Forbidden'],
|
24
|
+
404 => [Hugs::Errors::NotFound, 'Not Found'],
|
25
|
+
405 => [Hugs::Errors::MethodNotAllowed, 'Method Not Allowed'],
|
26
|
+
406 => [Hugs::Errors::NotAcceptable, 'Not Acceptable'],
|
27
|
+
407 => [Hugs::Errors::ProxyAuthenticationRequired, 'Proxy Authentication Required'],
|
28
|
+
408 => [Hugs::Errors::RequestTimeout, 'Request Timeout'],
|
29
|
+
409 => [Hugs::Errors::Conflict, 'Conflict'],
|
30
|
+
410 => [Hugs::Errors::Gone, 'Gone'],
|
31
|
+
411 => [Hugs::Errors::LengthRequired, 'Length Required'],
|
32
|
+
412 => [Hugs::Errors::PreconditionFailed, 'Precondition Failed'],
|
33
|
+
413 => [Hugs::Errors::RequestEntityTooLarge, 'Request Entity Too Large'],
|
34
|
+
414 => [Hugs::Errors::RequestURITooLong, 'Request-URI Too Long'],
|
35
|
+
415 => [Hugs::Errors::UnsupportedMediaType, 'Unsupported Media Type'],
|
36
|
+
416 => [Hugs::Errors::RequestedRangeNotSatisfiable, 'Request Range Not Satisfiable'],
|
37
|
+
417 => [Hugs::Errors::ExpectationFailed, 'Expectation Failed'],
|
38
|
+
422 => [Hugs::Errors::UnprocessableEntity, 'Unprocessable Entity'],
|
39
|
+
}
|
40
|
+
|
41
|
+
Error_5xx = {
|
42
|
+
500 => [Hugs::Errors::InternalServerError, 'InternalServerError'],
|
43
|
+
501 => [Hugs::Errors::NotImplemented, 'Not Implemented'],
|
44
|
+
502 => [Hugs::Errors::BadGateway, 'Bad Gateway'],
|
45
|
+
503 => [Hugs::Errors::ServiceUnavailable, 'Service Unavailable'],
|
46
|
+
504 => [Hugs::Errors::GatewayTimeout, 'Gateway Timeout']
|
47
|
+
}
|
48
|
+
|
49
|
+
describe "error codes" do
|
50
|
+
before do
|
51
|
+
@instance.raise_4xx = false
|
52
|
+
@instance.raise_5xx = false
|
53
|
+
end
|
54
|
+
|
55
|
+
(Error_4xx.merge(Error_5xx)).each_pair do |code, errors|
|
56
|
+
error, message = errors
|
57
|
+
|
58
|
+
it "raises" do
|
59
|
+
(code.to_s =~ %r{^4[0-9]{2}$}) ? (@instance.raise_4xx = true) : (@instance.raise_5xx = true)
|
60
|
+
stub_request(:get, "#{@scheme}://#{@base}/").to_return :status => [code, message]
|
61
|
+
|
62
|
+
lambda { @instance.send :response_for, Net::HTTP::Get, "/", {} }.must_raise error
|
63
|
+
end
|
64
|
+
|
65
|
+
it "doesn't raise" do
|
66
|
+
stub_request(:get, "#{@scheme}://#{@base}/").to_return :status => [code, message]
|
67
|
+
|
68
|
+
response = @instance.send :response_for, Net::HTTP::Get, "/", {}
|
69
|
+
|
70
|
+
response.code.must_equal code.to_s
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 2
|
7
|
-
-
|
7
|
+
- 2
|
8
8
|
- 0
|
9
|
-
version: 2.
|
9
|
+
version: 2.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- retr0h
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date:
|
17
|
+
date: 2011-01-04 00:00:00 -08:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -154,8 +154,11 @@ files:
|
|
154
154
|
- VERSION
|
155
155
|
- hugs.gemspec
|
156
156
|
- lib/hugs.rb
|
157
|
-
-
|
158
|
-
-
|
157
|
+
- lib/hugs/client.rb
|
158
|
+
- lib/hugs/errors.rb
|
159
|
+
- test/lib/hugs/client_test.rb
|
160
|
+
- test/lib/hugs/errors_test.rb
|
161
|
+
- test/test_helper.rb
|
159
162
|
has_rdoc: true
|
160
163
|
homepage: http://github.com/retr0h/hugs
|
161
164
|
licenses: []
|
@@ -189,5 +192,6 @@ signing_key:
|
|
189
192
|
specification_version: 3
|
190
193
|
summary: Hugs net-http-persistent with convenient get, delete, post, and put methods.
|
191
194
|
test_files:
|
192
|
-
- test/
|
193
|
-
- test/
|
195
|
+
- test/lib/hugs/client_test.rb
|
196
|
+
- test/lib/hugs/errors_test.rb
|
197
|
+
- test/test_helper.rb
|
data/test/test_hugs.rb
DELETED
@@ -1,228 +0,0 @@
|
|
1
|
-
%w(support base64 hugs).each { |r| require r }
|
2
|
-
|
3
|
-
describe Hugs do
|
4
|
-
before do
|
5
|
-
@scheme = "https"
|
6
|
-
@host = "example.com"
|
7
|
-
@port = 80
|
8
|
-
@base = "#{@host}:#{@port}"
|
9
|
-
@valid_options = {
|
10
|
-
:host => @host,
|
11
|
-
:port => @port,
|
12
|
-
:scheme => @scheme,
|
13
|
-
}
|
14
|
-
|
15
|
-
WebMock.reset!
|
16
|
-
@instance = Hugs.new @valid_options
|
17
|
-
end
|
18
|
-
|
19
|
-
describe "#response_for" do
|
20
|
-
before do
|
21
|
-
@request = Net::HTTP::Get
|
22
|
-
end
|
23
|
-
|
24
|
-
describe "path" do
|
25
|
-
it "is valid" do
|
26
|
-
stub_request :get, "#{@scheme}://#{@base}/"
|
27
|
-
|
28
|
-
@instance.send :response_for, @request, "/", {}
|
29
|
-
|
30
|
-
assert_requested :get, "#{@scheme}://#{@base}/"
|
31
|
-
end
|
32
|
-
|
33
|
-
it "is valid when an invalid :query is supplied" do
|
34
|
-
stub_request :get, "#{@scheme}://#{@base}/"
|
35
|
-
|
36
|
-
@instance.send :response_for, @request, "/", :query => nil
|
37
|
-
|
38
|
-
assert_requested :get, "#{@scheme}://#{@base}/"
|
39
|
-
end
|
40
|
-
|
41
|
-
it "also has a query string" do
|
42
|
-
stub_request(:get, "#{@scheme}://#{@base}/").with:query => {"foo" => "bar"}
|
43
|
-
|
44
|
-
@instance.send :response_for, @request, "/", :query => "foo=bar"
|
45
|
-
|
46
|
-
assert_requested :get, "#{@scheme}://#{@base}/", :query => {"foo" => "bar"}
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
describe "multi-part" do
|
51
|
-
Content_Type_Matcher = %r{multipart/form-data}
|
52
|
-
|
53
|
-
before do
|
54
|
-
@request = Net::HTTP::Post
|
55
|
-
end
|
56
|
-
|
57
|
-
it "uploads a file" do
|
58
|
-
stub_request :post, "#{@scheme}://#{@base}/"
|
59
|
-
|
60
|
-
upload = {
|
61
|
-
:upload => {
|
62
|
-
:parts => { :file => "/dev/null" },
|
63
|
-
:content_type => "type/subtype"
|
64
|
-
}
|
65
|
-
}
|
66
|
-
|
67
|
-
@instance.send :response_for, @request, "/", upload
|
68
|
-
|
69
|
-
assert_requested :post, "#{@scheme}://#{@base}/", :body => %r{Content-Type: type/subtype}, :headers => {
|
70
|
-
"Content-Type" => Content_Type_Matcher
|
71
|
-
}
|
72
|
-
end
|
73
|
-
|
74
|
-
it "has parts" do
|
75
|
-
stub_request :post, "#{@scheme}://#{@base}/"
|
76
|
-
|
77
|
-
upload = {
|
78
|
-
:upload => {
|
79
|
-
:parts => { :foo => :bar, :baz => :xyzzy },
|
80
|
-
:content_type => "foo/bar"
|
81
|
-
}
|
82
|
-
}
|
83
|
-
|
84
|
-
@instance.send :response_for, @request, "/", upload
|
85
|
-
|
86
|
-
### wtf can't use mx together.
|
87
|
-
content_disposition_matcher = %r{^Content-Disposition: form-data; name="foo".*^bar.*^Content-Disposition: form-data; name="baz".*^xyzzy.*}m
|
88
|
-
assert_requested :post, "#{@scheme}://#{@base}/", :body => content_disposition_matcher, :headers => {
|
89
|
-
"Content-Type" => Content_Type_Matcher
|
90
|
-
}
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
describe "body" do
|
95
|
-
before do
|
96
|
-
stub_request :get, "#{@scheme}://#{@base}/"
|
97
|
-
end
|
98
|
-
|
99
|
-
it "is not set when :body invalid" do
|
100
|
-
@instance.send :response_for, @request, "/", :body => nil
|
101
|
-
|
102
|
-
assert_requested :get, "#{@scheme}://#{@base}/", :body => nil
|
103
|
-
end
|
104
|
-
|
105
|
-
it "is not set when :body is missing" do
|
106
|
-
@instance.send :response_for, @request, "/", {}
|
107
|
-
|
108
|
-
assert_requested :get, "#{@scheme}://#{@base}/", {}
|
109
|
-
end
|
110
|
-
|
111
|
-
describe "json" do
|
112
|
-
before do
|
113
|
-
@instance = Hugs.new @valid_options.merge(:type => :json)
|
114
|
-
end
|
115
|
-
|
116
|
-
it "is valid" do
|
117
|
-
@instance.send :response_for, @request, "/", :body => {:foo => :bar}
|
118
|
-
|
119
|
-
assert_requested :get, "#{@scheme}://#{@base}/", :body => '{"foo":"bar"}'
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
describe "xml" do
|
124
|
-
before do
|
125
|
-
@instance = Hugs.new @valid_options.merge(:type => :xml)
|
126
|
-
end
|
127
|
-
|
128
|
-
it "is valid" do
|
129
|
-
@instance.send :response_for, @request, "/", :body => "foo bar"
|
130
|
-
|
131
|
-
assert_requested :get, "#{@scheme}://#{@base}/", :body => "foo bar"
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
describe "headers" do
|
137
|
-
it "authenticates" do
|
138
|
-
stub_request :get, "#{@scheme}://user:credentials@#{@base}/"
|
139
|
-
|
140
|
-
@instance = Hugs.new @valid_options.merge(:user => "user", :password => "credentials")
|
141
|
-
|
142
|
-
@instance.send :response_for, @request, "/", {}
|
143
|
-
|
144
|
-
assert_requested :get, "#{@scheme}://user:credentials@#{@base}/"
|
145
|
-
end
|
146
|
-
|
147
|
-
[:user, :password].each do |option|
|
148
|
-
it "doesn't authenticate when '#{option}' missing" do
|
149
|
-
stub_request :get, "#{@scheme}://#{@base}/"
|
150
|
-
|
151
|
-
invalid_options = @valid_options.reject { |k,v| k == option }
|
152
|
-
@instance = Hugs.new invalid_options
|
153
|
-
|
154
|
-
@instance.send :response_for, @request, "/", {}
|
155
|
-
|
156
|
-
assert_requested :get, "#{@scheme}://#{@base}/"
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
[
|
161
|
-
{ :json => "application/json" },
|
162
|
-
{ :xml => "application/xml" },
|
163
|
-
].each do |pair|
|
164
|
-
pair.each_pair do |type, subtype|
|
165
|
-
[:post, :put].each do |verb|
|
166
|
-
clazz = eval "Net::HTTP::#{verb.capitalize}"
|
167
|
-
|
168
|
-
it "has '#{subtype}' Content-Type and Accept for '#{clazz}'" do
|
169
|
-
stub_request verb, "#{@scheme}://#{@base}/"
|
170
|
-
|
171
|
-
@instance = Hugs.new @valid_options.merge(:type => type)
|
172
|
-
|
173
|
-
@instance.send :response_for, clazz, "/", {}
|
174
|
-
|
175
|
-
assert_requested verb, "#{@scheme}://#{@base}/", :headers => {
|
176
|
-
"Accept" => ["*/*", subtype], "Content-Type" => subtype }
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
[:get, :delete].each do |verb|
|
181
|
-
clazz = eval "Net::HTTP::#{verb.capitalize}"
|
182
|
-
|
183
|
-
it "has '#{subtype}' Accept for '#{clazz}'" do
|
184
|
-
stub_request verb, "#{@scheme}://#{@base}/"
|
185
|
-
|
186
|
-
@instance = Hugs.new @valid_options.merge(:type => type)
|
187
|
-
|
188
|
-
@instance.send :response_for, clazz, "/", {}
|
189
|
-
|
190
|
-
assert_requested verb, "#{@scheme}://#{@base}/", :headers => {
|
191
|
-
"Accept" => ["*/*", subtype] }
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
describe "HTTP methods" do
|
200
|
-
describe "json" do
|
201
|
-
before do
|
202
|
-
@instance = Hugs.new @valid_options.merge(:type => :json)
|
203
|
-
end
|
204
|
-
|
205
|
-
it "objectifies and returns a hash" do
|
206
|
-
stub_request(:get, "#{@scheme}://#{@base}/").to_return :body => '{"foo":"bar"}'
|
207
|
-
|
208
|
-
response = @instance.get "/", :body => { :foo => :bar }
|
209
|
-
|
210
|
-
response.body.must_be_kind_of Hash
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
describe "xml" do
|
215
|
-
before do
|
216
|
-
@instance = Hugs.new @valid_options.merge(:type => :xml)
|
217
|
-
end
|
218
|
-
|
219
|
-
it "parses and returns a Nokogiri object" do
|
220
|
-
stub_request(:get, "#{@scheme}://#{@base}/").to_return :body => "<STORAGE></STORAGE>"
|
221
|
-
|
222
|
-
response = @instance.get "/", :body => { :foo => :bar }
|
223
|
-
|
224
|
-
response.body.must_be_kind_of Nokogiri::XML::Document
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|