james-rc-rest 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+
@@ -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
+
@@ -0,0 +1,9 @@
1
+ History.txt
2
+ LICENSE.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ lib/rc_rest.rb
7
+ lib/rc_rest/net_http_stub.rb
8
+ lib/rc_rest/uri_stub.rb
9
+ test/test_rc_rest.rb
@@ -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
+
@@ -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
+
@@ -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