rc-rest 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +27 -0
- data/Manifest.txt +7 -0
- data/README +27 -0
- data/Rakefile +66 -0
- data/lib/rc_rest.rb +115 -0
- data/lib/rc_rest/uri_stub.rb +51 -0
- data/test/test_rc_rest.rb +97 -0
- metadata +52 -0
data/LICENSE
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
ADDED
@@ -0,0 +1,27 @@
|
|
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 REST web service APIs. By itself it isn't at all
|
14
|
+
useful.
|
15
|
+
|
16
|
+
== Installing rc-rest
|
17
|
+
|
18
|
+
Just install the gem:
|
19
|
+
|
20
|
+
$ sudo gem install rc-rest
|
21
|
+
|
22
|
+
== Using rc-rest
|
23
|
+
|
24
|
+
rc-rest is used by gems such as yahoo-search, google-geocode and geocoder-us.
|
25
|
+
If you'd like to write bindings a web service using rc-rest see RCRest, its
|
26
|
+
tests or the above-mentioned gems for examples.
|
27
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
|
7
|
+
$VERBOSE = nil
|
8
|
+
|
9
|
+
spec = Gem::Specification.new do |s|
|
10
|
+
s.name = 'rc-rest'
|
11
|
+
s.version = '1.0.0'
|
12
|
+
s.summary = 'Robot Co-op REST web services base class'
|
13
|
+
s.description = 'This library makes it easy to implement REST-like web services APIs.'
|
14
|
+
s.author = 'Eric Hodel'
|
15
|
+
s.email = 'eric@robotcoop.com'
|
16
|
+
|
17
|
+
s.has_rdoc = true
|
18
|
+
s.files = File.read('Manifest.txt').split($/)
|
19
|
+
s.require_path = 'lib'
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Run tests'
|
23
|
+
task :default => [ :test ]
|
24
|
+
|
25
|
+
Rake::TestTask.new('test') do |t|
|
26
|
+
t.libs << 'test'
|
27
|
+
t.pattern = 'test/test_*.rb'
|
28
|
+
t.verbose = true
|
29
|
+
end
|
30
|
+
|
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
|
+
# vim: syntax=Ruby
|
66
|
+
|
data/lib/rc_rest.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'rexml/document'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Abstract class for implementing REST APIs.
|
6
|
+
#
|
7
|
+
# === Example
|
8
|
+
#
|
9
|
+
# The following methods must be implemented in sublcasses:
|
10
|
+
#
|
11
|
+
# +initialize+:: Sets @url to the service enpoint.
|
12
|
+
# +check_error+:: Checks for errors in the server response.
|
13
|
+
# +parse_response+:: Extracts information from the server response.
|
14
|
+
#
|
15
|
+
# If you have extra URL paramaters (application id, output type) or need to
|
16
|
+
# perform URL customization, override +make_url+.
|
17
|
+
#
|
18
|
+
# class FakeService < RCRest
|
19
|
+
#
|
20
|
+
# class Error < RCRest::Error; end
|
21
|
+
#
|
22
|
+
# def initialize(appid)
|
23
|
+
# @appid = appid
|
24
|
+
# @url = URI.parse 'http://example.com/test'
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# def check_error(xml)
|
28
|
+
# raise Error, xml.elements['error'].text if xml.elements['error']
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# def make_url(params)
|
32
|
+
# params[:appid] = @appid
|
33
|
+
# super params
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# def parse_response(xml)
|
37
|
+
# return xml
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# def test(query)
|
41
|
+
# get :q => query
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# end
|
45
|
+
|
46
|
+
class RCRest
|
47
|
+
|
48
|
+
##
|
49
|
+
# Error class.
|
50
|
+
|
51
|
+
class Error < RuntimeError; end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Web services initializer.
|
55
|
+
#
|
56
|
+
# Concrete web services implementations must set the +url+ instance
|
57
|
+
# variable which must be a URI.
|
58
|
+
|
59
|
+
def initialize
|
60
|
+
raise NotImplementedError
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Must extract and raise an error from +xml+, an REXML::Document, if any.
|
65
|
+
# Must returns if no error could be found.
|
66
|
+
|
67
|
+
def check_error(xml)
|
68
|
+
raise NotImplementedError
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Performs a GET request with +params+. Calls the parse_response method on
|
73
|
+
# the concrete class with an REXML::Document instance and returns its
|
74
|
+
# result.
|
75
|
+
|
76
|
+
def get(params = {})
|
77
|
+
url = make_url params
|
78
|
+
|
79
|
+
url.open do |xml|
|
80
|
+
res = REXML::Document.new xml.read
|
81
|
+
|
82
|
+
check_error res
|
83
|
+
|
84
|
+
return parse_response(res)
|
85
|
+
end
|
86
|
+
rescue OpenURI::HTTPError => e
|
87
|
+
xml = REXML::Document.new e.io.read
|
88
|
+
check_error xml
|
89
|
+
raise
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Creates a URI from the Hash +params+. Override this then call super if
|
94
|
+
# you need to add extra params like an application id or output type.
|
95
|
+
|
96
|
+
def make_url(params)
|
97
|
+
escaped_params = params.sort_by { |k,v| k.to_s }.map do |k,v|
|
98
|
+
"#{URI.escape k.to_s}=#{URI.escape v.to_s}"
|
99
|
+
end
|
100
|
+
|
101
|
+
url = @url.dup
|
102
|
+
url.query = escaped_params.join '&'
|
103
|
+
return url
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Must parse results from +xml+, an REXML::Document, into something sensible
|
108
|
+
# for the API.
|
109
|
+
|
110
|
+
def parse_response(xml)
|
111
|
+
raise NotImplementedError
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
@@ -0,0 +1,51 @@
|
|
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
|
+
yield StringIO.new(self.class.responses.shift)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rc_rest/uri_stub'
|
3
|
+
require 'rc_rest'
|
4
|
+
|
5
|
+
class FakeService < RCRest
|
6
|
+
|
7
|
+
class Error < RCRest::Error; end
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@url = URI.parse 'http://example.com/test'
|
11
|
+
end
|
12
|
+
|
13
|
+
def check_error(xml)
|
14
|
+
raise Error, xml.elements['error'].text if xml.elements['error']
|
15
|
+
end
|
16
|
+
|
17
|
+
def test
|
18
|
+
get
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse_response(xml)
|
22
|
+
return xml
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class TestFakeService < Test::Unit::TestCase
|
28
|
+
|
29
|
+
def setup
|
30
|
+
URI::HTTP.responses = []
|
31
|
+
URI::HTTP.uris = []
|
32
|
+
|
33
|
+
@fs = FakeService.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_check_error
|
37
|
+
xml = REXML::Document.new '<error>you broked it</error>'
|
38
|
+
@fs.check_error xml
|
39
|
+
|
40
|
+
rescue FakeService::Error => e
|
41
|
+
assert_equal 'you broked it', e.message
|
42
|
+
|
43
|
+
else
|
44
|
+
flunk 'expected an error'
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_get
|
48
|
+
xml = '<result>stuff</result>'
|
49
|
+
URI::HTTP.responses << xml
|
50
|
+
|
51
|
+
result = @fs.test
|
52
|
+
|
53
|
+
assert_equal xml, result.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_get_error
|
57
|
+
def @fs.make_url(*args) # HACK extend uri_stub with error raising ability
|
58
|
+
u = Object.new
|
59
|
+
def u.open
|
60
|
+
xml = '<error>you did the bad thing</error>'
|
61
|
+
raise OpenURI::HTTPError.new('400 Bad Request', StringIO.new(xml))
|
62
|
+
end
|
63
|
+
return u
|
64
|
+
end
|
65
|
+
|
66
|
+
assert_raise FakeService::Error do @fs.test end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
class TestRCRest < Test::Unit::TestCase
|
72
|
+
|
73
|
+
def test_initialize
|
74
|
+
assert_raise NotImplementedError do RCRest.new end
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_check_error
|
78
|
+
r = RCRest.allocate
|
79
|
+
assert_raise NotImplementedError do r.check_error nil end
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_make_url
|
83
|
+
r = RCRest.allocate
|
84
|
+
r.instance_variable_set :@url, URI.parse('http://example.com/')
|
85
|
+
|
86
|
+
url = r.make_url :a => 'b c', :x => 'y z'
|
87
|
+
|
88
|
+
assert_equal 'http://example.com/?a=b%20c&x=y%20z', url.to_s
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_parse_response
|
92
|
+
r = RCRest.allocate
|
93
|
+
assert_raise NotImplementedError do r.parse_response nil end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.99
|
3
|
+
specification_version: 1
|
4
|
+
name: rc-rest
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 1.0.0
|
7
|
+
date: 2006-06-15 00:00:00 -07:00
|
8
|
+
summary: Robot Co-op REST web services base class
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: eric@robotcoop.com
|
12
|
+
homepage:
|
13
|
+
rubyforge_project:
|
14
|
+
description: This library makes it easy to implement REST-like web services APIs.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Eric Hodel
|
31
|
+
files:
|
32
|
+
- LICENSE
|
33
|
+
- Manifest.txt
|
34
|
+
- README
|
35
|
+
- Rakefile
|
36
|
+
- lib/rc_rest.rb
|
37
|
+
- lib/rc_rest/uri_stub.rb
|
38
|
+
- test/test_rc_rest.rb
|
39
|
+
test_files: []
|
40
|
+
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
extra_rdoc_files: []
|
44
|
+
|
45
|
+
executables: []
|
46
|
+
|
47
|
+
extensions: []
|
48
|
+
|
49
|
+
requirements: []
|
50
|
+
|
51
|
+
dependencies: []
|
52
|
+
|