rc-rest 2.0.0 → 2.1.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/History.txt +6 -0
- data/Rakefile +3 -3
- data/lib/rc_rest.rb +72 -16
- data/test/test_rc_rest.rb +71 -2
- metadata +6 -6
data/History.txt
CHANGED
data/Rakefile
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'hoe'
|
2
2
|
require './lib/rc_rest'
|
3
3
|
|
4
|
-
DEV_DOC_PATH = 'Libraries/
|
4
|
+
DEV_DOC_PATH = 'Libraries/rc-rest'
|
5
5
|
|
6
6
|
hoe = Hoe.new 'rc-rest', RCRest::VERSION do |p|
|
7
7
|
p.summary = 'Robot Co-op REST web services base class'
|
8
8
|
p.description = 'This library makes it easy to implement REST-like web services APIs.'
|
9
9
|
p.author = 'Eric Hodel'
|
10
|
-
p.email = '
|
10
|
+
p.email = 'drbrain@segment7.net'
|
11
11
|
p.url = "http://dev.robotcoop.com/#{DEV_DOC_PATH}"
|
12
12
|
p.rubyforge_name = 'rctools'
|
13
13
|
|
@@ -18,7 +18,7 @@ SPEC = hoe.spec
|
|
18
18
|
|
19
19
|
begin
|
20
20
|
require '../tasks'
|
21
|
-
rescue
|
21
|
+
rescue LoadError
|
22
22
|
end
|
23
23
|
|
24
24
|
# vim: syntax=Ruby
|
data/lib/rc_rest.rb
CHANGED
@@ -14,7 +14,7 @@ require 'rexml/document'
|
|
14
14
|
# +parse_response+:: Extracts information from the server response.
|
15
15
|
#
|
16
16
|
# If you have extra URL paramaters (application id, output type) or need to
|
17
|
-
# perform URL customization, override +make_url+.
|
17
|
+
# perform URL customization, override +make_url+ and +make_multipart+.
|
18
18
|
#
|
19
19
|
# class FakeService < RCRest
|
20
20
|
#
|
@@ -49,7 +49,7 @@ class RCRest
|
|
49
49
|
##
|
50
50
|
# You are using this version of RCRest
|
51
51
|
|
52
|
-
VERSION = '2.
|
52
|
+
VERSION = '2.1.0'
|
53
53
|
|
54
54
|
##
|
55
55
|
# Abstract Error class.
|
@@ -74,6 +74,20 @@ class RCRest
|
|
74
74
|
raise NotImplementedError
|
75
75
|
end
|
76
76
|
|
77
|
+
def expand_params(params) # :nodoc:
|
78
|
+
expanded_params = []
|
79
|
+
|
80
|
+
params.each do |k,v|
|
81
|
+
if v.respond_to? :each and not String === v then
|
82
|
+
v.each { |v| expanded_params << [k, v] }
|
83
|
+
else
|
84
|
+
expanded_params << [k, v]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
expanded_params.sort_by { |k,v| [k.to_s, v.to_s] }
|
89
|
+
end
|
90
|
+
|
77
91
|
##
|
78
92
|
# Performs a GET request for method +method+ with +params+. Calls
|
79
93
|
# #parse_response on the concrete class with an REXML::Document instance and
|
@@ -126,27 +140,39 @@ class RCRest
|
|
126
140
|
# http://example.com/api/method?a=1
|
127
141
|
|
128
142
|
def make_url(method, params = nil)
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
v.each { |v| expanded_params << [k, v] }
|
134
|
-
else
|
135
|
-
expanded_params << [k, v]
|
136
|
-
end
|
143
|
+
escaped_params = expand_params(params).map do |k,v|
|
144
|
+
k = URI.escape(k.to_s).gsub(';', '%3B').gsub('+', '%2B').gsub('&', '%26')
|
145
|
+
v = URI.escape(v.to_s).gsub(';', '%3B').gsub('+', '%2B').gsub('&', '%26')
|
146
|
+
"#{k}=#{v}"
|
137
147
|
end
|
138
148
|
|
139
|
-
|
140
|
-
|
141
|
-
escaped_params = sorted_params.map do |k,v|
|
142
|
-
"#{URI.escape k.to_s}=#{URI.escape v.to_s}"
|
143
|
-
end
|
149
|
+
query = escaped_params.join '&'
|
144
150
|
|
145
151
|
url = @url + "./#{method}"
|
146
|
-
url.query =
|
152
|
+
url.query = query
|
147
153
|
return url
|
148
154
|
end
|
149
155
|
|
156
|
+
##
|
157
|
+
# Creates a multipart form post for the Hash of parameters +params+.
|
158
|
+
# Override this then call super if you need to add extra params like an
|
159
|
+
# application id, output type, etc.
|
160
|
+
#
|
161
|
+
# #make_multipart handles arguments similarly to #make_url.
|
162
|
+
|
163
|
+
def make_multipart(params)
|
164
|
+
boundary = (0...8).map { rand(255).to_s 16 }.join '_'
|
165
|
+
data = expand_params(params).map do |key, value|
|
166
|
+
[ "--#{boundary}",
|
167
|
+
"Content-Disposition: form-data; name=\"#{key}\"",
|
168
|
+
nil,
|
169
|
+
value]
|
170
|
+
end
|
171
|
+
|
172
|
+
data << "--#{boundary}--"
|
173
|
+
return [boundary, data.join("\r\n")]
|
174
|
+
end
|
175
|
+
|
150
176
|
##
|
151
177
|
# Must parse results from +xml+, an REXML::Document, into something sensible
|
152
178
|
# for the API.
|
@@ -184,5 +210,35 @@ class RCRest
|
|
184
210
|
raise
|
185
211
|
end
|
186
212
|
|
213
|
+
##
|
214
|
+
# Performs a POST request for method +method+ with +params+, submitting a
|
215
|
+
# multipart form. Calls #parse_response on the concrete class with an
|
216
|
+
# REXML::Document instance and returns its result.
|
217
|
+
|
218
|
+
def post_multipart(method, params = {})
|
219
|
+
url = make_url method, {}
|
220
|
+
url.query = nil
|
221
|
+
|
222
|
+
boundary, data = make_multipart params
|
223
|
+
|
224
|
+
req = Net::HTTP::Post.new url.path
|
225
|
+
req.content_type = "multipart/form-data; boundary=#{boundary}"
|
226
|
+
req.body = data
|
227
|
+
|
228
|
+
res = Net::HTTP.start url.host, url.port do |http|
|
229
|
+
http.request req
|
230
|
+
end
|
231
|
+
|
232
|
+
xml = REXML::Document.new res.body
|
233
|
+
|
234
|
+
check_error xml
|
235
|
+
|
236
|
+
return parse_response(xml)
|
237
|
+
rescue Net::HTTPError => e
|
238
|
+
xml = REXML::Document.new e.res.body
|
239
|
+
check_error xml
|
240
|
+
raise
|
241
|
+
end
|
242
|
+
|
187
243
|
end
|
188
244
|
|
data/test/test_rc_rest.rb
CHANGED
@@ -25,6 +25,10 @@ class FakeService < RCRest
|
|
25
25
|
post :method, :param => 'value'
|
26
26
|
end
|
27
27
|
|
28
|
+
def do_post_multipart
|
29
|
+
post_multipart :method, :param => 'value'
|
30
|
+
end
|
31
|
+
|
28
32
|
def parse_response(xml)
|
29
33
|
return xml
|
30
34
|
end
|
@@ -42,6 +46,8 @@ class TestFakeService < Test::Unit::TestCase
|
|
42
46
|
Net::HTTP.responses = []
|
43
47
|
|
44
48
|
@fs = FakeService.new
|
49
|
+
|
50
|
+
srand 0
|
45
51
|
end
|
46
52
|
|
47
53
|
def test_check_error
|
@@ -94,6 +100,30 @@ class TestFakeService < Test::Unit::TestCase
|
|
94
100
|
assert_equal '/method', Net::HTTP.paths.first
|
95
101
|
end
|
96
102
|
|
103
|
+
def test_do_post_multipart
|
104
|
+
xml = '<result>stuff</result>'
|
105
|
+
Net::HTTP.responses << xml
|
106
|
+
|
107
|
+
result = @fs.do_post_multipart
|
108
|
+
|
109
|
+
assert_equal xml, result.to_s
|
110
|
+
|
111
|
+
assert_equal 1, Net::HTTP.params.length
|
112
|
+
assert_equal 1, Net::HTTP.paths.length
|
113
|
+
assert_empty Net::HTTP.responses
|
114
|
+
|
115
|
+
expected = <<-EOF.strip
|
116
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
117
|
+
Content-Disposition: form-data; name="param"\r
|
118
|
+
\r
|
119
|
+
value\r
|
120
|
+
--ac_2f_75_c0_43_fb_c3_67--
|
121
|
+
EOF
|
122
|
+
|
123
|
+
assert_equal expected, Net::HTTP.params.first
|
124
|
+
assert_equal '/method', Net::HTTP.paths.first
|
125
|
+
end
|
126
|
+
|
97
127
|
end
|
98
128
|
|
99
129
|
class TestRCRest < Test::Unit::TestCase
|
@@ -111,13 +141,52 @@ class TestRCRest < Test::Unit::TestCase
|
|
111
141
|
assert_raise NotImplementedError do r.check_error nil end
|
112
142
|
end
|
113
143
|
|
144
|
+
def test_make_multipart
|
145
|
+
srand 0
|
146
|
+
|
147
|
+
r = RCRest.allocate
|
148
|
+
boundary, data = r.make_multipart :a => 'b c', :x => 'y z',
|
149
|
+
:array => ['v2', 'v1'],
|
150
|
+
:newlines => "a\nb"
|
151
|
+
|
152
|
+
assert_equal 'ac_2f_75_c0_43_fb_c3_67', boundary
|
153
|
+
|
154
|
+
expected = <<-EOF.strip
|
155
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
156
|
+
Content-Disposition: form-data; name="a"\r
|
157
|
+
\r
|
158
|
+
b c\r
|
159
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
160
|
+
Content-Disposition: form-data; name="array"\r
|
161
|
+
\r
|
162
|
+
v1\r
|
163
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
164
|
+
Content-Disposition: form-data; name="array"\r
|
165
|
+
\r
|
166
|
+
v2\r
|
167
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
168
|
+
Content-Disposition: form-data; name="newlines"\r
|
169
|
+
\r
|
170
|
+
a
|
171
|
+
b\r
|
172
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
173
|
+
Content-Disposition: form-data; name="x"\r
|
174
|
+
\r
|
175
|
+
y z\r
|
176
|
+
--ac_2f_75_c0_43_fb_c3_67--
|
177
|
+
EOF
|
178
|
+
|
179
|
+
assert_equal expected, data
|
180
|
+
end
|
181
|
+
|
114
182
|
def test_make_url
|
115
183
|
r = RCRest.allocate
|
116
184
|
r.instance_variable_set :@url, URI.parse('http://example.com/')
|
117
185
|
|
118
|
-
url = r.make_url :method, :a => 'b c', :x => 'y z', :array => ['v2', 'v1']
|
186
|
+
url = r.make_url :method, :a => 'b c', :x => 'y z', :array => ['v2', 'v1'],
|
187
|
+
:newlines => "a\nb", :funky => 'a;b+c&d'
|
119
188
|
|
120
|
-
assert_equal 'http://example.com/method?a=b%20c&array=v1&array=v2&x=y%20z',
|
189
|
+
assert_equal 'http://example.com/method?a=b%20c&array=v1&array=v2&funky=a%3Bb%2Bc%26d&newlines=a%0Ab&x=y%20z',
|
121
190
|
url.to_s
|
122
191
|
end
|
123
192
|
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.0.
|
2
|
+
rubygems_version: 0.9.0.7
|
3
3
|
specification_version: 1
|
4
4
|
name: rc-rest
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 2.
|
7
|
-
date: 2006-11-
|
6
|
+
version: 2.1.0
|
7
|
+
date: 2006-11-29 00:00:00 -08:00
|
8
8
|
summary: Robot Co-op REST web services base class
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
|
-
email:
|
12
|
-
homepage: http://dev.robotcoop.com/Libraries/
|
11
|
+
email: drbrain@segment7.net
|
12
|
+
homepage: http://dev.robotcoop.com/Libraries/rc-rest
|
13
13
|
rubyforge_project: rctools
|
14
14
|
description: This library makes it easy to implement REST-like web services APIs.
|
15
15
|
autorequire:
|
@@ -58,5 +58,5 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.1.
|
61
|
+
version: 1.1.5
|
62
62
|
version:
|