james-rc-rest 2.2.1
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/History.txt +30 -0
- data/LICENSE.txt +27 -0
- data/Manifest.txt +9 -0
- data/README.txt +32 -0
- data/Rakefile +18 -0
- data/lib/rc_rest.rb +279 -0
- data/lib/rc_rest/net_http_stub.rb +65 -0
- data/lib/rc_rest/uri_stub.rb +56 -0
- data/test/test_rc_rest.rb +268 -0
- metadata +78 -0
data/History.txt
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
= 2.2.1
|
2
|
+
|
3
|
+
* Turn IOError into RCRest::CommunicationError for #post and #post_multipart.
|
4
|
+
|
5
|
+
= 2.2.0
|
6
|
+
|
7
|
+
* Moved to seattlerb rubyforge project
|
8
|
+
* Moved to p4
|
9
|
+
* Wrap communication errors in RCRest::CommunicationError
|
10
|
+
|
11
|
+
= 2.1.1
|
12
|
+
|
13
|
+
* Fix dependency on ZenTest for testing
|
14
|
+
|
15
|
+
= 2.1.0
|
16
|
+
|
17
|
+
* Don't split strings into extra params pairs on newlines
|
18
|
+
* Added RCRest#post_multipart and RCRest#make_multipart
|
19
|
+
* Fixed various query parameter incompatiblities
|
20
|
+
|
21
|
+
= 2.0.0
|
22
|
+
|
23
|
+
* Added +method+ parameter to RCRest#make_url to add path components
|
24
|
+
to a generic endpoint
|
25
|
+
* Added RCRest#post
|
26
|
+
|
27
|
+
= 1.0.0
|
28
|
+
|
29
|
+
* Birthday!
|
30
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright 2006 Eric Hodel, The Robot Co-op. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions
|
5
|
+
are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer in the
|
11
|
+
documentation and/or other materials provided with the distribution.
|
12
|
+
3. Neither the names of the authors nor the names of their contributors
|
13
|
+
may be used to endorse or promote products derived from this software
|
14
|
+
without specific prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
|
17
|
+
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
19
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
|
20
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
21
|
+
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
22
|
+
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
23
|
+
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
24
|
+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
25
|
+
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
26
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
|
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
= rc-rest
|
2
|
+
|
3
|
+
Rubyforge Project:
|
4
|
+
|
5
|
+
http://rubyforge.org/projects/rctools/
|
6
|
+
|
7
|
+
Documentation:
|
8
|
+
|
9
|
+
http://dev.robotcoop.com/Libraries/rc-rest/
|
10
|
+
|
11
|
+
== About
|
12
|
+
|
13
|
+
This is an abstract class for creating wrappers for REST web service APIs.
|
14
|
+
|
15
|
+
== Installing rc-rest
|
16
|
+
|
17
|
+
Just install the gem:
|
18
|
+
|
19
|
+
$ sudo gem install rc-rest
|
20
|
+
|
21
|
+
== Using rc-rest
|
22
|
+
|
23
|
+
rc-rest is used by gems such as yahoo-search, google-geocode and geocoder-us.
|
24
|
+
If you'd like to write bindings a web service using rc-rest see RCRest, its
|
25
|
+
tests or the above-mentioned gems for examples.
|
26
|
+
|
27
|
+
== Upgrading from 1.x
|
28
|
+
|
29
|
+
RCRest#get and RCRest#make_url now accept a method argument as the
|
30
|
+
first parameter. To use 2.x, pass the last component of the path to
|
31
|
+
RCRest#get or RCRest#make_url.
|
32
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'hoe'
|
2
|
+
require './lib/rc_rest'
|
3
|
+
|
4
|
+
Hoe.new 'rc-rest', RCRest::VERSION do |p|
|
5
|
+
p.summary = 'Robot Co-op REST web services base class'
|
6
|
+
p.description = 'This library makes it easy to implement REST-like web services APIs.'
|
7
|
+
p.author = 'Eric Hodel'
|
8
|
+
p.email = 'drbrain@segment7.net'
|
9
|
+
p.url = "http://seattlerb.rubyforge.org/rc-rest"
|
10
|
+
p.rubyforge_name = 'seattlerb'
|
11
|
+
|
12
|
+
p.changes = File.read('History.txt').scan(/\A(=.*?)^=/m).first.first
|
13
|
+
|
14
|
+
p.extra_deps << ['ZenTest', '>= 3.4.2']
|
15
|
+
end
|
16
|
+
|
17
|
+
# vim: syntax=Ruby
|
18
|
+
|
data/lib/rc_rest.rb
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'rexml/document'
|
4
|
+
|
5
|
+
##
|
6
|
+
# Abstract class for implementing REST APIs.
|
7
|
+
#
|
8
|
+
# === Example
|
9
|
+
#
|
10
|
+
# The following methods must be implemented in sublcasses:
|
11
|
+
#
|
12
|
+
# +initialize+:: Sets @url to the service enpoint.
|
13
|
+
# +check_error+:: Checks for errors in the server response.
|
14
|
+
# +parse_response+:: Extracts information from the server response.
|
15
|
+
#
|
16
|
+
# If you have extra URL paramaters (application id, output type) or need to
|
17
|
+
# perform URL customization, override +make_url+ and +make_multipart+.
|
18
|
+
#
|
19
|
+
# class FakeService < RCRest
|
20
|
+
#
|
21
|
+
# class Error < RCRest::Error; end
|
22
|
+
#
|
23
|
+
# def initialize(appid)
|
24
|
+
# @appid = appid
|
25
|
+
# @url = URI.parse 'http://example.com/api/'
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# def check_error(xml)
|
29
|
+
# raise Error, xml.elements['error'].text if xml.elements['error']
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# def make_url(method, params)
|
33
|
+
# params[:appid] = @appid
|
34
|
+
# super method, params
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# def parse_response(xml)
|
38
|
+
# return xml
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# def test(query)
|
42
|
+
# get :test, :q => query
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# end
|
46
|
+
|
47
|
+
class RCRest
|
48
|
+
|
49
|
+
##
|
50
|
+
# You are using this version of RCRest
|
51
|
+
|
52
|
+
VERSION = '2.2.1'
|
53
|
+
|
54
|
+
##
|
55
|
+
# Abstract Error class.
|
56
|
+
|
57
|
+
class Error < RuntimeError; end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Error raised when communicating with the server
|
61
|
+
|
62
|
+
class CommunicationError < Error
|
63
|
+
|
64
|
+
##
|
65
|
+
# The original exception
|
66
|
+
|
67
|
+
attr_accessor :original_exception
|
68
|
+
|
69
|
+
##
|
70
|
+
# Creates a new CommunicationError with +message+ and +original_exception+
|
71
|
+
|
72
|
+
def initialize(original_exception)
|
73
|
+
@original_exception = original_exception
|
74
|
+
message = "Communication error: #{original_exception.message}(#{original_exception.class})"
|
75
|
+
super message
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Web services initializer.
|
82
|
+
#
|
83
|
+
# Concrete web services implementations must set the +url+ instance
|
84
|
+
# variable which must be a URI.
|
85
|
+
|
86
|
+
def initialize
|
87
|
+
raise NotImplementedError, 'need to implement #intialize and set @url'
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Must extract and raise an error from +xml+, an REXML::Document, if any.
|
92
|
+
# Must return if no error could be found.
|
93
|
+
|
94
|
+
def check_error(xml)
|
95
|
+
raise NotImplementedError
|
96
|
+
end
|
97
|
+
|
98
|
+
def expand_params(params) # :nodoc:
|
99
|
+
expanded_params = []
|
100
|
+
|
101
|
+
params.each do |k,v|
|
102
|
+
if v.respond_to? :each and not String === v then
|
103
|
+
v.each { |v| expanded_params << [k, v] }
|
104
|
+
else
|
105
|
+
expanded_params << [k, v]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
expanded_params.sort_by { |k,v| [k.to_s, v.to_s] }
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Performs a GET request for method +method+ with +params+. Calls
|
114
|
+
# #parse_response on the concrete class with an REXML::Document instance and
|
115
|
+
# returns its result.
|
116
|
+
|
117
|
+
def get(method, params = {})
|
118
|
+
url = make_url method, params
|
119
|
+
|
120
|
+
url.open do |xml|
|
121
|
+
res = REXML::Document.new xml.read
|
122
|
+
|
123
|
+
check_error res
|
124
|
+
|
125
|
+
return parse_response(res)
|
126
|
+
end
|
127
|
+
rescue IOError, SystemCallError, SocketError, Timeout::Error,
|
128
|
+
REXML::ParseException => e
|
129
|
+
raise CommunicationError.new(e)
|
130
|
+
rescue OpenURI::HTTPError => e
|
131
|
+
begin
|
132
|
+
xml = REXML::Document.new e.io.read
|
133
|
+
check_error xml
|
134
|
+
rescue REXML::ParseException => e
|
135
|
+
end
|
136
|
+
new_e = CommunicationError.new e
|
137
|
+
new_e.message << "\n\nunhandled error:\n#{xml.to_s}"
|
138
|
+
raise new_e
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# Creates a URI for method +method+ and a Hash of parameters +params+.
|
143
|
+
# Override this then call super if you need to add extra params like an
|
144
|
+
# application id, output type, etc.
|
145
|
+
#
|
146
|
+
# If the value of a parameter responds to #each, make_url creates a
|
147
|
+
# key-value pair per value in the param.
|
148
|
+
#
|
149
|
+
# Examples:
|
150
|
+
#
|
151
|
+
# If the URL base is:
|
152
|
+
#
|
153
|
+
# http://example.com/api/
|
154
|
+
#
|
155
|
+
# then:
|
156
|
+
#
|
157
|
+
# make_url nil, :a => '1 2', :b => [4, 3]
|
158
|
+
#
|
159
|
+
# creates the URL:
|
160
|
+
#
|
161
|
+
# http://example.com/api/?a=1%202&b=3&b=4
|
162
|
+
#
|
163
|
+
# and
|
164
|
+
#
|
165
|
+
# make_url :method, :a => '1'
|
166
|
+
#
|
167
|
+
# creates the URL:
|
168
|
+
#
|
169
|
+
# http://example.com/api/method?a=1
|
170
|
+
|
171
|
+
def make_url(method, params = nil)
|
172
|
+
escaped_params = expand_params(params).map do |k,v|
|
173
|
+
k = URI.escape(k.to_s).gsub(';', '%3B').gsub('+', '%2B').gsub('&', '%26')
|
174
|
+
v = URI.escape(v.to_s).gsub(';', '%3B').gsub('+', '%2B').gsub('&', '%26')
|
175
|
+
"#{k}=#{v}"
|
176
|
+
end
|
177
|
+
|
178
|
+
query = escaped_params.join '&'
|
179
|
+
|
180
|
+
url = @url + "./#{method}"
|
181
|
+
url.query = query
|
182
|
+
return url
|
183
|
+
end
|
184
|
+
|
185
|
+
##
|
186
|
+
# Creates a multipart form post for the Hash of parameters +params+.
|
187
|
+
# Override this then call super if you need to add extra params like an
|
188
|
+
# application id, output type, etc.
|
189
|
+
#
|
190
|
+
# #make_multipart handles arguments similarly to #make_url.
|
191
|
+
|
192
|
+
def make_multipart(params)
|
193
|
+
boundary = (0...8).map { rand(255).to_s 16 }.join '_'
|
194
|
+
data = expand_params(params).map do |key, value|
|
195
|
+
[ "--#{boundary}",
|
196
|
+
"Content-Disposition: form-data; name=\"#{key}\"",
|
197
|
+
nil,
|
198
|
+
value]
|
199
|
+
end
|
200
|
+
|
201
|
+
data << "--#{boundary}--"
|
202
|
+
return [boundary, data.join("\r\n")]
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# Must parse results from +xml+, an REXML::Document, into something sensible
|
207
|
+
# for the API.
|
208
|
+
|
209
|
+
def parse_response(xml)
|
210
|
+
raise NotImplementedError
|
211
|
+
end
|
212
|
+
|
213
|
+
##
|
214
|
+
# Performs a POST request for method +method+ with +params+. Calls
|
215
|
+
# #parse_response on the concrete class with an REXML::Document instance and
|
216
|
+
# returns its result.
|
217
|
+
|
218
|
+
def post(method, params = {})
|
219
|
+
url = make_url method, params
|
220
|
+
query = url.query
|
221
|
+
url.query = nil
|
222
|
+
|
223
|
+
req = Net::HTTP::Post.new url.path
|
224
|
+
req.body = query
|
225
|
+
req.content_type = 'application/x-www-form-urlencoded'
|
226
|
+
|
227
|
+
res = Net::HTTP.start url.host, url.port do |http|
|
228
|
+
http.request req
|
229
|
+
end
|
230
|
+
|
231
|
+
xml = REXML::Document.new res.body
|
232
|
+
|
233
|
+
check_error xml
|
234
|
+
|
235
|
+
parse_response xml
|
236
|
+
rescue SystemCallError, SocketError, Timeout::Error, IOError,
|
237
|
+
REXML::ParseException => e
|
238
|
+
raise CommunicationError.new(e)
|
239
|
+
rescue Net::HTTPError => e
|
240
|
+
xml = REXML::Document.new e.res.body
|
241
|
+
check_error xml
|
242
|
+
raise CommunicationError.new(e)
|
243
|
+
end
|
244
|
+
|
245
|
+
##
|
246
|
+
# Performs a POST request for method +method+ with +params+, submitting a
|
247
|
+
# multipart form. Calls #parse_response on the concrete class with an
|
248
|
+
# REXML::Document instance and returns its result.
|
249
|
+
|
250
|
+
def post_multipart(method, params = {})
|
251
|
+
url = make_url method, {}
|
252
|
+
url.query = nil
|
253
|
+
|
254
|
+
boundary, data = make_multipart params
|
255
|
+
|
256
|
+
req = Net::HTTP::Post.new url.path
|
257
|
+
req.content_type = "multipart/form-data; boundary=#{boundary}"
|
258
|
+
req.body = data
|
259
|
+
|
260
|
+
res = Net::HTTP.start url.host, url.port do |http|
|
261
|
+
http.request req
|
262
|
+
end
|
263
|
+
|
264
|
+
xml = REXML::Document.new res.body
|
265
|
+
|
266
|
+
check_error xml
|
267
|
+
|
268
|
+
parse_response xml
|
269
|
+
rescue SystemCallError, SocketError, Timeout::Error, IOError,
|
270
|
+
REXML::ParseException => e
|
271
|
+
raise CommunicationError.new(e)
|
272
|
+
rescue Net::HTTPError => e
|
273
|
+
xml = REXML::Document.new e.res.body
|
274
|
+
check_error xml
|
275
|
+
raise CommunicationError.new(e)
|
276
|
+
end
|
277
|
+
|
278
|
+
end
|
279
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
class Net::HTTPResponse
|
4
|
+
|
5
|
+
##
|
6
|
+
# Setter for body content
|
7
|
+
|
8
|
+
attr_accessor :body
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
class Net::HTTP
|
13
|
+
|
14
|
+
@params = nil
|
15
|
+
@paths = nil
|
16
|
+
@responses = nil
|
17
|
+
|
18
|
+
class << self
|
19
|
+
|
20
|
+
##
|
21
|
+
# Records submitted POST params
|
22
|
+
|
23
|
+
attr_accessor :params
|
24
|
+
|
25
|
+
##
|
26
|
+
# Records POST paths
|
27
|
+
|
28
|
+
attr_accessor :paths
|
29
|
+
|
30
|
+
##
|
31
|
+
# Holds POST body responses
|
32
|
+
|
33
|
+
attr_accessor :responses
|
34
|
+
|
35
|
+
remove_method :start
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Override Net::HTTP::start to not connect
|
41
|
+
|
42
|
+
def self.start(host, port)
|
43
|
+
yield Net::HTTP.new(host)
|
44
|
+
end
|
45
|
+
|
46
|
+
remove_method :request
|
47
|
+
|
48
|
+
##
|
49
|
+
# Override Net::HTTP#request to fake its results
|
50
|
+
|
51
|
+
def request(req)
|
52
|
+
self.class.paths << req.path
|
53
|
+
self.class.params << req.body
|
54
|
+
response = self.class.responses.shift
|
55
|
+
if response.respond_to? :call then
|
56
|
+
response.call req
|
57
|
+
else
|
58
|
+
res = Net::HTTPResponse.new '1.0', 200, 'OK'
|
59
|
+
res.body = response
|
60
|
+
res
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module URI # :nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
##
|
7
|
+
# This stub overrides OpenURI's open method to allow programs that use OpenURI
|
8
|
+
# to be easily tested.
|
9
|
+
#
|
10
|
+
# == Usage
|
11
|
+
#
|
12
|
+
# require 'rc_rest/uri_stub'
|
13
|
+
#
|
14
|
+
# class TestMyClass < Test::Unit::TestCase
|
15
|
+
#
|
16
|
+
# def setup
|
17
|
+
# URI::HTTP.responses = []
|
18
|
+
# URI::HTTP.uris = []
|
19
|
+
#
|
20
|
+
# @obj = MyClass.new
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# def test_my_method
|
24
|
+
# URI::HTTP.responses << 'some text open would ordinarily return'
|
25
|
+
#
|
26
|
+
# result = @obj.my_method
|
27
|
+
#
|
28
|
+
# assert_equal :something_meaninfgul, result
|
29
|
+
#
|
30
|
+
# assert_equal true, URI::HTTP.responses.empty?
|
31
|
+
# assert_equal 1, URI::HTTP.uris.length
|
32
|
+
# assert_equal 'http://example.com/path', URI::HTTP.uris.first
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# end
|
36
|
+
|
37
|
+
class URI::HTTP # :nodoc:
|
38
|
+
|
39
|
+
class << self
|
40
|
+
attr_accessor :responses, :uris
|
41
|
+
end
|
42
|
+
|
43
|
+
alias original_open open
|
44
|
+
|
45
|
+
def open
|
46
|
+
self.class.uris << self.to_s
|
47
|
+
response = self.class.responses.shift
|
48
|
+
if response.respond_to? :call then
|
49
|
+
response.call
|
50
|
+
else
|
51
|
+
yield StringIO.new(response)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,268 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'test/zentest_assertions'
|
4
|
+
require 'rc_rest/uri_stub'
|
5
|
+
require 'rc_rest/net_http_stub'
|
6
|
+
require 'rc_rest'
|
7
|
+
|
8
|
+
class FakeService < RCRest
|
9
|
+
|
10
|
+
class Error < RCRest::Error; end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@url = URI.parse 'http://example.com/test'
|
14
|
+
end
|
15
|
+
|
16
|
+
def check_error(xml)
|
17
|
+
raise Error, xml.elements['error'].text if xml.elements['error']
|
18
|
+
end
|
19
|
+
|
20
|
+
def do_get
|
21
|
+
get :method
|
22
|
+
end
|
23
|
+
|
24
|
+
def do_post
|
25
|
+
post :method, :param => 'value'
|
26
|
+
end
|
27
|
+
|
28
|
+
def do_post_multipart
|
29
|
+
post_multipart :method, :param => 'value'
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_response(xml)
|
33
|
+
return xml
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
class TestFakeService < Test::Unit::TestCase
|
39
|
+
|
40
|
+
def setup
|
41
|
+
URI::HTTP.responses = []
|
42
|
+
URI::HTTP.uris = []
|
43
|
+
|
44
|
+
Net::HTTP.params = []
|
45
|
+
Net::HTTP.paths = []
|
46
|
+
Net::HTTP.responses = []
|
47
|
+
|
48
|
+
@fs = FakeService.new
|
49
|
+
|
50
|
+
srand 0
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_check_error
|
54
|
+
xml = REXML::Document.new '<error>you broked it</error>'
|
55
|
+
@fs.check_error xml
|
56
|
+
|
57
|
+
rescue FakeService::Error => e
|
58
|
+
assert_equal 'you broked it', e.message
|
59
|
+
|
60
|
+
else
|
61
|
+
flunk 'expected an error'
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_do_get
|
65
|
+
xml = '<result>stuff</result>'
|
66
|
+
URI::HTTP.responses << xml
|
67
|
+
|
68
|
+
result = @fs.do_get
|
69
|
+
|
70
|
+
assert_equal xml, result.to_s
|
71
|
+
assert_equal 'http://example.com/method?', URI::HTTP.uris.first
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_do_get_bad_xml
|
75
|
+
xml = '<result>stuff</result><extra/>'
|
76
|
+
URI::HTTP.responses << xml
|
77
|
+
|
78
|
+
assert_raise RCRest::CommunicationError do @fs.do_get end
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_do_get_error_400
|
82
|
+
URI::HTTP.responses << proc do
|
83
|
+
xml = '<error>you did the bad thing</error>'
|
84
|
+
raise OpenURI::HTTPError.new('400 Bad Request', StringIO.new(xml))
|
85
|
+
end
|
86
|
+
|
87
|
+
assert_raise FakeService::Error do @fs.do_get end
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_do_get_error_400_bad_xml
|
91
|
+
URI::HTTP.responses << proc do
|
92
|
+
xml = '<error>you did the bad thing</error><extra/>'
|
93
|
+
raise OpenURI::HTTPError.new('400 Bad Request', StringIO.new(xml))
|
94
|
+
end
|
95
|
+
|
96
|
+
assert_raise RCRest::CommunicationError do @fs.do_get end
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_do_get_error_unhandled
|
100
|
+
URI::HTTP.responses << proc do
|
101
|
+
xml = '<other_error>you did the bad thing</other_error>'
|
102
|
+
raise OpenURI::HTTPError.new('500 Internal Server Error', StringIO.new(xml))
|
103
|
+
end
|
104
|
+
|
105
|
+
e = assert_raise RCRest::CommunicationError do @fs.do_get end
|
106
|
+
|
107
|
+
expected = <<-EOF.strip
|
108
|
+
Communication error: 500 Internal Server Error(OpenURI::HTTPError)
|
109
|
+
|
110
|
+
unhandled error:
|
111
|
+
<other_error>you did the bad thing</other_error>
|
112
|
+
EOF
|
113
|
+
|
114
|
+
assert_equal expected, e.message
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_do_get_eof_error
|
118
|
+
URI::HTTP.responses << proc do
|
119
|
+
xml = '<error>you did the bad thing</error>'
|
120
|
+
raise EOFError, 'end of file reached'
|
121
|
+
end
|
122
|
+
|
123
|
+
assert_raise RCRest::CommunicationError do @fs.do_get end
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_do_post
|
127
|
+
xml = '<result>stuff</result>'
|
128
|
+
Net::HTTP.responses << xml
|
129
|
+
|
130
|
+
result = @fs.do_post
|
131
|
+
|
132
|
+
assert_equal xml, result.to_s
|
133
|
+
|
134
|
+
assert_equal 1, Net::HTTP.params.length
|
135
|
+
assert_equal 1, Net::HTTP.paths.length
|
136
|
+
assert_empty Net::HTTP.responses
|
137
|
+
|
138
|
+
assert_equal 'param=value', Net::HTTP.params.first
|
139
|
+
assert_equal '/method', Net::HTTP.paths.first
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_do_post_bad_xml
|
143
|
+
xml = '<result>stuff</result><extra/>'
|
144
|
+
Net::HTTP.responses << xml
|
145
|
+
|
146
|
+
assert_raise RCRest::CommunicationError do @fs.do_post end
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_do_post_eof_error
|
150
|
+
Net::HTTP.responses << proc do
|
151
|
+
raise IOError, 'end of file reached'
|
152
|
+
end
|
153
|
+
|
154
|
+
assert_raise RCRest::CommunicationError do @fs.do_post end
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_do_post_multipart
|
158
|
+
xml = '<result>stuff</result>'
|
159
|
+
Net::HTTP.responses << xml
|
160
|
+
|
161
|
+
result = @fs.do_post_multipart
|
162
|
+
|
163
|
+
assert_equal xml, result.to_s
|
164
|
+
|
165
|
+
assert_equal 1, Net::HTTP.params.length
|
166
|
+
assert_equal 1, Net::HTTP.paths.length
|
167
|
+
assert_empty Net::HTTP.responses
|
168
|
+
|
169
|
+
expected = <<-EOF.strip
|
170
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
171
|
+
Content-Disposition: form-data; name="param"\r
|
172
|
+
\r
|
173
|
+
value\r
|
174
|
+
--ac_2f_75_c0_43_fb_c3_67--
|
175
|
+
EOF
|
176
|
+
|
177
|
+
assert_equal expected, Net::HTTP.params.first
|
178
|
+
assert_equal '/method', Net::HTTP.paths.first
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_do_post_multipart_eof_error
|
182
|
+
Net::HTTP.responses << proc do
|
183
|
+
raise EOFError, 'end of file reached'
|
184
|
+
end
|
185
|
+
|
186
|
+
assert_raise RCRest::CommunicationError do @fs.do_post_multipart end
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_do_post_multipart_bad_xml
|
190
|
+
xml = '<result>stuff</result><extra/>'
|
191
|
+
Net::HTTP.responses << xml
|
192
|
+
|
193
|
+
assert_raise RCRest::CommunicationError do @fs.do_post_multipart end
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
class TestRCRest < Test::Unit::TestCase
|
199
|
+
|
200
|
+
def test_initialize
|
201
|
+
e = assert_raise NotImplementedError do
|
202
|
+
RCRest.new
|
203
|
+
end
|
204
|
+
|
205
|
+
assert_equal 'need to implement #intialize and set @url', e.message
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_check_error
|
209
|
+
r = RCRest.allocate
|
210
|
+
assert_raise NotImplementedError do r.check_error nil end
|
211
|
+
end
|
212
|
+
|
213
|
+
def test_make_multipart
|
214
|
+
srand 0
|
215
|
+
|
216
|
+
r = RCRest.allocate
|
217
|
+
boundary, data = r.make_multipart :a => 'b c', :x => 'y z',
|
218
|
+
:array => ['v2', 'v1'],
|
219
|
+
:newlines => "a\nb"
|
220
|
+
|
221
|
+
assert_equal 'ac_2f_75_c0_43_fb_c3_67', boundary
|
222
|
+
|
223
|
+
expected = <<-EOF.strip
|
224
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
225
|
+
Content-Disposition: form-data; name="a"\r
|
226
|
+
\r
|
227
|
+
b c\r
|
228
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
229
|
+
Content-Disposition: form-data; name="array"\r
|
230
|
+
\r
|
231
|
+
v1\r
|
232
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
233
|
+
Content-Disposition: form-data; name="array"\r
|
234
|
+
\r
|
235
|
+
v2\r
|
236
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
237
|
+
Content-Disposition: form-data; name="newlines"\r
|
238
|
+
\r
|
239
|
+
a
|
240
|
+
b\r
|
241
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
242
|
+
Content-Disposition: form-data; name="x"\r
|
243
|
+
\r
|
244
|
+
y z\r
|
245
|
+
--ac_2f_75_c0_43_fb_c3_67--
|
246
|
+
EOF
|
247
|
+
|
248
|
+
assert_equal expected, data
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_make_url
|
252
|
+
r = RCRest.allocate
|
253
|
+
r.instance_variable_set :@url, URI.parse('http://example.com/')
|
254
|
+
|
255
|
+
url = r.make_url :method, :a => 'b c', :x => 'y z', :array => ['v2', 'v1'],
|
256
|
+
:newlines => "a\nb", :funky => 'a;b+c&d'
|
257
|
+
|
258
|
+
assert_equal 'http://example.com/method?a=b%20c&array=v1&array=v2&funky=a%3Bb%2Bc%26d&newlines=a%0Ab&x=y%20z',
|
259
|
+
url.to_s
|
260
|
+
end
|
261
|
+
|
262
|
+
def test_parse_response
|
263
|
+
r = RCRest.allocate
|
264
|
+
assert_raise NotImplementedError do r.parse_response nil end
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: james-rc-rest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eric Hodel
|
8
|
+
- James Darling
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain:
|
12
|
+
date: 2007-01-31 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ZenTest
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.4.2
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: hoe
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: 1.1.7
|
32
|
+
version:
|
33
|
+
description: JAMES' VERSION OF This library makes it easy to implement REST-like web services APIs.
|
34
|
+
email: drbrain@segment7.net
|
35
|
+
executables: []
|
36
|
+
|
37
|
+
extensions: []
|
38
|
+
|
39
|
+
extra_rdoc_files: []
|
40
|
+
|
41
|
+
files:
|
42
|
+
- History.txt
|
43
|
+
- LICENSE.txt
|
44
|
+
- Manifest.txt
|
45
|
+
- README.txt
|
46
|
+
- Rakefile
|
47
|
+
- lib/rc_rest.rb
|
48
|
+
- lib/rc_rest/net_http_stub.rb
|
49
|
+
- lib/rc_rest/uri_stub.rb
|
50
|
+
- test/test_rc_rest.rb
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://seattlerb.rubyforge.org/rc-rest
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 0.0.0
|
63
|
+
version:
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.2.0
|
74
|
+
signing_key:
|
75
|
+
specification_version: 1
|
76
|
+
summary: James' mashup of rc-rest
|
77
|
+
test_files:
|
78
|
+
- test/test_rc_rest.rb
|