libxml-xmlrpc 0.1.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/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:
|