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