remote_api 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +6 -4
- data/README.txt +91 -20
- data/Rakefile +17 -0
- data/lib/remote_api/base.rb +76 -40
- data/lib/remote_api/version.rb +2 -2
- data/lib/remote_api.rb +3 -3
- data/spec/base_spec.rb +65 -0
- data/{test/test_helper.rb → spec/spec_helper.rb} +0 -2
- data/spec/xml_response_spec.rb +77 -0
- data/{test/xml_test.rb → spec/xml_spec.rb} +33 -35
- data/test/spec_test.rb +19 -0
- metadata +8 -9
- data/test/base_test.rb +0 -58
- data/test/xml_response_test.rb +0 -77
data/Manifest.txt
CHANGED
@@ -12,7 +12,9 @@ lib/remote_api/xml_response.rb
|
|
12
12
|
|
13
13
|
lib/remote_api/version.rb
|
14
14
|
|
15
|
-
test/
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
test/spec_test.rb
|
16
|
+
|
17
|
+
spec/base_spec.rb
|
18
|
+
spec/spec_helper.rb
|
19
|
+
spec/xml_response_spec.rb
|
20
|
+
spec/xml_spec.rb
|
data/README.txt
CHANGED
@@ -1,41 +1,112 @@
|
|
1
|
-
|
1
|
+
= Remote API
|
2
2
|
|
3
3
|
Rubyforge Project: http://rubyforge.org/projects/remote-api/
|
4
4
|
|
5
|
-
|
5
|
+
Installation: <tt>gem install remote_api</tt>
|
6
6
|
|
7
|
-
|
7
|
+
== Overview
|
8
8
|
|
9
|
-
|
9
|
+
+RemoteAPI+ is an abstraction from API classes. I noticed that there was very overlapping
|
10
|
+
functionality in the various API wrappers I had going. This gem will make it much easier to
|
11
|
+
write an API wrapper by taking care of the common stuff. All you have to do is write the
|
12
|
+
parts that are specific to the API you are talking to.
|
13
|
+
|
14
|
+
=== Usage and Checklist
|
15
|
+
|
16
|
+
To use this gem you should follow this simple checklist of things that you must implement on
|
17
|
+
your own.
|
18
|
+
|
19
|
+
1. Create a class that descends from RemoteAPI: <tt>class Foo < RemoteAPI; end</tt>
|
20
|
+
2. Define the url to access with the url dsl_accessor <tt>url 'some url here'</tt>
|
21
|
+
3. Add a +request+ method to your class. This should return the body of your request.
|
22
|
+
4. Add a +assert_response+ method. This method should raise exceptions if the contents
|
23
|
+
of <tt>@response</tt> describe a fatal error.
|
24
|
+
5. Add a +process+ method. This parses the API response. It should pull the relevant
|
25
|
+
data out of the <tt>@response</tt> object and put it in instance variables.
|
26
|
+
6. Create <tt>attr_reader</tt>s for the instance variables from the +process+ method that
|
27
|
+
you want to expose as methods.
|
28
|
+
|
29
|
+
=== Basic Walkthrough
|
30
|
+
|
31
|
+
I'm going to explain just what happens under the hood of a class that inherits from RemoteAPI.
|
32
|
+
|
33
|
+
So lets say that I am writing an API to ship a package. It requires address and weight, each
|
34
|
+
on their own line in a YAML like format. On success, it returns the tracking number of the
|
35
|
+
package. On failure it just says "There was an error!".
|
36
|
+
|
37
|
+
Here is an API class that can handle that:
|
10
38
|
|
11
|
-
This is an example of how to access a little API that allows you to change the title of an object.
|
12
|
-
The API would return the title, but in Title Case, and we want to capture that.
|
13
|
-
|
14
39
|
require 'remote_api'
|
15
40
|
|
16
|
-
class
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
url 'http://test.com/posts/change_title'
|
41
|
+
class Shipment < RemoteAPI
|
42
|
+
attr_reader :tracking_number
|
43
|
+
url 'http://test.com/shipments'
|
21
44
|
|
22
45
|
def request
|
23
|
-
|
46
|
+
<<-EOF
|
47
|
+
address: #{@address}
|
48
|
+
weight: #{@weight}
|
49
|
+
EOF
|
24
50
|
end
|
25
51
|
|
26
52
|
def assert_success
|
27
|
-
if @response =~
|
28
|
-
raise ResponseFailure, $1
|
29
|
-
end
|
53
|
+
raise ResponseFailure if @response =~ /error/
|
30
54
|
end
|
31
55
|
|
32
56
|
def process
|
33
|
-
@
|
57
|
+
@tracking_number = @response
|
34
58
|
end
|
35
59
|
|
36
60
|
end
|
61
|
+
|
62
|
+
All the magic happens on instantiation. The request is built and sent, and the response is
|
63
|
+
processed. It all starts with:
|
64
|
+
|
65
|
+
Shipment.new(:address => '123 Fake St', :weight => 12)
|
66
|
+
|
67
|
+
==== Instantiation
|
68
|
+
|
69
|
+
The hash passed to +new+ is converted to instance variables. In this case the Shipment instance
|
70
|
+
now has <tt>@address</tt> and <tt>@weight</tt> instance variables in it.
|
71
|
+
|
72
|
+
==== Building the request
|
73
|
+
|
74
|
+
The next step is the +request+ method is called. This should return a string, something that can
|
75
|
+
be converted to a sttring. This string will be sent out as the request body to the remote server.
|
76
|
+
|
77
|
+
==== Sending the request
|
78
|
+
|
79
|
+
Now that we have built the request, the connection is made to the remote server at the url defined
|
80
|
+
by the dsl_accessor +url+. The request is sent with the result of the +request+ method as the body.
|
81
|
+
What the server returns is stored in the <tt>@response</tt> variable. This all happens automatically.
|
82
|
+
|
83
|
+
==== Assertion of Success
|
84
|
+
|
85
|
+
Most API's have some sort of error reporting. The <tt>assert_success</tt> method is for raising
|
86
|
+
exceptions if the API reports any errors. The ResponseFailure exception is built into RemoteAPI.
|
87
|
+
Parse the response to see if it reports errors.
|
88
|
+
|
89
|
+
==== Processing the response
|
90
|
+
|
91
|
+
You must deinfe a +process+ method. This method will parse the response and stuff any needed data into
|
92
|
+
instance varibales. In this case, we are simply getting the entire response since that will be the
|
93
|
+
only tracking number that we are expecting.
|
94
|
+
|
95
|
+
==== Using the result
|
96
|
+
|
97
|
+
Define a <tt>attr_reader</tt> for each instance variable from the +process+ you want exposed. Then use
|
98
|
+
your new object as you would any other object
|
99
|
+
|
100
|
+
pkg = Shipment.new(:address => '123 Fake St', :weight => 12)
|
101
|
+
puts "Sucess! Your tracking number is #{pkg.tracking_number}"
|
37
102
|
|
38
|
-
|
39
|
-
|
103
|
+
==== Using the result with Exception handling
|
104
|
+
|
105
|
+
You probably don't want your whole app to crash if there is an API error. This is why ruby has exception
|
106
|
+
handling
|
40
107
|
|
41
|
-
|
108
|
+
begin
|
109
|
+
pkg = Shipment.new(:address => '123 Fake St', :weight => 'foo')
|
110
|
+
rescue RemoteAPI::ResponseFailure
|
111
|
+
puts "Unable to ship that package. The API really didn't like it."
|
112
|
+
end
|
data/Rakefile
CHANGED
@@ -46,4 +46,21 @@ hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
|
46
46
|
#p.changes - A description of the release's latest changes.
|
47
47
|
p.extra_deps = [['activesupport'], ['dsl_accessor']]
|
48
48
|
#p.spec_extras - A hash of extra values to set in the gemspec.
|
49
|
+
end
|
50
|
+
|
51
|
+
require 'spec/rake/spectask'
|
52
|
+
desc "Run all specifications"
|
53
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
54
|
+
t.spec_files = FileList['spec/**/*.rb']
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "Run all specification with spec output"
|
58
|
+
Spec::Rake::SpecTask.new('spec_print') do |t|
|
59
|
+
t.spec_files = FileList['spec/**/*.rb']
|
60
|
+
t.spec_opts = ["-f", "s"]
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "Tell you to run specs instead of tests"
|
64
|
+
task :test do
|
65
|
+
puts "This gem has specs, not tests. Run 'rake spec' or 'rake spec_print' instead"
|
49
66
|
end
|
data/lib/remote_api/base.rb
CHANGED
@@ -1,42 +1,71 @@
|
|
1
1
|
class RemoteAPI
|
2
2
|
class ResponseFailure < RuntimeError; end
|
3
3
|
|
4
|
-
dsl_accessor :debug
|
4
|
+
dsl_accessor :debug
|
5
5
|
dsl_accessor :log_file
|
6
6
|
dsl_accessor :url
|
7
7
|
dsl_accessor :content_type
|
8
|
-
|
9
|
-
class ResponseFailure < RuntimeError; end
|
10
8
|
|
11
|
-
|
12
|
-
|
9
|
+
class ResponseFailure < RuntimeError; end
|
10
|
+
|
11
|
+
# Prepare a new API object. This will not make the actual remote
|
12
|
+
# call until the +call+ method is used. To execute the API call
|
13
|
+
# immediately, use the +call+ class method.
|
14
|
+
#
|
15
|
+
# Any options you pass in as a hash will be created as instance
|
16
|
+
# variables. So the below example will have an instance variable
|
17
|
+
# <tt>@foo</tt> in the generated API object.
|
18
|
+
#
|
19
|
+
# api = MyApi.new(:foo => 'bar')
|
20
|
+
# api.call
|
21
|
+
# api.some_result #=> "You said bar!"
|
13
22
|
#
|
14
|
-
# Then we send the request, assert that is the response is successful, and process the
|
15
|
-
# response.
|
16
23
|
def initialize(params = {})
|
17
24
|
params.each do |k, v|
|
18
25
|
instance_variable_set "@#{k}", v
|
19
26
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
27
|
+
end
|
28
|
+
|
29
|
+
# Instatiate and call the API in one step.
|
30
|
+
#
|
31
|
+
# api = MyApi.call(:foo => 'bar')
|
32
|
+
# api.some_result #=> "You said bar!"
|
33
|
+
#
|
34
|
+
def self.call(params = {})
|
35
|
+
returning new(params) do |api|
|
36
|
+
api.call
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Execute an instance of an API object that has not been sent to the
|
41
|
+
# remote server yet.
|
42
|
+
#
|
43
|
+
# api = MyApi.new(:foo => 'bar')
|
44
|
+
# api.call
|
45
|
+
# api.some_result #=> "You said bar!"
|
46
|
+
#
|
47
|
+
def call
|
48
|
+
returning self do
|
49
|
+
send_request
|
50
|
+
assert_success
|
51
|
+
process
|
52
|
+
end
|
24
53
|
end
|
25
54
|
|
26
55
|
private
|
27
56
|
|
28
|
-
# Send the request to the
|
57
|
+
# Send the request to the remote servers
|
29
58
|
def send_request(request_body = nil)
|
30
59
|
request_body ||= request
|
31
60
|
debug_request(request_body)
|
32
61
|
|
33
62
|
# Setup HTTP objects
|
34
|
-
url
|
35
|
-
http
|
63
|
+
url = URI.parse(server_url)
|
64
|
+
http = Net::HTTP.new(url.host, url.port)
|
36
65
|
|
37
66
|
if url.is_a?(URI::HTTPS)
|
38
|
-
|
39
|
-
|
67
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
68
|
+
http.use_ssl = true
|
40
69
|
end
|
41
70
|
|
42
71
|
# Setup request options
|
@@ -49,28 +78,30 @@ class RemoteAPI
|
|
49
78
|
|
50
79
|
# Convert response to proper format
|
51
80
|
@response = prepare_response(response)
|
52
|
-
|
53
|
-
# Code hook to raise execption if error conditions are met
|
81
|
+
|
82
|
+
# Code hook to raise execption if error conditions are met
|
54
83
|
assert_success
|
55
84
|
end
|
56
85
|
|
57
|
-
|
58
|
-
|
59
|
-
end
|
86
|
+
# override this method and raises exception on failure
|
87
|
+
def assert_success; end
|
60
88
|
|
89
|
+
# override this method to parse your response
|
61
90
|
def process
|
62
|
-
raise 'You must define a "process" method! This should should parse the @response and
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
def request
|
67
|
-
raise 'You must define a "request" method! This should create the body of your API request'
|
91
|
+
raise 'You must define a "process" method! This should should parse the @response and extract relevant data.'
|
92
|
+
end
|
93
|
+
|
94
|
+
# override this method to define your request body
|
95
|
+
def request
|
96
|
+
raise 'You must define a "request" method! This should create the body of your API request'
|
68
97
|
end
|
69
98
|
|
99
|
+
# Convert response to proper format
|
70
100
|
def prepare_response(response)
|
71
101
|
response.body
|
72
102
|
end
|
73
103
|
|
104
|
+
# Get the full path to the remote server
|
74
105
|
def server_url
|
75
106
|
url = self.class.url
|
76
107
|
|
@@ -78,7 +109,11 @@ class RemoteAPI
|
|
78
109
|
when 'String'
|
79
110
|
url
|
80
111
|
when 'Hash'
|
81
|
-
|
112
|
+
if env = ENV['RAILS_ENV']
|
113
|
+
url.symbolize_keys[ENV['RAILS_ENV'].to_sym]
|
114
|
+
else
|
115
|
+
raise 'Cannot use a hash for server url outside of a rails environment'
|
116
|
+
end
|
82
117
|
when 'Proc'
|
83
118
|
url.call
|
84
119
|
else
|
@@ -86,16 +121,17 @@ class RemoteAPI
|
|
86
121
|
end
|
87
122
|
end
|
88
123
|
|
124
|
+
# Convert reponse to proper format
|
89
125
|
def format_response(response)
|
90
126
|
response
|
91
127
|
end
|
92
|
-
|
93
|
-
def log(data)
|
94
|
-
data = "\n\n\n\n\n*** #{Time.now}\n\n" + data
|
95
|
-
if file = self.class.log_file
|
96
|
-
File.open(file, 'a') { |f| f.write data }
|
97
|
-
end
|
98
|
-
end
|
128
|
+
|
129
|
+
def log(data)
|
130
|
+
data = "\n\n\n\n\n*** #{Time.now}\n\n" + data
|
131
|
+
if file = self.class.log_file
|
132
|
+
File.open(file, 'a') { |f| f.write data }
|
133
|
+
end
|
134
|
+
end
|
99
135
|
|
100
136
|
def debug_request(request_body)
|
101
137
|
return unless self.class.debug || self.class.log_file
|
@@ -106,8 +142,8 @@ class RemoteAPI
|
|
106
142
|
-----------
|
107
143
|
|
108
144
|
#{request_body}
|
109
|
-
DEBUG
|
110
|
-
puts text if self.class.debug
|
145
|
+
DEBUG
|
146
|
+
puts text if self.class.debug
|
111
147
|
log(text) if self.class.log_file
|
112
148
|
end
|
113
149
|
|
@@ -125,9 +161,9 @@ DEBUG
|
|
125
161
|
|
126
162
|
#{text}
|
127
163
|
|
128
|
-
DEBUG
|
129
|
-
|
130
|
-
puts text if self.class.debug
|
164
|
+
DEBUG
|
165
|
+
|
166
|
+
puts text if self.class.debug
|
131
167
|
log(text) if self.class.log_file
|
132
168
|
end
|
133
169
|
end
|
data/lib/remote_api/version.rb
CHANGED
data/lib/remote_api.rb
CHANGED
@@ -8,6 +8,6 @@ require 'active_support'
|
|
8
8
|
require 'dsl_accessor'
|
9
9
|
|
10
10
|
# remote_api
|
11
|
-
|
12
|
-
require 'remote_api/
|
13
|
-
|
11
|
+
%w( base xml xml_response ).each do |file|
|
12
|
+
require File.dirname(__FILE__) + '/remote_api/' + file
|
13
|
+
end
|
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
FakeWeb.register_uri(
|
4
|
+
'http://test.com/base',
|
5
|
+
:string => '<server_response>The server recieved your message</server_response>'
|
6
|
+
)
|
7
|
+
FakeWeb.register_uri(
|
8
|
+
'http://test.com/base/error',
|
9
|
+
:string => '<server_response>An error occurred!</server_response>'
|
10
|
+
)
|
11
|
+
|
12
|
+
context "A Generic API" do
|
13
|
+
|
14
|
+
setup do
|
15
|
+
BaseApi.url = 'http://test.com/base'
|
16
|
+
end
|
17
|
+
|
18
|
+
specify "'initialize'_should_set_hash_keys_as_instance_variables" do
|
19
|
+
api = BaseApi.new(:foo => 'bar', :baz => 'taz', :message => 'Hello')
|
20
|
+
|
21
|
+
api.instance_eval { @foo }.should_be == 'bar'
|
22
|
+
api.instance_eval { @baz }.should_be == 'taz'
|
23
|
+
api.instance_eval { @message }.should_be == 'Hello'
|
24
|
+
end
|
25
|
+
|
26
|
+
specify "instance method 'call' should perform API call" do
|
27
|
+
api = BaseApi.new(:message => 'Hello World!')
|
28
|
+
api.return_message.should_be_nil
|
29
|
+
|
30
|
+
api.call
|
31
|
+
api.return_message.should_be == 'The server recieved your message'
|
32
|
+
end
|
33
|
+
|
34
|
+
specify "class method 'call' should instantiate and call API" do
|
35
|
+
api = BaseApi.call(:message => 'Hello World!')
|
36
|
+
api.return_message.should_be == 'The server recieved your message'
|
37
|
+
end
|
38
|
+
|
39
|
+
specify "'assert_success' should raise and exception on an error" do
|
40
|
+
BaseApi.url = 'http://test.com/base/error'
|
41
|
+
proc { BaseApi.call(:message => 'foo') }.should_raise RemoteAPI::ResponseFailure
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
class BaseApi < RemoteAPI
|
48
|
+
attr_reader :return_message
|
49
|
+
|
50
|
+
def request
|
51
|
+
x = Builder::XmlMarkup.new
|
52
|
+
x.my_request @message
|
53
|
+
end
|
54
|
+
|
55
|
+
def assert_success
|
56
|
+
if @response =~ %r{<server_response>(.*error.*)</server_response>}
|
57
|
+
raise ResponseFailure, $1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def process
|
62
|
+
@return_message = @response.gsub(%r{</?server_response>}, '')
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
context "An XML Response object" do
|
4
|
+
|
5
|
+
XML = <<XML
|
6
|
+
<response>
|
7
|
+
<title>Test Title</title>
|
8
|
+
<items>
|
9
|
+
<item>
|
10
|
+
<name>Bob</name>
|
11
|
+
<id>1</id>
|
12
|
+
</item>
|
13
|
+
<item>
|
14
|
+
<name>Joe</name>
|
15
|
+
<id>2</id>
|
16
|
+
</item>
|
17
|
+
<item>
|
18
|
+
<name>Walter</name>
|
19
|
+
<id>3</id>
|
20
|
+
</item>
|
21
|
+
</items>
|
22
|
+
</response>
|
23
|
+
XML
|
24
|
+
|
25
|
+
setup do
|
26
|
+
@xml = RemoteAPI::XML::Response.new(XML)
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "[] should accept XPath" do
|
30
|
+
@xml['//title'].should_be == "Test Title"
|
31
|
+
end
|
32
|
+
|
33
|
+
specify "a bad XPath passed to [] should return nil" do
|
34
|
+
@xml['/foo/bar/baz'].should_be_nil
|
35
|
+
end
|
36
|
+
|
37
|
+
specify "'each' with a bad XPath should not run provided block" do
|
38
|
+
count = 0
|
39
|
+
@xml.each('/foo/bar/baz') { |node| count += 1 }
|
40
|
+
count.should_be_zero
|
41
|
+
end
|
42
|
+
|
43
|
+
specify "'each' should execute block for every matching XPath" do
|
44
|
+
expected_names = %w( Bob Joe Walter )
|
45
|
+
expected_ids = %w( 1 2 3 )
|
46
|
+
count = 0
|
47
|
+
|
48
|
+
@xml.each "//items/item" do |node|
|
49
|
+
count += 1
|
50
|
+
node['name'].should_be == expected_names.shift
|
51
|
+
node['id'].should_be == expected_ids.shift
|
52
|
+
end
|
53
|
+
|
54
|
+
count.should_be == 3
|
55
|
+
end
|
56
|
+
|
57
|
+
specify "calling 'each' without a block should raise an ArgumentError" do
|
58
|
+
proc {
|
59
|
+
@xml.each('//title')
|
60
|
+
}.should_raise ArgumentError
|
61
|
+
end
|
62
|
+
|
63
|
+
specify "'to_formatted_s' should make the xml pretty" do
|
64
|
+
input = '<a><b><c>foo</c></b></a>'
|
65
|
+
expected = <<XML
|
66
|
+
<a>
|
67
|
+
<b>
|
68
|
+
<c>foo</c>
|
69
|
+
</b>
|
70
|
+
</a>
|
71
|
+
XML
|
72
|
+
xml_response = RemoteAPI::XML::Response.new(input)
|
73
|
+
|
74
|
+
xml_response.to_formatted_s.should_be == expected.chomp
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
2
|
|
3
3
|
FakeWeb.register_uri(
|
4
4
|
'http://test.com/xml',
|
@@ -8,56 +8,54 @@ FakeWeb.register_uri(
|
|
8
8
|
'http://test.com/xml/error',
|
9
9
|
:string => '<server_response><error>Big fat error</error></server_response>'
|
10
10
|
)
|
11
|
+
|
12
|
+
class XmlApi < RemoteAPI::XML
|
13
|
+
attr_reader :return_message
|
14
|
+
|
15
|
+
def request
|
16
|
+
x = Builder::XmlMarkup.new
|
17
|
+
x.my_request @message
|
18
|
+
end
|
19
|
+
|
20
|
+
def assert_success
|
21
|
+
if error = @response['/server_response/error']
|
22
|
+
raise ResponseFailure, error
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def process
|
27
|
+
@return_message = @response['/foo/server_response']
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
11
31
|
|
12
|
-
|
32
|
+
context "An XML API" do
|
13
33
|
|
14
|
-
|
34
|
+
setup do
|
15
35
|
XmlApi.url = 'http://test.com/xml'
|
16
36
|
end
|
17
37
|
|
18
|
-
|
19
|
-
result = XmlApi.
|
20
|
-
|
38
|
+
specify "response should be XML" do
|
39
|
+
result = XmlApi.call(:message => 'foo')
|
40
|
+
result.return_message.should_be == 'The server recieved your message'
|
21
41
|
end
|
22
42
|
|
23
|
-
|
43
|
+
specify "'format_response' should produce pretty xml" do
|
24
44
|
expected = <<XML
|
25
45
|
<foo>
|
26
46
|
<server_response>The server recieved your message</server_response>
|
27
47
|
</foo>
|
28
48
|
XML
|
29
49
|
|
30
|
-
result = XmlApi.
|
31
|
-
data = result.send(:format_response, '<foo><server_response>The server recieved your message</server_response></foo>')
|
32
|
-
|
50
|
+
result = XmlApi.call(:message => 'foo')
|
51
|
+
data = result.send(:format_response, '<foo><server_response>The server recieved your message</server_response></foo>')
|
52
|
+
|
53
|
+
data.should_be == expected.chomp
|
33
54
|
end
|
34
55
|
|
35
|
-
|
56
|
+
specify "'assert_success' should raise an exception on failure" do
|
36
57
|
XmlApi.url = 'http://test.com/xml/error'
|
37
|
-
|
38
|
-
XmlApi.new(:message => 'foo')
|
39
|
-
end
|
58
|
+
proc { XmlApi.call(:message => 'foo') }.should_raise RemoteAPI::ResponseFailure
|
40
59
|
end
|
41
60
|
|
42
|
-
end
|
43
|
-
|
44
|
-
|
45
|
-
class XmlApi < RemoteAPI::XML
|
46
|
-
attr_reader :return_message
|
47
|
-
|
48
|
-
def request
|
49
|
-
x = Builder::XmlMarkup.new
|
50
|
-
x.my_request @message
|
51
|
-
end
|
52
|
-
|
53
|
-
def assert_success
|
54
|
-
if error = @response['/server_response/error']
|
55
|
-
raise ResponseFailure, error
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def process
|
60
|
-
@return_message = @response['/foo/server_response']
|
61
|
-
end
|
62
|
-
|
63
61
|
end
|
data/test/spec_test.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
class SpecTest < Test::Unit::TestCase
|
4
|
+
def test_true
|
5
|
+
assert true
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
puts <<SPEC
|
10
|
+
|
11
|
+
----------------------------------------------
|
12
|
+
This gem has no tests. Please run 'rake spec'
|
13
|
+
or 'rake spec_print' instead.
|
14
|
+
|
15
|
+
The rspec gem must be installed to run specs.
|
16
|
+
gem install rspec
|
17
|
+
----------------------------------------------
|
18
|
+
|
19
|
+
SPEC
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.1
|
|
3
3
|
specification_version: 1
|
4
4
|
name: remote_api
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2007-04-19 00:00:00 -07:00
|
8
8
|
summary: Provides a basic framework for easily creating classes that access remote APIs.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -39,14 +39,13 @@ files:
|
|
39
39
|
- lib/remote_api/xml.rb
|
40
40
|
- lib/remote_api/xml_response.rb
|
41
41
|
- lib/remote_api/version.rb
|
42
|
-
- test/
|
43
|
-
-
|
44
|
-
-
|
45
|
-
-
|
42
|
+
- test/spec_test.rb
|
43
|
+
- spec/base_spec.rb
|
44
|
+
- spec/spec_helper.rb
|
45
|
+
- spec/xml_response_spec.rb
|
46
|
+
- spec/xml_spec.rb
|
46
47
|
test_files:
|
47
|
-
- test/
|
48
|
-
- test/xml_response_test.rb
|
49
|
-
- test/xml_test.rb
|
48
|
+
- test/spec_test.rb
|
50
49
|
rdoc_options: []
|
51
50
|
|
52
51
|
extra_rdoc_files: []
|
data/test/base_test.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
-
|
3
|
-
FakeWeb.register_uri(
|
4
|
-
'http://test.com/base',
|
5
|
-
:string => '<server_response>The server recieved your message</server_response>'
|
6
|
-
)
|
7
|
-
FakeWeb.register_uri(
|
8
|
-
'http://test.com/base/error',
|
9
|
-
:string => '<server_response>An error occurred!</server_response>'
|
10
|
-
)
|
11
|
-
|
12
|
-
class RemoteApiTest < Test::Unit::TestCase
|
13
|
-
|
14
|
-
def setup
|
15
|
-
BaseApi.url = 'http://test.com/base'
|
16
|
-
end
|
17
|
-
|
18
|
-
def test_initialize_should_set_hash_keys_as_instance_variables
|
19
|
-
result = BaseApi.new(:foo => 'bar', :baz => 'taz', :message => 'Hello')
|
20
|
-
|
21
|
-
assert_equal('bar', result.instance_eval { @foo })
|
22
|
-
assert_equal('taz', result.instance_eval { @baz })
|
23
|
-
assert_equal('Hello', result.instance_eval { @message })
|
24
|
-
end
|
25
|
-
|
26
|
-
def test_initialize_should_perform_api_call
|
27
|
-
result = BaseApi.new(:message => 'Hello World!')
|
28
|
-
assert_equal 'The server recieved your message', result.return_message
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_assert_success_should_raise_exception
|
32
|
-
BaseApi.url = 'http://test.com/base/error'
|
33
|
-
assert_raise RemoteAPI::ResponseFailure do
|
34
|
-
BaseApi.new(:message => 'foo')
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
|
40
|
-
class BaseApi < RemoteAPI
|
41
|
-
attr_reader :return_message
|
42
|
-
|
43
|
-
def request
|
44
|
-
x = Builder::XmlMarkup.new
|
45
|
-
x.my_request @message
|
46
|
-
end
|
47
|
-
|
48
|
-
def assert_success
|
49
|
-
if @response =~ %r{<server_response>(.*error.*)</server_response>}
|
50
|
-
raise ResponseFailure, $1
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def process
|
55
|
-
@return_message = @response.gsub(%r{</?server_response>}, '')
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
data/test/xml_response_test.rb
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
-
|
3
|
-
class XmlResponseTest < Test::Unit::TestCase
|
4
|
-
|
5
|
-
XML = <<XML
|
6
|
-
<response>
|
7
|
-
<title>Test Title</title>
|
8
|
-
<items>
|
9
|
-
<item>
|
10
|
-
<name>Bob</name>
|
11
|
-
<id>1</id>
|
12
|
-
</item>
|
13
|
-
<item>
|
14
|
-
<name>Joe</name>
|
15
|
-
<id>2</id>
|
16
|
-
</item>
|
17
|
-
<item>
|
18
|
-
<name>Walter</name>
|
19
|
-
<id>3</id>
|
20
|
-
</item>
|
21
|
-
</items>
|
22
|
-
</response>
|
23
|
-
XML
|
24
|
-
|
25
|
-
def setup
|
26
|
-
@xml = RemoteAPI::XML::Response.new(XML)
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_brace_accessor
|
30
|
-
assert_equal('Test Title', @xml['//title'])
|
31
|
-
end
|
32
|
-
|
33
|
-
def test_bad_xpath_should_return_nil
|
34
|
-
assert_nil @xml['/foo/bar/baz']
|
35
|
-
end
|
36
|
-
|
37
|
-
def test_each_on_bad_xpath_should_not_run_block
|
38
|
-
count = 0
|
39
|
-
@xml.each('/foo/bar/baz') { |node| count += 1 }
|
40
|
-
assert_equal(0, count)
|
41
|
-
end
|
42
|
-
|
43
|
-
def test_each
|
44
|
-
expected_names = %w( Bob Joe Walter )
|
45
|
-
expected_ids = %w( 1 2 3 )
|
46
|
-
count = 0
|
47
|
-
|
48
|
-
@xml.each '//items/item' do |node|
|
49
|
-
count += 1
|
50
|
-
assert_equal(expected_names.shift, node['name'])
|
51
|
-
assert_equal(expected_ids.shift, node['id'])
|
52
|
-
end
|
53
|
-
|
54
|
-
assert_equal(3, count)
|
55
|
-
end
|
56
|
-
|
57
|
-
def test_each_without_block_should_raise_exception
|
58
|
-
assert_raise(ArgumentError) do
|
59
|
-
@xml.each('//title')
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def test_to_formatted_s
|
64
|
-
input = '<a><b><c>foo</c></b></a>'
|
65
|
-
expected = <<XML
|
66
|
-
<a>
|
67
|
-
<b>
|
68
|
-
<c>foo</c>
|
69
|
-
</b>
|
70
|
-
</a>
|
71
|
-
XML
|
72
|
-
xml_response = RemoteAPI::XML::Response.new(input)
|
73
|
-
|
74
|
-
assert_equal(expected.chomp, xml_response.to_formatted_s)
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|