rapuncel 0.0.1 → 0.0.2
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/README.md +39 -33
- data/{Rakefile.rb → Rakefile} +1 -1
- data/lib/rapuncel.rb +2 -1
- data/lib/rapuncel/adapters/net_http_adapter.rb +6 -6
- data/lib/rapuncel/base.rb +2 -2
- data/lib/rapuncel/client.rb +21 -13
- data/lib/rapuncel/connection.rb +2 -2
- data/lib/rapuncel/core_ext/array.rb +9 -16
- data/lib/rapuncel/core_ext/big_decimal.rb +7 -0
- data/lib/rapuncel/core_ext/boolean.rb +10 -13
- data/lib/rapuncel/core_ext/float.rb +4 -7
- data/lib/rapuncel/core_ext/hash.rb +17 -27
- data/lib/rapuncel/core_ext/integer.rb +2 -10
- data/lib/rapuncel/core_ext/nil.rb +7 -0
- data/lib/rapuncel/core_ext/object.rb +6 -6
- data/lib/rapuncel/core_ext/string.rb +5 -7
- data/lib/rapuncel/core_ext/symbol.rb +2 -3
- data/lib/rapuncel/core_ext/time.rb +5 -8
- data/lib/rapuncel/proxy.rb +39 -14
- data/lib/rapuncel/request.rb +7 -6
- data/lib/rapuncel/response.rb +60 -31
- data/rapuncel.gemspec +2 -3
- data/test/functional/client_test.rb +38 -7
- data/test/functional_test_helper.rb +2 -2
- data/test/test_server.rb +3 -3
- data/test/unit/array_test.rb +14 -14
- data/test/unit/boolean_test.rb +6 -6
- data/test/unit/float_test.rb +7 -7
- data/test/unit/hash_test.rb +16 -16
- data/test/unit/nil_test.rb +16 -0
- data/test/unit/object_test.rb +29 -29
- data/test/unit/proxy_test.rb +9 -9
- data/test/unit/response_test.rb +69 -5
- data/test/unit/string_test.rb +15 -7
- metadata +13 -53
- data/lib/rapuncel/adapters/typhoeus_adapter.rb +0 -33
- data/lib/rapuncel/fault.rb +0 -10
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-adapters-typhoeus_adapter_rb.html +0 -231
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-base_rb.html +0 -105
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-client_rb.html +0 -321
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-connection_rb.html +0 -513
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-array_rb.html +0 -303
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-boolean_rb.html +0 -255
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-float_rb.html +0 -177
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-hash_rb.html +0 -297
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-integer_rb.html +0 -177
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-object_rb.html +0 -297
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-string_rb.html +0 -159
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-time_rb.html +0 -165
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-fault_rb.html +0 -93
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-proxy_rb.html +0 -393
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-request_rb.html +0 -351
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-response_rb.html +0 -399
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel_rb.html +0 -201
- data/test/coverage/functional_test_helper_rb.html +0 -141
- data/test/coverage/index.html +0 -410
- data/test/coverage/jquery-1.3.2.min.js +0 -19
- data/test/coverage/jquery.tablesorter.min.js +0 -15
- data/test/coverage/print.css +0 -12
- data/test/coverage/rcov.js +0 -42
- data/test/coverage/screen.css +0 -270
- data/test/coverage/test_helper_rb.html +0 -291
- data/test/coverage/test_server_rb.html +0 -231
data/README.md
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# Rapuncel - Simple XML-RPC Client
|
2
2
|
|
3
|
-
Rapuncel ([wikipedia](http://en.wikipedia.org/wiki/Rapunzel)) is a simple and lightweight, but fast XML-RPC client library for ruby.
|
4
|
-
It's based on Nokogiri for XML parsing and thus provides a major performance improvement for large XML responses.
|
3
|
+
Rapuncel ([wikipedia](http://en.wikipedia.org/wiki/Rapunzel)) is a simple and lightweight, but fast XML-RPC client library for ruby.
|
4
|
+
It's based on Nokogiri for XML parsing and thus provides a major performance improvement for large XML responses.
|
5
|
+
|
6
|
+
## Alpha release!
|
7
|
+
Not everything is working yet, especially: SSL, HTTP Basic Auth, ApiKey-Auth. If in doubt, have a look at the functionality being auto-tested.
|
5
8
|
|
6
9
|
## Installation
|
7
10
|
|
@@ -9,18 +12,18 @@ It's based on Nokogiri for XML parsing and thus provides a major performance imp
|
|
9
12
|
Add this to your Gemfile:
|
10
13
|
|
11
14
|
gem 'rapuncel'
|
12
|
-
|
13
|
-
Run
|
15
|
+
|
16
|
+
Run
|
14
17
|
|
15
18
|
bundle install
|
16
|
-
|
17
|
-
and you're good to go.
|
18
|
-
|
19
|
+
|
20
|
+
and you're good to go.
|
21
|
+
|
19
22
|
### Other Ruby / IRB
|
20
23
|
Install it as gem:
|
21
24
|
|
22
25
|
gem install rapuncel
|
23
|
-
|
26
|
+
|
24
27
|
Require **rubygems** and **rapuncel**
|
25
28
|
|
26
29
|
require 'rubygems'
|
@@ -34,47 +37,50 @@ object.
|
|
34
37
|
First you have to create a client with the connection details, e.g.
|
35
38
|
|
36
39
|
client = Rapuncel::Client.new :host => 'localhost', :port => 8080, :path => '/xmlrpc'
|
37
|
-
|
40
|
+
|
38
41
|
Available options are:
|
39
42
|
|
40
43
|
* **host**
|
41
|
-
hostname or ip-address,
|
44
|
+
hostname or ip-address,
|
42
45
|
_default_: localhost
|
43
46
|
* **port**
|
44
|
-
port where your XMLRPC service is listening,
|
47
|
+
port where your XMLRPC service is listening,
|
45
48
|
_default_: 8080
|
46
49
|
* **path**
|
47
|
-
path to the service,
|
50
|
+
path to the service,
|
48
51
|
_default_: /
|
49
52
|
* **user**
|
50
|
-
Username for HTTP Authentication
|
53
|
+
Username for HTTP Authentication
|
51
54
|
_default_: _empty_
|
52
55
|
* **password**
|
53
|
-
Username for HTTP Authentication
|
56
|
+
Username for HTTP Authentication
|
54
57
|
_default_: _empty_
|
55
58
|
* **auth\_method**
|
56
|
-
HTTP Auth method
|
59
|
+
HTTP Auth method
|
57
60
|
_default_: basic **IF** user or password is set
|
58
61
|
* **api\_key**
|
59
62
|
If set, sends all request with a X-ApiKey: _api\_key_ header
|
60
63
|
* **api\_key\_header**
|
61
|
-
Allows you to modify the header key for API-Key auth
|
64
|
+
Allows you to modify the header key for API-Key auth
|
62
65
|
_default_: X-ApiKey
|
66
|
+
* **raise_on**
|
67
|
+
Lets you define the behavior on errors or faults, if set to _:fault_, _:error_ or _:both_,
|
68
|
+
an Exception will be raised if something goes wrong
|
63
69
|
|
64
|
-
### Get a proxy object and ...
|
70
|
+
### Get a proxy object and ...
|
65
71
|
A proxy object receives ruby method calls, redirects them to your XMLRPC service and returns the response as ruby objects!
|
66
|
-
|
72
|
+
|
67
73
|
proxy = client.proxy
|
68
|
-
|
74
|
+
|
69
75
|
# suppose your XMLRPC service has a method exposed 'concat_string(string1, string2)'
|
70
76
|
proxy.concat_string "foo", "bar"
|
71
77
|
-> "foobar"
|
72
|
-
|
78
|
+
|
73
79
|
# if you need to access specific interfaces on your service, e.g. 'string.concat(string1, string2)'
|
74
80
|
proxy = client.proxy_for 'string'
|
75
81
|
proxy.concat 'foo', 'bar'
|
76
82
|
-> 'foobar'
|
77
|
-
|
83
|
+
|
78
84
|
## Supported objects
|
79
85
|
Rapuncel supports natively following object-types (and all their subclasses):
|
80
86
|
|
@@ -84,6 +90,7 @@ Rapuncel supports natively following object-types (and all their subclasses):
|
|
84
90
|
* Hash
|
85
91
|
* TrueClass, FalseClass
|
86
92
|
* Float
|
93
|
+
* BigDecimal (treated like Float)
|
87
94
|
* Time, Time-like objects
|
88
95
|
|
89
96
|
* Symbols are converted to Strings
|
@@ -107,34 +114,33 @@ Of course you don't have to delegate to #to\_s, you just can use the Builder obj
|
|
107
114
|
You can use most methods via
|
108
115
|
|
109
116
|
proxy.methodname *args
|
110
|
-
|
117
|
+
|
111
118
|
However methods starting with \_\_, or ending with a bang \! or a question mark ? are not supported. To call those methods you can always
|
112
119
|
use
|
113
120
|
|
114
121
|
proxy.call! 'methodname', *args
|
115
|
-
|
122
|
+
|
116
123
|
or via
|
117
124
|
|
118
125
|
client.call_to_ruby 'methodname', *args
|
119
|
-
|
120
|
-
note
|
121
126
|
|
122
|
-
|
123
|
-
|
127
|
+
note
|
128
|
+
|
129
|
+
client.call 'methodname', *args
|
130
|
+
|
124
131
|
will return a Rapuncel::Response object, use _call\_to\_ruby_ to get standard ruby objects
|
125
132
|
|
126
133
|
## Todo ?
|
127
134
|
|
128
|
-
* Base64 support (or rather a consistent concept for Base64)
|
129
|
-
* XMLRPC Extensions (pluggable support)
|
130
135
|
* RDoc
|
131
136
|
* Extensive functional tests
|
137
|
+
* Async requests
|
138
|
+
* Base64 support (or rather a consistent concept for Base64)
|
139
|
+
* XMLRPC Extensions (pluggable support)
|
132
140
|
|
133
141
|
## What happens if something goes wrong?
|
134
|
-
### HTTP Errors
|
135
|
-
|
136
|
-
### XMLRPC Faults
|
137
|
-
If the XMLRPC response is 'fault', a Rapuncel::Fault object will be returned, having a _code_ and a _string_ attribute
|
142
|
+
### HTTP Errors / XMLRPC Faults
|
143
|
+
See Usage -> configuration -> raise\_on switch
|
138
144
|
### Malformed XML/XMLRPC
|
139
145
|
Rapuncel will most likely fail hard.
|
140
146
|
|
data/{Rakefile.rb → Rakefile}
RENAMED
@@ -19,6 +19,6 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
19
19
|
rdoc.rdoc_dir = 'rdoc'
|
20
20
|
rdoc.title = 'Rapuncel'
|
21
21
|
rdoc.options << '--line-numbers' << '--inline-source'
|
22
|
-
rdoc.rdoc_files.include('README')
|
22
|
+
rdoc.rdoc_files.include('README.md')
|
23
23
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
24
24
|
end
|
data/lib/rapuncel.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
|
3
3
|
require 'active_support/core_ext/hash/keys'
|
4
4
|
|
@@ -20,5 +20,6 @@ require 'rapuncel/core_ext/float'
|
|
20
20
|
require 'rapuncel/core_ext/hash'
|
21
21
|
require 'rapuncel/core_ext/array'
|
22
22
|
require 'rapuncel/core_ext/boolean'
|
23
|
+
require 'rapuncel/core_ext/nil'
|
23
24
|
|
24
25
|
require 'rapuncel/core_ext/time'
|
@@ -5,28 +5,28 @@ module Rapuncel
|
|
5
5
|
module Adapters
|
6
6
|
module NetHttpAdapter
|
7
7
|
extend ActiveSupport::Memoizable
|
8
|
-
|
8
|
+
|
9
9
|
class HttpResponse
|
10
10
|
def initialize response
|
11
11
|
@response = response
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def success?
|
15
15
|
@response.is_a? Net::HTTPOK
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def body
|
19
19
|
@response.body
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def code
|
23
23
|
@response.code
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def send_method_call str
|
28
28
|
req = Net::HTTP.new connection.host, connection.port
|
29
|
-
|
29
|
+
|
30
30
|
HttpResponse.new req.post(connection.path, str, connection.headers.stringify_keys)
|
31
31
|
end
|
32
32
|
end
|
data/lib/rapuncel/base.rb
CHANGED
data/lib/rapuncel/client.rb
CHANGED
@@ -1,28 +1,34 @@
|
|
1
|
-
# require 'rapuncel/adapters/typhoeus_adapter'
|
2
1
|
require 'rapuncel/adapters/net_http_adapter'
|
3
2
|
require 'rapuncel/connection'
|
4
|
-
|
3
|
+
require 'active_support/core_ext/hash/except'
|
5
4
|
|
6
5
|
module Rapuncel
|
7
6
|
class Client
|
8
|
-
attr_accessor :connection
|
7
|
+
attr_accessor :connection, :raise_on_fault, :raise_on_error
|
9
8
|
|
10
9
|
include Adapters::NetHttpAdapter
|
11
10
|
|
12
11
|
def proxy_for interface
|
13
12
|
Proxy.new self, interface
|
14
13
|
end
|
15
|
-
|
14
|
+
|
16
15
|
def proxy
|
17
16
|
proxy_for nil
|
18
17
|
end
|
19
18
|
|
20
19
|
def initialize configuration = {}
|
21
|
-
@connection =
|
22
|
-
end
|
20
|
+
@connection = Connection.build configuration.except(:raise_on)
|
23
21
|
|
24
|
-
|
25
|
-
|
22
|
+
@raise_on_fault, @raise_on_error = case configuration[:raise_on]
|
23
|
+
when :fault
|
24
|
+
[true, false]
|
25
|
+
when :error
|
26
|
+
[false, true]
|
27
|
+
when :both
|
28
|
+
[true, true]
|
29
|
+
else
|
30
|
+
[false, false]
|
31
|
+
end
|
26
32
|
end
|
27
33
|
|
28
34
|
def call name, *args
|
@@ -30,16 +36,18 @@ module Rapuncel
|
|
30
36
|
end
|
31
37
|
|
32
38
|
def call_to_ruby name, *args
|
33
|
-
call
|
34
|
-
|
39
|
+
response = call name, *args
|
40
|
+
|
41
|
+
raise_on_fault && response.fault? && raise(Response::Fault, response.fault.inspect)
|
42
|
+
raise_on_error && response.error? && raise(Response::Error, response.error.inspect)
|
35
43
|
|
36
|
-
|
37
|
-
execute(request).to_ruby
|
44
|
+
response.to_ruby
|
38
45
|
end
|
39
46
|
|
47
|
+
protected
|
40
48
|
def execute request
|
41
49
|
xml = request.to_xml_rpc
|
42
|
-
|
50
|
+
|
43
51
|
Response.new send_method_call(xml)
|
44
52
|
end
|
45
53
|
end
|
data/lib/rapuncel/connection.rb
CHANGED
@@ -17,7 +17,7 @@ module Rapuncel
|
|
17
17
|
alias_method :ssl?, :ssl
|
18
18
|
|
19
19
|
def initialize configuration = {}
|
20
|
-
|
20
|
+
|
21
21
|
@host = configuration[:host] || 'localhost'
|
22
22
|
@port = configuration[:port] || '8080'
|
23
23
|
@path = configuration[:path] || '/'
|
@@ -33,7 +33,7 @@ module Rapuncel
|
|
33
33
|
def url
|
34
34
|
"http://#{host}:#{port}#{path}"
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def headers
|
38
38
|
@headers.merge :Accept => 'text/xml', :'content-type' => 'text/xml'
|
39
39
|
end
|
@@ -1,30 +1,23 @@
|
|
1
|
-
require 'builder'
|
2
|
-
require 'nokogiri'
|
3
|
-
|
4
1
|
class Array
|
5
|
-
|
6
2
|
def to_xml_rpc b = Rapuncel.get_builder
|
7
|
-
|
8
|
-
|
9
|
-
b.data do
|
3
|
+
b.array do |b|
|
4
|
+
b.data do |b|
|
10
5
|
each do |array_entry|
|
11
|
-
b.value do
|
6
|
+
b.value do |b|
|
12
7
|
array_entry.to_xml_rpc b
|
13
8
|
end
|
14
9
|
end
|
15
10
|
end
|
16
11
|
end
|
12
|
+
|
13
|
+
b.to_xml
|
17
14
|
end
|
18
|
-
|
19
|
-
|
15
|
+
|
20
16
|
def self.from_xml_rpc xml_node
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
values = xml_node.first_element_child.element_children #xpath('./data/value/*')
|
25
|
-
|
17
|
+
values = xml_node.first_element_child.element_children
|
18
|
+
|
26
19
|
values.map do |value|
|
27
20
|
Object.from_xml_rpc value.first_element_child
|
28
|
-
end
|
21
|
+
end
|
29
22
|
end
|
30
23
|
end
|
@@ -1,32 +1,29 @@
|
|
1
|
-
require 'builder'
|
2
|
-
require 'nokogiri'
|
3
|
-
|
4
1
|
class TrueClass
|
5
|
-
|
6
2
|
def to_xml_rpc b = Rapuncel.get_builder
|
7
3
|
b.boolean "1"
|
4
|
+
|
5
|
+
b.to_xml
|
8
6
|
end
|
9
7
|
end
|
10
8
|
|
11
|
-
|
12
9
|
class FalseClass
|
13
|
-
|
14
10
|
def to_xml_rpc b = Rapuncel.get_builder
|
15
11
|
b.boolean "0"
|
12
|
+
|
13
|
+
b.to_xml
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|
19
|
-
|
20
|
-
class Rapuncel::Boolean
|
17
|
+
# this is to catch the from_xml_rpc call from Object
|
18
|
+
class Rapuncel::Boolean
|
21
19
|
def self.from_xml_rpc xml_node
|
22
|
-
|
23
|
-
|
24
|
-
#need convention here:
|
20
|
+
|
21
|
+
# DISCUSS: need convention here:
|
25
22
|
case xml_node.text.downcase
|
26
23
|
when '1'
|
27
24
|
true
|
28
25
|
else
|
29
26
|
false
|
30
|
-
end
|
31
|
-
end
|
27
|
+
end
|
28
|
+
end
|
32
29
|
end
|
@@ -1,14 +1,11 @@
|
|
1
|
-
require 'builder'
|
2
|
-
require 'nokogiri'
|
3
|
-
|
4
1
|
class Float
|
5
2
|
def self.from_xml_rpc xml_node
|
6
|
-
|
7
|
-
|
8
|
-
xml_node.text.to_f #calling to_float on the text between the (hopefully correct) tags
|
3
|
+
xml_node.text.strip.to_f #calling to_float on the text between the (hopefully correct) tags
|
9
4
|
end
|
10
|
-
|
5
|
+
|
11
6
|
def to_xml_rpc b = Rapuncel.get_builder
|
12
7
|
b.double to_s
|
8
|
+
|
9
|
+
b.to_xml
|
13
10
|
end
|
14
11
|
end
|
@@ -1,42 +1,32 @@
|
|
1
|
-
require 'builder'
|
2
|
-
|
3
|
-
class Rapuncel::Hash
|
4
|
-
|
5
|
-
end
|
6
|
-
|
7
1
|
class Hash
|
8
|
-
#Hash will be translated into an XML-RPC "struct" object
|
2
|
+
# Hash will be translated into an XML-RPC "struct" object
|
9
3
|
|
10
4
|
def to_xml_rpc b = Rapuncel.get_builder
|
11
|
-
|
12
5
|
b.struct do |b|
|
13
6
|
self.each_pair do |key, value|
|
14
|
-
|
15
|
-
#warn "The key #{key.to_s} is a #{key.class.to_s}, which is neither a symbol nor a string. It will be converted using to_s" unless key.is_a?(String) || key.is_a?(Symbol)
|
16
|
-
|
17
|
-
b.member do
|
18
|
-
|
7
|
+
b.member do |b|
|
19
8
|
b.name key.to_s
|
20
|
-
|
9
|
+
|
10
|
+
b.value do |b|
|
21
11
|
value.to_xml_rpc b
|
22
12
|
end
|
23
13
|
end
|
24
14
|
end
|
25
15
|
end
|
16
|
+
|
17
|
+
b.to_xml
|
26
18
|
end
|
27
|
-
|
19
|
+
|
28
20
|
def self.from_xml_rpc xml_node
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
hash[key] = value
|
21
|
+
keys_and_values = xml_node.element_children
|
22
|
+
|
23
|
+
new.tap do |hash|
|
24
|
+
keys_and_values.each do |kv|
|
25
|
+
key = kv.first_element_child.text.strip.to_sym
|
26
|
+
value = Object.from_xml_rpc kv.last_element_child.first_element_child
|
27
|
+
|
28
|
+
hash[key] = value
|
29
|
+
end
|
39
30
|
end
|
40
|
-
|
41
|
-
end
|
31
|
+
end
|
42
32
|
end
|