libxml-xmlrpc 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/xml/libxml/xmlrpc.rb +18 -0
- data/lib/xml/libxml/xmlrpc/builder.rb +228 -0
- data/lib/xml/libxml/xmlrpc/client.rb +44 -0
- data/lib/xml/libxml/xmlrpc/parser.rb +259 -0
- data/test/test_builder.rb +92 -0
- data/test/test_parser_good.rb +119 -0
- metadata +63 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'xml/libxml/xmlrpc/client'
|
2
|
+
require 'xml/libxml/xmlrpc/parser'
|
3
|
+
require 'xml/libxml/xmlrpc/builder'
|
4
|
+
|
5
|
+
module XML
|
6
|
+
#
|
7
|
+
# XML::XMLRPC -- LibXML interface to XML-RPC
|
8
|
+
#
|
9
|
+
# Right now, look at XML::XMLRPC::Parser, XML::XMLRPC::Builder
|
10
|
+
# and XML::XMLRPC::Client for docs
|
11
|
+
#
|
12
|
+
# Author:: Erik Hollensbe <erik@hollensbe.org>
|
13
|
+
#
|
14
|
+
|
15
|
+
module XMLRPC
|
16
|
+
VERSION = "0.1.0"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module XML
|
4
|
+
module XMLRPC
|
5
|
+
#
|
6
|
+
# Class to build XML-RPC responses and calls.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# XML::XMLRPC::Builder.foo(1,2,3) # generates xml for method call
|
11
|
+
# # 'foo' with arguments of int 1, 2
|
12
|
+
# # and 3
|
13
|
+
#
|
14
|
+
# XML::XMLRPC::Builder.response(1,2,3) # builds a response with args
|
15
|
+
# # 1,2,3
|
16
|
+
#
|
17
|
+
# # builds a fault response with faultCode 0 and faultString "Foo"
|
18
|
+
# XML::XMLRPC::Builder.fault_response(2, "Foo")
|
19
|
+
#
|
20
|
+
# # builds a call called 'fault_response'
|
21
|
+
# XML::XMLRPC::Builder.call('fault_response', 1, 2, 3)
|
22
|
+
#
|
23
|
+
# Notes:
|
24
|
+
# * To build a Base64 object, check out the XML::XMLRPC::Builder::Base64 class.
|
25
|
+
# * Date (and all other) objects must inherit directly from class
|
26
|
+
# Date or be the class themselves, DateTime is an example of direct
|
27
|
+
# inheritance of Date. Time (which inherits from Object) will NOT
|
28
|
+
# work.
|
29
|
+
# * All responses are encoded UTF-8. Be sure your strings, etc are
|
30
|
+
# UTF-8 before passing them into this module.
|
31
|
+
#
|
32
|
+
module Builder
|
33
|
+
#
|
34
|
+
# Builds the appropriate XML for a methodCall.
|
35
|
+
#
|
36
|
+
# Takes a methodname and a series of arguments.
|
37
|
+
#
|
38
|
+
def self.call(methodname, *args)
|
39
|
+
methodname = methodname.to_s
|
40
|
+
|
41
|
+
output = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
|
42
|
+
output += "<methodCall><methodName>#{methodname}</methodName>"
|
43
|
+
output += Value.generate(*args)
|
44
|
+
output += "</methodCall>"
|
45
|
+
|
46
|
+
return output
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Builds a response. Takes a series of response arguments.
|
51
|
+
#
|
52
|
+
def self.response(*args)
|
53
|
+
output = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
|
54
|
+
output += "<methodResponse>"
|
55
|
+
output += Value.generate(*args)
|
56
|
+
output += "</methodResponse>"
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# Builds a fault response. Takes a faultCode (integer) and a faultMessage (string).
|
62
|
+
#
|
63
|
+
def self.fault_response(faultCode, faultMessage)
|
64
|
+
output = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
|
65
|
+
output += "<methodResponse>"
|
66
|
+
output += "<fault><value><struct>"
|
67
|
+
output += "<member><name>faultCode</name><value><int>#{faultCode}</int></value></member>"
|
68
|
+
output += "<member><name>faultString</name><value><string>#{faultMessage}</string></value></member>"
|
69
|
+
output += "</struct></value></fault>"
|
70
|
+
output += "</methodResponse>"
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Just calls #call, your method name will be the first argument and
|
75
|
+
# will be passed to call properly.
|
76
|
+
#
|
77
|
+
def self.method_missing(*args)
|
78
|
+
self.call(*args)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# Thrown when Builder encounters an error.
|
84
|
+
#
|
85
|
+
class Builder::Error < Exception
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Base64 type class. The 'Base64' module that comes with ruby does not
|
90
|
+
# hold anything (it's not a class), and since our parser depends on
|
91
|
+
# having the appropriate type to generate the right value clause, this
|
92
|
+
# is required for Base64 transfers.
|
93
|
+
#
|
94
|
+
class Builder::Base64
|
95
|
+
|
96
|
+
#
|
97
|
+
# Takes a string.
|
98
|
+
#
|
99
|
+
def initialize(str)
|
100
|
+
@string = str
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Encodes the encapsulated string as Base64.
|
105
|
+
#
|
106
|
+
def encode
|
107
|
+
::Base64.encode64(@string)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Generates Values. This has several subclasses that map to
|
113
|
+
# core types which I will not document.
|
114
|
+
#
|
115
|
+
# RTFS.
|
116
|
+
#
|
117
|
+
|
118
|
+
module Builder::Value
|
119
|
+
|
120
|
+
def self.generate(*args)
|
121
|
+
output = "<params>"
|
122
|
+
args.each do |x|
|
123
|
+
output += "<param>"
|
124
|
+
output += self.generate_value(x)
|
125
|
+
output += "</param>"
|
126
|
+
end
|
127
|
+
output += "</params>"
|
128
|
+
|
129
|
+
return output
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.generate_value(arg)
|
133
|
+
output = "<value>"
|
134
|
+
|
135
|
+
# try the superclass if the class doesn't work (this isn't
|
136
|
+
# perfect but is better than nothing)
|
137
|
+
if arg.class == Builder::Base64
|
138
|
+
output += Base64.generate(arg)
|
139
|
+
elsif const_get(arg.class.to_s).respond_to? :generate
|
140
|
+
output += const_get(arg.class.to_s).generate(arg)
|
141
|
+
elsif const_get(arg.class.superclass.to_s).respond_to? :generate
|
142
|
+
output += const_get(arg.class.superclass.to_s).generate(arg)
|
143
|
+
else
|
144
|
+
raise Builder::Error, "Type '#{arg.class}' is not supported by XML-RPC"
|
145
|
+
end
|
146
|
+
|
147
|
+
output += "</value>"
|
148
|
+
|
149
|
+
return output
|
150
|
+
end
|
151
|
+
|
152
|
+
module Base64
|
153
|
+
def self.generate(arg)
|
154
|
+
"<base64>#{arg.encode}</base64>"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
module Fixnum
|
159
|
+
def self.generate(arg)
|
160
|
+
"<int>#{arg}</int>"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
module String
|
165
|
+
def self.generate(arg)
|
166
|
+
"<string>#{arg}</string>"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
module Float
|
171
|
+
def self.generate(arg)
|
172
|
+
"<double>#{arg}</double>"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
module Date
|
177
|
+
def self.generate(arg)
|
178
|
+
"<dateTime.iso8601>" + arg.strftime("%Y%m%dT%T") + "</dateTime.iso8601>"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
module Array
|
183
|
+
def self.generate(args)
|
184
|
+
output = "<array><data>"
|
185
|
+
args.each do |x|
|
186
|
+
output += Builder::Value.generate_value(x)
|
187
|
+
end
|
188
|
+
output += "</data></array>"
|
189
|
+
|
190
|
+
return output
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
module Hash
|
195
|
+
def self.generate(args)
|
196
|
+
output = "<struct>"
|
197
|
+
args.each_key do |key|
|
198
|
+
output += "<member>"
|
199
|
+
output += "<name>#{key}</name>"
|
200
|
+
output += Builder::Value.generate_value(args[key])
|
201
|
+
output += "</member>"
|
202
|
+
end
|
203
|
+
output += "</struct>"
|
204
|
+
return output
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
module TrueClass
|
209
|
+
def self.generate(arg)
|
210
|
+
"<boolean>1</boolean>"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
module FalseClass
|
215
|
+
def self.generate(arg)
|
216
|
+
"<boolean>0</boolean>"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
module NilClass
|
221
|
+
def self.generate(arg)
|
222
|
+
# nil is treated as false in our spec
|
223
|
+
FalseClass.generate(arg)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'xml/libxml/xmlrpc/parser'
|
2
|
+
require 'xml/libxml/xmlrpc/builder'
|
3
|
+
|
4
|
+
module XML
|
5
|
+
module XMLRPC
|
6
|
+
#
|
7
|
+
# Client is an easy-to-use XML-RPC method call and response mechanism.
|
8
|
+
#
|
9
|
+
# It will not handle redirection.
|
10
|
+
#
|
11
|
+
class Client
|
12
|
+
#
|
13
|
+
# Given an unused Net::HTTP object and a relative URL, it will post
|
14
|
+
# the XML-RPC information to this form after calling a method with
|
15
|
+
# ruby types.
|
16
|
+
#
|
17
|
+
# See XML::XMLRPC::Builder for caveats related to Base64 handling.
|
18
|
+
#
|
19
|
+
def initialize(http, url)
|
20
|
+
@http = http
|
21
|
+
@url = url
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# See #call.
|
26
|
+
#
|
27
|
+
def method_missing(*args)
|
28
|
+
self.call(*args)
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Call and recieve the response. Returns a XML::XMLRPC::Parser object.
|
33
|
+
#
|
34
|
+
# Will throw an XML::XMLRPC::RemoteCallError if the call returns a
|
35
|
+
# fault response.
|
36
|
+
#
|
37
|
+
def call(methodName, *args)
|
38
|
+
res = @http.post(@url, XML::XMLRPC::Builder.call(methodName, *args))
|
39
|
+
res_args = XML::XMLRPC::Parser.new(res.body)
|
40
|
+
return res_args
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require 'xml/libxml'
|
2
|
+
require 'base64'
|
3
|
+
require 'date'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
module XML::XMLRPC
|
7
|
+
class RemoteCallError < Exception
|
8
|
+
end
|
9
|
+
class ParserError < Exception
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# Spec-compliant XML-RPC parser. Converts XML-RPC types to native Ruby
|
14
|
+
# types.
|
15
|
+
#
|
16
|
+
# _Overview_:
|
17
|
+
# <?xml version="1.0"?>
|
18
|
+
# <methodResponse>
|
19
|
+
# <params>
|
20
|
+
# <param>
|
21
|
+
# <value><string>South Dakota</string></value>
|
22
|
+
# </param>
|
23
|
+
# </params>
|
24
|
+
# </methodResponse>
|
25
|
+
#
|
26
|
+
# xml = XML::XMLRPC::Parser.new(IO or String object)
|
27
|
+
# xml[0] == "South Dakota"
|
28
|
+
#
|
29
|
+
# Notes:
|
30
|
+
# * Structs and Arrays are Hashes and Arrays respectively.
|
31
|
+
# * Base64 is auto-decoded.
|
32
|
+
# * Any interpreter-level (as opposed to syntax-level or exception
|
33
|
+
# handling) crash you see in ruby is the fault of libxml, not this code.
|
34
|
+
# * In a case where you're parsing a methodCall request, the method
|
35
|
+
# attribute will have data. In the case where you parse a response, method
|
36
|
+
# will be nil.
|
37
|
+
#
|
38
|
+
class Parser
|
39
|
+
|
40
|
+
include Enumerable
|
41
|
+
|
42
|
+
attr_reader :params
|
43
|
+
attr_reader :method
|
44
|
+
|
45
|
+
#
|
46
|
+
# Takes a String or IO object, which contains a response or call.
|
47
|
+
#
|
48
|
+
# Parses the document immediately.
|
49
|
+
#
|
50
|
+
def initialize(io)
|
51
|
+
@string = ""
|
52
|
+
|
53
|
+
if io.kind_of? String
|
54
|
+
@string = io
|
55
|
+
elsif io.kind_of? IO or io.kind_of? StringIO # stupid StringIO
|
56
|
+
@string = io.read
|
57
|
+
else
|
58
|
+
raise ParserError, "Argument to new must be String or IO"
|
59
|
+
end
|
60
|
+
|
61
|
+
@params = []
|
62
|
+
@method = nil
|
63
|
+
self.parse!
|
64
|
+
|
65
|
+
@params.freeze
|
66
|
+
@method.freeze
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Parses the document. Should not be required -- Parser#new already
|
71
|
+
# does this for you.
|
72
|
+
#
|
73
|
+
def parse!
|
74
|
+
document = XML::Parser.string(@string).parse
|
75
|
+
node = document.root
|
76
|
+
case node.name
|
77
|
+
when "methodCall"
|
78
|
+
@method, @params = Parser::Call.parse(node)
|
79
|
+
when "methodResponse"
|
80
|
+
@params = Parser::Response.parse(node)
|
81
|
+
else
|
82
|
+
raise ParserError, "XMLRPC is invalid - no call or response"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# Obtain param x, where x is an integer.
|
88
|
+
#
|
89
|
+
# Same as:
|
90
|
+
#
|
91
|
+
# xml.params[x]
|
92
|
+
#
|
93
|
+
def [](x)
|
94
|
+
@params[x]
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# Iterate over each parameter.
|
99
|
+
#
|
100
|
+
|
101
|
+
def each
|
102
|
+
@params.each do |x|
|
103
|
+
yield x
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Parse a methodCall.
|
110
|
+
#
|
111
|
+
|
112
|
+
module Parser::Call
|
113
|
+
def self.parse(node)
|
114
|
+
method = node.find('/methodCall/methodName')
|
115
|
+
methodname = "unknown"
|
116
|
+
if method and method[0]
|
117
|
+
content = method[0].content
|
118
|
+
methodname = content
|
119
|
+
end
|
120
|
+
|
121
|
+
values = node.find('/methodCall/params/param/value')
|
122
|
+
|
123
|
+
parsed_params = Parser::ValueParser.parse(values)
|
124
|
+
|
125
|
+
return methodname, parsed_params
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Parse a methodResponse
|
131
|
+
#
|
132
|
+
|
133
|
+
class Parser::Response
|
134
|
+
def self.parse(node)
|
135
|
+
node.each_child do |child_node|
|
136
|
+
case child_node.name.downcase
|
137
|
+
when 'fault'
|
138
|
+
# RPC call has returned an error - find the fault and GTFO
|
139
|
+
value = Parser::ValueParser.parse(node.find('/methodResponse/fault/value'))
|
140
|
+
raise RemoteCallError, value[0][:faultCode].to_s + ": " + value[0][:faultString]
|
141
|
+
when 'params'
|
142
|
+
return Parser::ValueParser.parse(node.find('/methodResponse/params/param/value'))
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
#
|
149
|
+
# Parse values from a XML::Node::Set or Array of XML::Node objects.
|
150
|
+
#
|
151
|
+
# May be called recursively by ValueParser::Array or ValueParser::Struct.
|
152
|
+
#
|
153
|
+
|
154
|
+
module Parser::ValueParser
|
155
|
+
def self.parse(values)
|
156
|
+
parsed_params = []
|
157
|
+
|
158
|
+
values.each do |param_value_node|
|
159
|
+
value = nil
|
160
|
+
if param_value_node
|
161
|
+
param_value_node.each_child do |type|
|
162
|
+
value = case type.name.downcase
|
163
|
+
when /^(?:i4|int)$/
|
164
|
+
type.content.strip.to_i
|
165
|
+
when 'string'
|
166
|
+
type.content
|
167
|
+
when 'boolean'
|
168
|
+
if type.content.strip.to_i == 1
|
169
|
+
true
|
170
|
+
else
|
171
|
+
false
|
172
|
+
end
|
173
|
+
when 'double'
|
174
|
+
type.content.strip.to_f
|
175
|
+
when 'datetime.iso8601'
|
176
|
+
# Looks like this: 19980717T14:08:55
|
177
|
+
DateTime.strptime(type.content.strip, "%Y%m%dT%T")
|
178
|
+
when 'base64'
|
179
|
+
Base64.decode64(type.content.strip)
|
180
|
+
when 'struct'
|
181
|
+
Parser::ValueParser::Struct.parse(type)
|
182
|
+
when 'array'
|
183
|
+
Parser::ValueParser::Array.parse(type)
|
184
|
+
end
|
185
|
+
|
186
|
+
break if value
|
187
|
+
end
|
188
|
+
end
|
189
|
+
parsed_params.push value
|
190
|
+
end
|
191
|
+
|
192
|
+
return parsed_params
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
#
|
197
|
+
# Parse a 'struct' type.
|
198
|
+
#
|
199
|
+
|
200
|
+
module Parser::ValueParser::Struct
|
201
|
+
def self.parse(node)
|
202
|
+
# convert it to a hash with symbols as keys first.
|
203
|
+
|
204
|
+
hash = { }
|
205
|
+
|
206
|
+
node.each_child do |child_node|
|
207
|
+
if child_node.name == "member"
|
208
|
+
name = nil
|
209
|
+
value = nil
|
210
|
+
|
211
|
+
child_node.each_child do |member_node|
|
212
|
+
if member_node.name == "name"
|
213
|
+
name = member_node.content.strip.to_sym
|
214
|
+
if value
|
215
|
+
hash[name] = value
|
216
|
+
break
|
217
|
+
end
|
218
|
+
elsif member_node.name == "value"
|
219
|
+
value = Parser::ValueParser.parse([member_node])[0]
|
220
|
+
if name
|
221
|
+
hash[name] = value
|
222
|
+
break
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
return hash
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
#
|
234
|
+
# Parse an 'array' type.
|
235
|
+
#
|
236
|
+
|
237
|
+
module Parser::ValueParser::Array
|
238
|
+
def self.parse(node)
|
239
|
+
|
240
|
+
value = []
|
241
|
+
|
242
|
+
node.each_child do |child_node|
|
243
|
+
if child_node.name == "data"
|
244
|
+
value_nodes = []
|
245
|
+
child_node.each_child do |value_node|
|
246
|
+
if value_node.name == "value"
|
247
|
+
value_nodes.push value_node
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
value = Parser::ValueParser.parse(value_nodes)
|
252
|
+
break # yes, first hit is last hit, so says the stay-puff't spec.
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
return value
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end # fin.
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'xml/libxml/xmlrpc/builder'
|
3
|
+
require 'xml/libxml/xmlrpc/parser'
|
4
|
+
|
5
|
+
class TestBuilder < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@class = XML::XMLRPC::Builder
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_call
|
12
|
+
# ugh. these sure are ugly.
|
13
|
+
assert_equal(@class.foo,
|
14
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodCall><methodName>foo</methodName><params></params></methodCall>")
|
15
|
+
assert_equal(@class.bar,
|
16
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodCall><methodName>bar</methodName><params></params></methodCall>")
|
17
|
+
assert_equal(@class.foo(1),
|
18
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodCall><methodName>foo</methodName><params><param><value><int>1</int></value></param></params></methodCall>")
|
19
|
+
assert_equal(@class.foo(1,2,3),
|
20
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodCall><methodName>foo</methodName><params><param><value><int>1</int></value></param><param><value><int>2</int></value></param><param><value><int>3</int></value></param></params></methodCall>")
|
21
|
+
assert_equal(@class.foo(nil),
|
22
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodCall><methodName>foo</methodName><params><param><value><boolean>0</boolean></value></param></params></methodCall>")
|
23
|
+
assert_equal(@class.foo(Date.civil(1978, 4, 6)),
|
24
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodCall><methodName>foo</methodName><params><param><value><dateTime.iso8601>19780406T00:00:00</dateTime.iso8601></value></param></params></methodCall>")
|
25
|
+
assert_equal(@class.foo([1,2,3]),
|
26
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodCall><methodName>foo</methodName><params><param><value><array><data><value><int>1</int></value><value><int>2</int></value><value><int>3</int></value></data></array></value></param></params></methodCall>")
|
27
|
+
assert_equal(@class.foo({:Bar => 1, :Foo => 2}),
|
28
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodCall><methodName>foo</methodName><params><param><value><struct><member><name>Bar</name><value><int>1</int></value></member><member><name>Foo</name><value><int>2</int></value></member></struct></value></param></params></methodCall>")
|
29
|
+
assert_equal(@class.foo(1, [1,2,3], {:Bar => 1, :Foo => 2}),
|
30
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodCall><methodName>foo</methodName><params><param><value><int>1</int></value></param><param><value><array><data><value><int>1</int></value><value><int>2</int></value><value><int>3</int></value></data></array></value></param><param><value><struct><member><name>Bar</name><value><int>1</int></value></member><member><name>Foo</name><value><int>2</int></value></member></struct></value></param></params></methodCall>")
|
31
|
+
assert_equal(@class.foo(XML::XMLRPC::Builder::Base64.new("foo")),
|
32
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodCall><methodName>foo</methodName><params><param><value><base64>Zm9v\n</base64></value></param></params></methodCall>")
|
33
|
+
|
34
|
+
# parse test. Our parser should be able to parse what we generate.
|
35
|
+
|
36
|
+
xml = XML::XMLRPC::Parser.new(@class.foo([1,2,3], { :Bar => 1, :Foo => 2 }, XML::XMLRPC::Builder::Base64.new("foo")))
|
37
|
+
assert_equal(xml[0], [1,2,3])
|
38
|
+
assert_equal(xml[1], { :Bar => 1, :Foo => 2})
|
39
|
+
assert_equal(xml[2], "foo")
|
40
|
+
assert_equal("foo", xml.method)
|
41
|
+
|
42
|
+
# response/fault call test
|
43
|
+
|
44
|
+
xml = XML::XMLRPC::Parser.new(@class.call('response', 1,2,3))
|
45
|
+
assert_equal(xml[0], 1)
|
46
|
+
assert_equal(xml[1], 2)
|
47
|
+
assert_equal(xml[2], 3)
|
48
|
+
assert_equal("response", xml.method)
|
49
|
+
|
50
|
+
|
51
|
+
xml = XML::XMLRPC::Parser.new(@class.call('fault_response', 1,2,3))
|
52
|
+
assert_equal(xml[0], 1)
|
53
|
+
assert_equal(xml[1], 2)
|
54
|
+
assert_equal(xml[2], 3)
|
55
|
+
assert_equal("fault_response", xml.method)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_response
|
59
|
+
assert_equal(@class.response,
|
60
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodResponse><params></params></methodResponse>")
|
61
|
+
assert_equal(@class.response(nil),
|
62
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodResponse><params><param><value><boolean>0</boolean></value></param></params></methodResponse>")
|
63
|
+
assert_equal(@class.fault_response(0, "foo"),
|
64
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<methodResponse><fault><value><struct><member><name>faultCode</name><value><int>0</int></value></member><member><name>faultString</name><value><string>foo</string></value></member></struct></value></fault></methodResponse>")
|
65
|
+
|
66
|
+
# parser test
|
67
|
+
|
68
|
+
xml = XML::XMLRPC::Parser.new(@class.response(1,2,3))
|
69
|
+
assert_equal(xml[0], 1)
|
70
|
+
assert_equal(xml[1], 2)
|
71
|
+
assert_equal(xml[2], 3)
|
72
|
+
assert_equal(xml.method, nil)
|
73
|
+
|
74
|
+
# test faults
|
75
|
+
assert_raise XML::XMLRPC::RemoteCallError do
|
76
|
+
xml = XML::XMLRPC::Parser.new(@class.fault_response(0, "Foo"))
|
77
|
+
end
|
78
|
+
|
79
|
+
e = nil
|
80
|
+
|
81
|
+
assert_nothing_raised do
|
82
|
+
begin
|
83
|
+
xml = XML::XMLRPC::Parser.new(@class.fault_response(0, "Foo"))
|
84
|
+
rescue XML::XMLRPC::RemoteCallError => error
|
85
|
+
e = error
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
assert_instance_of(XML::XMLRPC::RemoteCallError, e)
|
90
|
+
assert_equal(e.message, "0: Foo")
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'xml/libxml/xmlrpc/parser'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
class TestParserGood < Test::Unit::TestCase
|
6
|
+
def test_constructor
|
7
|
+
assert_raise XML::XMLRPC::ParserError do
|
8
|
+
XML::XMLRPC::Parser.new(nil)
|
9
|
+
end
|
10
|
+
|
11
|
+
assert_nothing_raised do
|
12
|
+
XML::XMLRPC::Parser.new(File.open("test/data/big_call.xml"))
|
13
|
+
end
|
14
|
+
|
15
|
+
# should raise a LibXML error because there's nothing to parse
|
16
|
+
# XXX libxml no likey empty string.
|
17
|
+
assert_raise XML::Parser::ParseError do
|
18
|
+
XML::XMLRPC::Parser.new("asdf")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_datatypes
|
23
|
+
xml = nil
|
24
|
+
|
25
|
+
assert_nothing_raised do
|
26
|
+
xml = XML::XMLRPC::Parser.new(File.open("test/data/big_call.xml"))
|
27
|
+
end
|
28
|
+
|
29
|
+
# primitives
|
30
|
+
|
31
|
+
assert_equal(41, xml[0])
|
32
|
+
assert_equal("41", xml[1])
|
33
|
+
assert_equal(true, xml[2])
|
34
|
+
assert_equal(false, xml[3])
|
35
|
+
assert_equal(1.23, xml[4])
|
36
|
+
|
37
|
+
# DateTime
|
38
|
+
assert_kind_of(DateTime, xml[5])
|
39
|
+
assert_equal("1998-07-17T14:08:55+00:00", xml[5].to_s)
|
40
|
+
|
41
|
+
assert_equal("monkeys", xml[6]) # base64
|
42
|
+
|
43
|
+
assert_kind_of(Array, xml[7])
|
44
|
+
assert_equal([xml[6], xml[5]], xml[7])
|
45
|
+
|
46
|
+
assert_kind_of(Hash, xml[8])
|
47
|
+
assert_equal({ :Monkeys => [xml[6], xml[5]], :Poo => xml[5]}, xml[8])
|
48
|
+
|
49
|
+
assert_equal("examples.getStateName", xml.method)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_response
|
53
|
+
xml = nil
|
54
|
+
|
55
|
+
assert_raise XML::XMLRPC::RemoteCallError do
|
56
|
+
xml = XML::XMLRPC::Parser.new(File.open("test/data/fail_response.xml"))
|
57
|
+
end
|
58
|
+
|
59
|
+
# ensure the failure message is getting sent properly
|
60
|
+
e = nil
|
61
|
+
begin
|
62
|
+
xml = XML::XMLRPC::Parser.new(File.open("test/data/fail_response.xml"))
|
63
|
+
rescue XML::XMLRPC::RemoteCallError => error
|
64
|
+
e = error
|
65
|
+
end
|
66
|
+
|
67
|
+
assert_equal("4: Too many parameters.", e.message)
|
68
|
+
|
69
|
+
assert_nothing_raised do
|
70
|
+
xml = XML::XMLRPC::Parser.new(File.open("test/data/good_response.xml"))
|
71
|
+
end
|
72
|
+
|
73
|
+
assert_equal("South Dakota", xml[0])
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_functionality
|
77
|
+
xml = nil
|
78
|
+
assert_nothing_raised do
|
79
|
+
xml = XML::XMLRPC::Parser.new(File.open("test/data/big_call.xml"))
|
80
|
+
end
|
81
|
+
|
82
|
+
# Enumerable functionality
|
83
|
+
xml.each_with_index do |x, i|
|
84
|
+
case i
|
85
|
+
when 0
|
86
|
+
assert_equal(41, x)
|
87
|
+
when 1
|
88
|
+
assert_equal("41", x)
|
89
|
+
when 2
|
90
|
+
assert_equal(true, x)
|
91
|
+
when 3
|
92
|
+
assert_equal(false, x)
|
93
|
+
when 4
|
94
|
+
assert_equal(1.23, x)
|
95
|
+
end
|
96
|
+
# that should be plenty.
|
97
|
+
end
|
98
|
+
|
99
|
+
assert(xml.include?(41))
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_recursion
|
103
|
+
xml = nil
|
104
|
+
assert_nothing_raised do
|
105
|
+
xml = XML::XMLRPC::Parser.new(File.open("test/data/recursion_test.xml"))
|
106
|
+
end
|
107
|
+
|
108
|
+
assert_equal(
|
109
|
+
{
|
110
|
+
:Poo => [{
|
111
|
+
:Poo => ["monkeys", "19980717T14:08:55"],
|
112
|
+
:Monkeys => ["monkeys", "19980717T14:08:55"]
|
113
|
+
}],
|
114
|
+
:Monkeys => ["monkeys", "19980717T14:08:55"]
|
115
|
+
},
|
116
|
+
xml[0]
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: libxml-xmlrpc
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2007-10-23 00:00:00 -07:00
|
8
|
+
summary: Provides a alternative and faster XML-RPC layer through libxml's parsing framework
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: erik@hollensbe.org
|
12
|
+
homepage:
|
13
|
+
rubyforge_project: libxml-tools
|
14
|
+
description:
|
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
|
+
- Erik Hollensbe
|
31
|
+
files:
|
32
|
+
- lib/xml
|
33
|
+
- lib/xml/libxml
|
34
|
+
- lib/xml/libxml/xmlrpc.rb
|
35
|
+
- lib/xml/libxml/xmlrpc
|
36
|
+
- lib/xml/libxml/xmlrpc/client.rb
|
37
|
+
- lib/xml/libxml/xmlrpc/parser.rb
|
38
|
+
- lib/xml/libxml/xmlrpc/builder.rb
|
39
|
+
- test/test_parser_good.rb
|
40
|
+
- test/data
|
41
|
+
- test/test_builder.rb
|
42
|
+
test_files: []
|
43
|
+
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
extra_rdoc_files: []
|
47
|
+
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
requirements: []
|
53
|
+
|
54
|
+
dependencies:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: libxml-ruby
|
57
|
+
version_requirement:
|
58
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 0.0.0
|
63
|
+
version:
|