rc-rest 1.0.0 → 2.0.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 +10 -0
- data/{LICENSE → LICENSE.txt} +0 -0
- data/Manifest.txt +4 -2
- data/{README → README.txt} +7 -2
- data/Rakefile +15 -56
- data/lib/rc_rest.rb +90 -17
- data/lib/rc_rest/net_http_stub.rb +60 -0
- data/test/test_rc_rest.rb +42 -9
- metadata +21 -11
data/History.txt
ADDED
data/{LICENSE → LICENSE.txt}
RENAMED
File without changes
|
data/Manifest.txt
CHANGED
data/{README → README.txt}
RENAMED
@@ -10,8 +10,7 @@ http://dev.robotcoop.com/Libraries/rc-rest/
|
|
10
10
|
|
11
11
|
== About
|
12
12
|
|
13
|
-
This is an abstract class for REST web service APIs.
|
14
|
-
useful.
|
13
|
+
This is an abstract class for creating wrappers for REST web service APIs.
|
15
14
|
|
16
15
|
== Installing rc-rest
|
17
16
|
|
@@ -25,3 +24,9 @@ rc-rest is used by gems such as yahoo-search, google-geocode and geocoder-us.
|
|
25
24
|
If you'd like to write bindings a web service using rc-rest see RCRest, its
|
26
25
|
tests or the above-mentioned gems for examples.
|
27
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
CHANGED
@@ -1,66 +1,25 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'rake/testtask'
|
4
|
-
require 'rake/rdoctask'
|
5
|
-
require 'rake/gempackagetask'
|
1
|
+
require 'hoe'
|
2
|
+
require './lib/rc_rest'
|
6
3
|
|
7
|
-
|
4
|
+
DEV_DOC_PATH = 'Libraries/rc_rest'
|
8
5
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
6
|
+
hoe = Hoe.new 'rc-rest', RCRest::VERSION do |p|
|
7
|
+
p.summary = 'Robot Co-op REST web services base class'
|
8
|
+
p.description = 'This library makes it easy to implement REST-like web services APIs.'
|
9
|
+
p.author = 'Eric Hodel'
|
10
|
+
p.email = 'eric@robotcoop.com'
|
11
|
+
p.url = "http://dev.robotcoop.com/#{DEV_DOC_PATH}"
|
12
|
+
p.rubyforge_name = 'rctools'
|
16
13
|
|
17
|
-
|
18
|
-
s.files = File.read('Manifest.txt').split($/)
|
19
|
-
s.require_path = 'lib'
|
14
|
+
p.changes = File.read('History.txt').scan(/\A(=.*?)^=/m).first.first
|
20
15
|
end
|
21
16
|
|
22
|
-
|
23
|
-
task :default => [ :test ]
|
17
|
+
SPEC = hoe.spec
|
24
18
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
t.verbose = true
|
19
|
+
begin
|
20
|
+
require '../tasks'
|
21
|
+
rescue RuntimeError
|
29
22
|
end
|
30
23
|
|
31
|
-
desc 'Update Manifest.txt'
|
32
|
-
task :update_manifest do
|
33
|
-
sh "find . -type f | sed -e 's%./%%' | egrep -v 'svn|swp|~' | egrep -v '^(doc|pkg)/' | sort > Manifest.txt"
|
34
|
-
end
|
35
|
-
|
36
|
-
desc 'Generate RDoc'
|
37
|
-
Rake::RDocTask.new :rdoc do |rd|
|
38
|
-
rd.rdoc_dir = 'doc'
|
39
|
-
rd.rdoc_files.add 'lib', 'README', 'LICENSE'
|
40
|
-
rd.main = 'README'
|
41
|
-
rd.options << '-d' if `which dot` =~ /\/dot/
|
42
|
-
rd.options << '-t Robot Co-op REST Web Services'
|
43
|
-
end
|
44
|
-
|
45
|
-
desc 'Generate RDoc for dev.robotcoop.com'
|
46
|
-
Rake::RDocTask.new :dev_rdoc do |rd|
|
47
|
-
rd.rdoc_dir = '../../../www/trunk/dev/html/Libraries/rc-rest'
|
48
|
-
rd.rdoc_files.add 'lib', 'README', 'LICENSE'
|
49
|
-
rd.main = 'README'
|
50
|
-
rd.options << '-d' if `which dot` =~ /\/dot/
|
51
|
-
rd.options << '-t Robot Co-op REST Web Services'
|
52
|
-
end
|
53
|
-
|
54
|
-
desc 'Build Gem'
|
55
|
-
Rake::GemPackageTask.new spec do |pkg|
|
56
|
-
pkg.need_tar = true
|
57
|
-
end
|
58
|
-
|
59
|
-
desc 'Clean up'
|
60
|
-
task :clean => [ :clobber_rdoc, :clobber_package ]
|
61
|
-
|
62
|
-
desc 'Clean up'
|
63
|
-
task :clobber => [ :clean ]
|
64
|
-
|
65
24
|
# vim: syntax=Ruby
|
66
25
|
|
data/lib/rc_rest.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'net/http'
|
1
2
|
require 'open-uri'
|
2
3
|
require 'rexml/document'
|
3
4
|
|
@@ -21,16 +22,16 @@ require 'rexml/document'
|
|
21
22
|
#
|
22
23
|
# def initialize(appid)
|
23
24
|
# @appid = appid
|
24
|
-
# @url = URI.parse 'http://example.com/
|
25
|
+
# @url = URI.parse 'http://example.com/api/'
|
25
26
|
# end
|
26
27
|
#
|
27
28
|
# def check_error(xml)
|
28
29
|
# raise Error, xml.elements['error'].text if xml.elements['error']
|
29
30
|
# end
|
30
31
|
#
|
31
|
-
# def make_url(params)
|
32
|
+
# def make_url(method, params)
|
32
33
|
# params[:appid] = @appid
|
33
|
-
# super params
|
34
|
+
# super method, params
|
34
35
|
# end
|
35
36
|
#
|
36
37
|
# def parse_response(xml)
|
@@ -38,7 +39,7 @@ require 'rexml/document'
|
|
38
39
|
# end
|
39
40
|
#
|
40
41
|
# def test(query)
|
41
|
-
# get :q => query
|
42
|
+
# get :test, :q => query
|
42
43
|
# end
|
43
44
|
#
|
44
45
|
# end
|
@@ -46,7 +47,12 @@ require 'rexml/document'
|
|
46
47
|
class RCRest
|
47
48
|
|
48
49
|
##
|
49
|
-
#
|
50
|
+
# You are using this version of RCRest
|
51
|
+
|
52
|
+
VERSION = '2.0.0'
|
53
|
+
|
54
|
+
##
|
55
|
+
# Abstract Error class.
|
50
56
|
|
51
57
|
class Error < RuntimeError; end
|
52
58
|
|
@@ -57,24 +63,24 @@ class RCRest
|
|
57
63
|
# variable which must be a URI.
|
58
64
|
|
59
65
|
def initialize
|
60
|
-
raise NotImplementedError
|
66
|
+
raise NotImplementedError, 'need to implement #intialize and set @url'
|
61
67
|
end
|
62
68
|
|
63
69
|
##
|
64
70
|
# Must extract and raise an error from +xml+, an REXML::Document, if any.
|
65
|
-
# Must
|
71
|
+
# Must return if no error could be found.
|
66
72
|
|
67
73
|
def check_error(xml)
|
68
74
|
raise NotImplementedError
|
69
75
|
end
|
70
76
|
|
71
77
|
##
|
72
|
-
# Performs a GET request with +params+. Calls
|
73
|
-
# the concrete class with an REXML::Document instance and
|
74
|
-
# result.
|
78
|
+
# Performs a GET request for method +method+ with +params+. Calls
|
79
|
+
# #parse_response on the concrete class with an REXML::Document instance and
|
80
|
+
# returns its result.
|
75
81
|
|
76
|
-
def get(params = {})
|
77
|
-
url = make_url params
|
82
|
+
def get(method, params = {})
|
83
|
+
url = make_url method, params
|
78
84
|
|
79
85
|
url.open do |xml|
|
80
86
|
res = REXML::Document.new xml.read
|
@@ -90,15 +96,53 @@ class RCRest
|
|
90
96
|
end
|
91
97
|
|
92
98
|
##
|
93
|
-
# Creates a URI
|
94
|
-
# you need to add extra params like an
|
99
|
+
# Creates a URI for method +method+ and a Hash of parameters +params+.
|
100
|
+
# Override this then call super if you need to add extra params like an
|
101
|
+
# application id, output type, etc.
|
102
|
+
#
|
103
|
+
# If the value of a parameter responds to #each, make_url creates a
|
104
|
+
# key-value pair per value in the param.
|
105
|
+
#
|
106
|
+
# Examples:
|
107
|
+
#
|
108
|
+
# If the URL base is:
|
109
|
+
#
|
110
|
+
# http://example.com/api/
|
111
|
+
#
|
112
|
+
# then:
|
113
|
+
#
|
114
|
+
# make_url nil, :a => '1 2', :b => [4, 3]
|
115
|
+
#
|
116
|
+
# creates the URL:
|
117
|
+
#
|
118
|
+
# http://example.com/api/?a=1%202&b=3&b=4
|
119
|
+
#
|
120
|
+
# and
|
121
|
+
#
|
122
|
+
# make_url :method, :a => '1'
|
123
|
+
#
|
124
|
+
# creates the URL:
|
125
|
+
#
|
126
|
+
# http://example.com/api/method?a=1
|
95
127
|
|
96
|
-
def make_url(params)
|
97
|
-
|
128
|
+
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
|
137
|
+
end
|
138
|
+
|
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|
|
98
142
|
"#{URI.escape k.to_s}=#{URI.escape v.to_s}"
|
99
143
|
end
|
100
144
|
|
101
|
-
url = @url
|
145
|
+
url = @url + "./#{method}"
|
102
146
|
url.query = escaped_params.join '&'
|
103
147
|
return url
|
104
148
|
end
|
@@ -111,5 +155,34 @@ class RCRest
|
|
111
155
|
raise NotImplementedError
|
112
156
|
end
|
113
157
|
|
158
|
+
##
|
159
|
+
# Performs a POST request for method +method+ with +params+. Calls
|
160
|
+
# #parse_response on the concrete class with an REXML::Document instance and
|
161
|
+
# returns its result.
|
162
|
+
|
163
|
+
def post(method, params = {})
|
164
|
+
url = make_url method, params
|
165
|
+
query = url.query
|
166
|
+
url.query = nil
|
167
|
+
|
168
|
+
req = Net::HTTP::Post.new url.path
|
169
|
+
req.body = query
|
170
|
+
req.content_type = 'application/x-www-form-urlencoded'
|
171
|
+
|
172
|
+
res = Net::HTTP.start url.host, url.port do |http|
|
173
|
+
http.request req
|
174
|
+
end
|
175
|
+
|
176
|
+
xml = REXML::Document.new res.body
|
177
|
+
|
178
|
+
check_error xml
|
179
|
+
|
180
|
+
return parse_response(xml)
|
181
|
+
rescue Net::HTTPError => e
|
182
|
+
xml = REXML::Document.new e.res.body
|
183
|
+
check_error xml
|
184
|
+
raise
|
185
|
+
end
|
186
|
+
|
114
187
|
end
|
115
188
|
|
@@ -0,0 +1,60 @@
|
|
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
|
+
res = Net::HTTPResponse.new '1.0', 200, 'OK'
|
55
|
+
res.body = self.class.responses.shift
|
56
|
+
res
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
data/test/test_rc_rest.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'test/zentest_assertions'
|
2
4
|
require 'rc_rest/uri_stub'
|
5
|
+
require 'rc_rest/net_http_stub'
|
3
6
|
require 'rc_rest'
|
4
7
|
|
5
8
|
class FakeService < RCRest
|
@@ -14,8 +17,12 @@ class FakeService < RCRest
|
|
14
17
|
raise Error, xml.elements['error'].text if xml.elements['error']
|
15
18
|
end
|
16
19
|
|
17
|
-
def
|
18
|
-
get
|
20
|
+
def do_get
|
21
|
+
get :method
|
22
|
+
end
|
23
|
+
|
24
|
+
def do_post
|
25
|
+
post :method, :param => 'value'
|
19
26
|
end
|
20
27
|
|
21
28
|
def parse_response(xml)
|
@@ -30,6 +37,10 @@ class TestFakeService < Test::Unit::TestCase
|
|
30
37
|
URI::HTTP.responses = []
|
31
38
|
URI::HTTP.uris = []
|
32
39
|
|
40
|
+
Net::HTTP.params = []
|
41
|
+
Net::HTTP.paths = []
|
42
|
+
Net::HTTP.responses = []
|
43
|
+
|
33
44
|
@fs = FakeService.new
|
34
45
|
end
|
35
46
|
|
@@ -44,16 +55,17 @@ class TestFakeService < Test::Unit::TestCase
|
|
44
55
|
flunk 'expected an error'
|
45
56
|
end
|
46
57
|
|
47
|
-
def
|
58
|
+
def test_do_get
|
48
59
|
xml = '<result>stuff</result>'
|
49
60
|
URI::HTTP.responses << xml
|
50
61
|
|
51
|
-
result = @fs.
|
62
|
+
result = @fs.do_get
|
52
63
|
|
53
64
|
assert_equal xml, result.to_s
|
65
|
+
assert_equal 'http://example.com/method?', URI::HTTP.uris.first
|
54
66
|
end
|
55
67
|
|
56
|
-
def
|
68
|
+
def test_do_get_error
|
57
69
|
def @fs.make_url(*args) # HACK extend uri_stub with error raising ability
|
58
70
|
u = Object.new
|
59
71
|
def u.open
|
@@ -63,7 +75,23 @@ class TestFakeService < Test::Unit::TestCase
|
|
63
75
|
return u
|
64
76
|
end
|
65
77
|
|
66
|
-
assert_raise FakeService::Error do @fs.
|
78
|
+
assert_raise FakeService::Error do @fs.do_get end
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_do_post
|
82
|
+
xml = '<result>stuff</result>'
|
83
|
+
Net::HTTP.responses << xml
|
84
|
+
|
85
|
+
result = @fs.do_post
|
86
|
+
|
87
|
+
assert_equal xml, result.to_s
|
88
|
+
|
89
|
+
assert_equal 1, Net::HTTP.params.length
|
90
|
+
assert_equal 1, Net::HTTP.paths.length
|
91
|
+
assert_empty Net::HTTP.responses
|
92
|
+
|
93
|
+
assert_equal 'param=value', Net::HTTP.params.first
|
94
|
+
assert_equal '/method', Net::HTTP.paths.first
|
67
95
|
end
|
68
96
|
|
69
97
|
end
|
@@ -71,7 +99,11 @@ end
|
|
71
99
|
class TestRCRest < Test::Unit::TestCase
|
72
100
|
|
73
101
|
def test_initialize
|
74
|
-
|
102
|
+
RCRest.new
|
103
|
+
rescue NotImplementedError => e
|
104
|
+
assert_equal 'need to implement #intialize and set @url', e.message
|
105
|
+
else
|
106
|
+
flunk 'expected NotImplementedError'
|
75
107
|
end
|
76
108
|
|
77
109
|
def test_check_error
|
@@ -83,9 +115,10 @@ class TestRCRest < Test::Unit::TestCase
|
|
83
115
|
r = RCRest.allocate
|
84
116
|
r.instance_variable_set :@url, URI.parse('http://example.com/')
|
85
117
|
|
86
|
-
url = r.make_url :a => 'b c', :x => 'y z'
|
118
|
+
url = r.make_url :method, :a => 'b c', :x => 'y z', :array => ['v2', 'v1']
|
87
119
|
|
88
|
-
assert_equal 'http://example.com
|
120
|
+
assert_equal 'http://example.com/method?a=b%20c&array=v1&array=v2&x=y%20z',
|
121
|
+
url.to_s
|
89
122
|
end
|
90
123
|
|
91
124
|
def test_parse_response
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.
|
2
|
+
rubygems_version: 0.9.0.6
|
3
3
|
specification_version: 1
|
4
4
|
name: rc-rest
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version:
|
7
|
-
date: 2006-
|
6
|
+
version: 2.0.0
|
7
|
+
date: 2006-11-27 00:00:00 -08:00
|
8
8
|
summary: Robot Co-op REST web services base class
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
11
|
email: eric@robotcoop.com
|
12
|
-
homepage:
|
13
|
-
rubyforge_project:
|
12
|
+
homepage: http://dev.robotcoop.com/Libraries/rc_rest
|
13
|
+
rubyforge_project: rctools
|
14
14
|
description: This library makes it easy to implement REST-like web services APIs.
|
15
15
|
autorequire:
|
16
16
|
default_executable:
|
@@ -29,15 +29,17 @@ post_install_message:
|
|
29
29
|
authors:
|
30
30
|
- Eric Hodel
|
31
31
|
files:
|
32
|
-
-
|
32
|
+
- History.txt
|
33
|
+
- LICENSE.txt
|
33
34
|
- Manifest.txt
|
34
|
-
- README
|
35
|
+
- README.txt
|
35
36
|
- Rakefile
|
36
37
|
- lib/rc_rest.rb
|
38
|
+
- lib/rc_rest/net_http_stub.rb
|
37
39
|
- lib/rc_rest/uri_stub.rb
|
38
40
|
- test/test_rc_rest.rb
|
39
|
-
test_files:
|
40
|
-
|
41
|
+
test_files:
|
42
|
+
- test/test_rc_rest.rb
|
41
43
|
rdoc_options: []
|
42
44
|
|
43
45
|
extra_rdoc_files: []
|
@@ -48,5 +50,13 @@ extensions: []
|
|
48
50
|
|
49
51
|
requirements: []
|
50
52
|
|
51
|
-
dependencies:
|
52
|
-
|
53
|
+
dependencies:
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: hoe
|
56
|
+
version_requirement:
|
57
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.1.4
|
62
|
+
version:
|