rapuncel 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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