rc-rest 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,9 @@
1
+ = 2.1.0
2
+
3
+ * Don't split strings into extra params pairs on newlines
4
+ * Added RCRest#post_multipart and RCRest#make_multipart
5
+ * Fixed various query parameter incompatiblities
6
+
1
7
  = 2.0.0
2
8
 
3
9
  * Added +method+ parameter to RCRest#make_url to add path components
data/Rakefile CHANGED
@@ -1,13 +1,13 @@
1
1
  require 'hoe'
2
2
  require './lib/rc_rest'
3
3
 
4
- DEV_DOC_PATH = 'Libraries/rc_rest'
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 = 'eric@robotcoop.com'
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 RuntimeError
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.0.0'
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
- expanded_params = []
130
-
131
- params.each do |k,v|
132
- if v.respond_to? :each then
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
- sorted_params = expanded_params.sort_by { |k,v| [k.to_s, v.to_s] }
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 = escaped_params.join '&'
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.6
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.0.0
7
- date: 2006-11-27 00:00:00 -08:00
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: eric@robotcoop.com
12
- homepage: http://dev.robotcoop.com/Libraries/rc_rest
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.4
61
+ version: 1.1.5
62
62
  version: