remote_api 0.1.2 → 0.2.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/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
|