rc-rest 1.0.0

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