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.
Files changed (63) hide show
  1. data/README.md +39 -33
  2. data/{Rakefile.rb → Rakefile} +1 -1
  3. data/lib/rapuncel.rb +2 -1
  4. data/lib/rapuncel/adapters/net_http_adapter.rb +6 -6
  5. data/lib/rapuncel/base.rb +2 -2
  6. data/lib/rapuncel/client.rb +21 -13
  7. data/lib/rapuncel/connection.rb +2 -2
  8. data/lib/rapuncel/core_ext/array.rb +9 -16
  9. data/lib/rapuncel/core_ext/big_decimal.rb +7 -0
  10. data/lib/rapuncel/core_ext/boolean.rb +10 -13
  11. data/lib/rapuncel/core_ext/float.rb +4 -7
  12. data/lib/rapuncel/core_ext/hash.rb +17 -27
  13. data/lib/rapuncel/core_ext/integer.rb +2 -10
  14. data/lib/rapuncel/core_ext/nil.rb +7 -0
  15. data/lib/rapuncel/core_ext/object.rb +6 -6
  16. data/lib/rapuncel/core_ext/string.rb +5 -7
  17. data/lib/rapuncel/core_ext/symbol.rb +2 -3
  18. data/lib/rapuncel/core_ext/time.rb +5 -8
  19. data/lib/rapuncel/proxy.rb +39 -14
  20. data/lib/rapuncel/request.rb +7 -6
  21. data/lib/rapuncel/response.rb +60 -31
  22. data/rapuncel.gemspec +2 -3
  23. data/test/functional/client_test.rb +38 -7
  24. data/test/functional_test_helper.rb +2 -2
  25. data/test/test_server.rb +3 -3
  26. data/test/unit/array_test.rb +14 -14
  27. data/test/unit/boolean_test.rb +6 -6
  28. data/test/unit/float_test.rb +7 -7
  29. data/test/unit/hash_test.rb +16 -16
  30. data/test/unit/nil_test.rb +16 -0
  31. data/test/unit/object_test.rb +29 -29
  32. data/test/unit/proxy_test.rb +9 -9
  33. data/test/unit/response_test.rb +69 -5
  34. data/test/unit/string_test.rb +15 -7
  35. metadata +13 -53
  36. data/lib/rapuncel/adapters/typhoeus_adapter.rb +0 -33
  37. data/lib/rapuncel/fault.rb +0 -10
  38. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-adapters-typhoeus_adapter_rb.html +0 -231
  39. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-base_rb.html +0 -105
  40. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-client_rb.html +0 -321
  41. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-connection_rb.html +0 -513
  42. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-array_rb.html +0 -303
  43. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-boolean_rb.html +0 -255
  44. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-float_rb.html +0 -177
  45. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-hash_rb.html +0 -297
  46. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-integer_rb.html +0 -177
  47. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-object_rb.html +0 -297
  48. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-string_rb.html +0 -159
  49. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-time_rb.html +0 -165
  50. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-fault_rb.html +0 -93
  51. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-proxy_rb.html +0 -393
  52. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-request_rb.html +0 -351
  53. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-response_rb.html +0 -399
  54. data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel_rb.html +0 -201
  55. data/test/coverage/functional_test_helper_rb.html +0 -141
  56. data/test/coverage/index.html +0 -410
  57. data/test/coverage/jquery-1.3.2.min.js +0 -19
  58. data/test/coverage/jquery.tablesorter.min.js +0 -15
  59. data/test/coverage/print.css +0 -12
  60. data/test/coverage/rcov.js +0 -42
  61. data/test/coverage/screen.css +0 -270
  62. data/test/coverage/test_helper_rb.html +0 -291
  63. 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
- client.call 'methodname', *args
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
- 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
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
 
@@ -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
- require 'builder'
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
@@ -1,7 +1,7 @@
1
1
  module Rapuncel
2
- BUILDER_OPTIONS = {}
2
+ BUILDER_OPTIONS = {:encoding => 'UTF-8'}
3
3
 
4
4
  def self.get_builder options = {}
5
- Builder::XmlMarkup.new BUILDER_OPTIONS.merge(options)
5
+ Nokogiri::XML::Builder.new options
6
6
  end
7
7
  end
@@ -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 = init_connection(configuration)
22
- end
20
+ @connection = Connection.build configuration.except(:raise_on)
23
21
 
24
- def init_connection configuration = {}
25
- Connection.build configuration
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(name, *args).to_ruby
34
- end
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
- def execute_to_ruby request
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
@@ -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
- b.array do
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
- #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
-
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
@@ -0,0 +1,7 @@
1
+ class BigDecimal
2
+ def to_xml_rpc b = Rapuncel.get_builder
3
+ b.double self.to_s
4
+
5
+ b.to_xml
6
+ end
7
+ 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 #this is to catch the from_xml_rpc call from Object
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
- #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:
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
- #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
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
- b.value do
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
- #warn "The given xml_node is a #{xml_node.name}, not a 'struct'. Continuing at your risk" unless ['struct'].include? xml_node.name
30
-
31
- keys_and_values = xml_node.element_children #xpath('./member')
32
-
33
- hash = new
34
- keys_and_values.each do |kv|
35
- key = kv.first_element_child.text.to_sym
36
-
37
- value = Object.from_xml_rpc kv.last_element_child.first_element_child
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
- hash
41
- end
31
+ end
42
32
  end