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
@@ -1,19 +1,11 @@
1
- require 'builder'
2
- require 'nokogiri'
3
- require 'ruby-debug'
4
-
5
1
  class Integer
6
-
7
2
  def to_xml_rpc b = Rapuncel.get_builder
8
- #warn "XML-RPC standard only supports 4 byte signed integers, i.e #{(-2**31).to_s} to #{2**31-1}" unless ((-2**31)...(2**31)) === self
9
-
10
3
  b.int self.to_s
11
4
 
5
+ b.to_xml
12
6
  end
13
7
 
14
8
  def self.from_xml_rpc xml_node
15
- #warn "xml node given (name of #{xml_node.name}) is not of integer type, node name should be 'i4' or 'int'" unless ['i4','int'].include? xml_node.name.downcase
16
-
17
- xml_node.text.to_i #calling to_i on the text between the i4 or int tags
9
+ xml_node.text.strip.to_i #calling to_i on the text between the i4 or int tags
18
10
  end
19
11
  end
@@ -0,0 +1,7 @@
1
+ class NilClass
2
+ def to_xml_rpc b = Rapuncel.get_builder
3
+ false.to_xml_rpc b
4
+
5
+ b.to_xml
6
+ end
7
+ end
@@ -1,21 +1,21 @@
1
- require 'builder'
2
-
3
1
  class Object
4
2
  def to_xml_rpc b = Rapuncel.get_builder
5
3
  if respond_to?(:acts_like?) && acts_like?(:time)
6
4
  to_time.to_xml_rpc b
7
- else
5
+ else
8
6
  _collect_ivars_in_hash.to_xml_rpc b
9
7
  end
8
+
9
+ b.to_xml
10
10
  end
11
11
 
12
12
  def self.from_xml_rpc xml_node
13
13
  if xml_node.is_a? String
14
14
  xml_node = Nokogiri::XML.parse(xml_node).root
15
15
  end
16
-
16
+
17
17
  return nil if xml_node.nil?
18
-
18
+
19
19
  case xml_node.name
20
20
  when 'i4', 'int'
21
21
  Integer.from_xml_rpc xml_node
@@ -34,7 +34,7 @@ class Object
34
34
  when 'base64'
35
35
  raise 'Now its time to implement Base64'
36
36
  else
37
- raise "What is this? I didn't know #{xml_node.name} was part of the XMLRPC specification? Anyway, the value was: #{xml_node.text}"
37
+ raise "What is this? I didn't know #{xml_node.name} was part of the XMLRPC specification? Anyway, the value was: #{xml_node.text.strip}"
38
38
  end
39
39
  end
40
40
 
@@ -1,14 +1,12 @@
1
- require 'builder'
2
- require 'nokogiri'
3
-
4
1
  class String
5
2
  def to_xml_rpc b = Rapuncel.get_builder
6
3
  b.string self
4
+
5
+ b.to_xml
7
6
  end
8
-
7
+
9
8
  def self.from_xml_rpc xml_node
10
- #warn "I, String.from_xml_rpc have been given an xml_node with the wrong tag (name of #{xml_node.name}). My tag should be 'string'. Will still parse at your risk" unless ['string'].include? xml_node.name.downcase
11
-
12
- xml_node.text #just give back the string between the 'string' tags
9
+ xml_node.text.gsub(/(\r\n|\r)/, "\n") #just give back the string between the 'string' tags
10
+ # DISCUSS: to strip or not to strip
13
11
  end
14
12
  end
@@ -1,8 +1,7 @@
1
- require 'builder'
2
- require 'nokogiri'
3
-
4
1
  class Symbol
5
2
  def to_xml_rpc b = Rapuncel.get_builder
6
3
  b.string to_s
4
+
5
+ b.to_xml
7
6
  end
8
7
  end
@@ -1,17 +1,14 @@
1
- require 'builder'
2
1
  require 'time'
3
- require 'nokogiri'
4
2
 
5
3
  class Time
6
4
 
7
5
  def to_xml_rpc b=Rapuncel.get_builder
8
- b.tag! "dateTime.iso8601", self.iso8601
6
+ b.send "dateTime.iso8601", self.iso8601
7
+
8
+ b.to_xml
9
9
  end
10
-
10
+
11
11
  def self.from_xml_rpc xml_node
12
- #warn "Need node to be named 'dateTime.iso8601', but it is #{xml_node.name}. Will still parse, at your risk" unless ['dateTime.iso8601'].include? xml_node.name
13
-
14
- Time.parse xml_node.text #make a Time object of this string which is hopefully in the right format
12
+ Time.parse xml_node.text.strip #make a Time object of this string which is hopefully in the right format
15
13
  end
16
-
17
14
  end
@@ -2,23 +2,38 @@ require 'rapuncel/request'
2
2
 
3
3
  module Rapuncel
4
4
  class Proxy
5
- PROXY_METHODS = %w(tap inspect clone freeze dup class initialize)
6
- LOCKED_METHODS = %w(method_missing)
7
- LOCKED_PATTERN = /(\A__|\?\Z|!\Z)/
5
+ PROXY_METHODS = %w(tap inspect clone freeze dup class initialize).freeze
6
+ LOCKED_METHODS = %w(method_missing).freeze
7
+ LOCKED_PATTERN = /(\A__|\?\Z|!\Z)/.freeze
8
8
 
9
9
  class << self
10
- def new client_or_configuration, interface = nil
10
+ # Initialize a new Proxy object for a specific Client. Alternatively
11
+ # you can pass a Hash containing configuration for a new Client, which
12
+ # will be created on-the-fly, but not accessible. The second parameter
13
+ # specifies a specific interface/namespace for the remote calls,
14
+ # i.e. if your RPC method is
15
+ #
16
+ # int numbers.add(int a, int b)
17
+ #
18
+ # You can create a specific proxy for +numbers+, and use +add+ directly
19
+ #
20
+ # proxy = Proxy.new client, 'numbers'
21
+ # proxy.add(40, 2) -> 42
22
+ #
23
+ def new client_or_configuration, interface = nil
11
24
  client_or_configuration = Client.new client_or_configuration if client_or_configuration.is_a?(Hash)
12
-
25
+
13
26
  allocate.__tap__ do |new_proxy|
14
27
  new_proxy.__initialize__ client_or_configuration, interface
15
28
  end
16
29
  end
17
30
 
18
31
  def define_proxy_method name
19
- define_method name do |*args|
20
- call! name, *args
21
- end
32
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
33
+ def #{name} *args, &block
34
+ call! '#{name}', *args, &block
35
+ end
36
+ RUBY
22
37
  end
23
38
  end
24
39
 
@@ -33,24 +48,34 @@ module Rapuncel
33
48
  end
34
49
 
35
50
  def call! name, *args
36
- name = [@interface, name] * '.' if @interface
37
- @client.execute_to_ruby Request.new(name, *args)
51
+ name = "#{@interface}.#{name}" if @interface
52
+
53
+ @client.call_to_ruby(name, *args).tap do |response|
54
+
55
+ if block_given?
56
+ yield response
57
+ end
58
+ end
38
59
  end
39
60
 
40
- def __initialize__ client, interface
61
+ def __initialize__ client, interface #:nodoc:
41
62
  @interface = interface
42
63
  @client = client
43
64
  end
44
65
 
66
+ def respond_to? name #:nodoc:
67
+ LOCKED_PATTERN.match(name.to_s) ? super : true
68
+ end
69
+
45
70
  protected
46
- def method_missing name, *args
71
+ def method_missing name, *args, &block #:nodoc:
47
72
  name = name.to_s
48
73
 
49
74
  if LOCKED_PATTERN.match name
50
- super
75
+ super name.to_sym, *args, &block
51
76
  else
52
77
  self.__class__.define_proxy_method name
53
- call! name, *args
78
+ call! name, *args, &block
54
79
  end
55
80
  end
56
81
  end
@@ -1,4 +1,4 @@
1
- require 'builder'
1
+
2
2
 
3
3
 
4
4
  module Rapuncel
@@ -13,13 +13,14 @@ module Rapuncel
13
13
 
14
14
  def to_xml_rpc builder = Rapuncel.get_builder
15
15
  method_call! builder
16
+
17
+ builder.to_xml :encoding => 'UTF-8'
16
18
  end
17
19
 
18
20
  protected
19
21
  def method_call! builder
20
- builder.instruct!
21
22
 
22
- builder.methodCall do
23
+ builder.methodCall do |builder|
23
24
  method_name! builder
24
25
  params! builder
25
26
  end
@@ -30,7 +31,7 @@ module Rapuncel
30
31
  end
31
32
 
32
33
  def params! builder
33
- builder.params do
34
+ builder.params do |builder|
34
35
  arguments.each do |value|
35
36
  param! builder, value
36
37
  end
@@ -38,8 +39,8 @@ module Rapuncel
38
39
  end
39
40
 
40
41
  def param! builder, value
41
- builder.param do
42
- builder.value do
42
+ builder.param do |builder|
43
+ builder.value do |builder|
43
44
  value.to_xml_rpc builder
44
45
  end
45
46
  end
@@ -1,9 +1,12 @@
1
1
  require 'active_support/core_ext/module/delegation'
2
- require 'rapuncel/fault'
3
2
 
4
3
  module Rapuncel
5
4
  class Response
6
- attr_accessor :http_response, :status
5
+ class Exception < ::Exception ; end
6
+ class Fault < Exception ; end
7
+ class Error < Exception ; end
8
+
9
+ attr_accessor :http_response, :status, :status_code
7
10
 
8
11
  delegate :body,
9
12
  :to => :http_response
@@ -11,11 +14,16 @@ module Rapuncel
11
14
  def initialize http_response
12
15
  @http_response = http_response
13
16
 
14
- if http_response.success?
15
- parse_response
16
- else
17
- @status = http_response.code
18
- raise("HTTP Error: #{@status}\n#{body}")
17
+ evaluate_status
18
+ end
19
+
20
+ def evaluate_status
21
+ @status_code = http_response.code.to_i
22
+
23
+ @status = case
24
+ when !http_response.success? then 'error'
25
+ when parsed_body && method_response_success? then 'success'
26
+ else 'fault'
19
27
  end
20
28
  end
21
29
 
@@ -23,32 +31,53 @@ module Rapuncel
23
31
  status == 'success'
24
32
  end
25
33
 
26
- def to_ruby
27
- @to_ruby
28
- end
29
-
30
- def parse_fault
31
- fault = @xml_doc.xpath('/methodResponse/fault/value/struct')
32
-
33
- @to_ruby = Fault.new Hash.from_xml_rpc(fault.first)
34
- end
35
-
36
- def parse_response
37
- @xml_doc = Nokogiri::XML.parse body
38
-
39
- if @xml_doc.xpath('/methodResponse/fault').empty?
40
- @status = 'success'
41
- parse_success
42
- else
43
- @status = 'fault'
44
- parse_fault
34
+ def fault?
35
+ status == 'fault'
36
+ end
37
+
38
+ def error?
39
+ status == 'error'
40
+ end
41
+
42
+ def fault
43
+ if fault?
44
+ @fault ||= begin
45
+ fault_node = parsed_body.xpath('/methodResponse/fault/value/struct').first
46
+
47
+ Hash.from_xml_rpc(fault_node).tap do |h|
48
+ h[:faultString] = h[:faultString].strip
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def error
55
+ if error?
56
+ @error ||= { :http_code => @status_code, :http_body => body }
57
+ end
58
+ end
59
+
60
+ def result
61
+ if success?
62
+ @result ||= begin
63
+ return_values = parsed_body.xpath('/methodResponse/params/param/value/*')
64
+
65
+ Object.from_xml_rpc return_values.first
66
+ end
45
67
  end
46
68
  end
47
-
48
- def parse_success
49
- values = @xml_doc.xpath('/methodResponse/params/param/value/*')
50
-
51
- @to_ruby = Object.from_xml_rpc values.to_a.first
69
+
70
+ def to_ruby
71
+ result || fault || error
72
+ end
73
+
74
+ protected
75
+ def parsed_body
76
+ @xml_doc ||= Nokogiri::XML.parse body
77
+ end
78
+
79
+ def method_response_success?
80
+ parsed_body.xpath('/methodResponse/fault').empty?
52
81
  end
53
82
  end
54
83
  end
data/rapuncel.gemspec CHANGED
@@ -1,12 +1,12 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rapuncel"
3
- s.version = "0.0.1"
3
+ s.version = "0.0.2"
4
4
  s.date = Time.now.strftime("%Y-%m-%d")
5
5
  s.authors = ["Michael Eickenberg", "Marian Theisen"]
6
6
  s.email = 'marian@cice-online.net'
7
7
  s.summary = "Simple XML-RPC Client"
8
8
  s.homepage = "http://github.com/cice/rapuncel"
9
- s.description = "Simple XML-RPC Client"
9
+ s.description = "Rapuncel is a simple XML-RPC Client based on Nokogiri, thus provides a fast and easy way to interact with XML-RPC services."
10
10
 
11
11
  s.files = Dir["**/*"] -
12
12
  Dir["coverage/**/*"] -
@@ -16,7 +16,6 @@ Gem::Specification.new do |s|
16
16
  Dir["rcov/**/*"]
17
17
 
18
18
  s.add_dependency 'nokogiri'
19
- s.add_dependency 'builder'
20
19
  s.add_dependency 'activesupport', '>= 3.0.0'
21
20
 
22
21
  s.add_development_dependency 'mocha'
@@ -2,22 +2,53 @@ require 'functional_test_helper'
2
2
 
3
3
  class FunctionalClientTest < FunctionalTest
4
4
  test "Simple XMLRPC call" do
5
- client = Rapuncel::Client.new :port => 8080
5
+ client = Rapuncel::Client.new :port => 8080, :raise_on => :both
6
6
  proxy = client.proxy_for 'num'
7
-
7
+
8
8
  result = proxy.add 40, 2
9
-
9
+
10
10
  assert_equal 42, result
11
11
  end
12
12
 
13
- test "Fault rpc call" do
13
+ test "Fault rpc call without raise" do
14
14
  client = Rapuncel::Client.new :port => 8080
15
15
  proxy = client.proxy_for 'num'
16
16
 
17
- result = proxy.add 20, 20, 2
17
+ assert_nothing_raised Rapuncel::Response::Exception do
18
+ proxy.add 20, 20, 2
19
+ end
18
20
 
19
- assert_kind_of Rapuncel::Fault, result
21
+ assert_kind_of Hash, proxy.add(20, 20, 2)
20
22
  end
21
-
22
23
 
24
+ test "Fault rpc call" do
25
+ client = Rapuncel::Client.new :port => 8080, :raise_on => :both
26
+ proxy = client.proxy_for 'num'
27
+
28
+ assert_raise Rapuncel::Response::Fault do
29
+ proxy.add 20, 20, 2
30
+ end
31
+ end
32
+
33
+ test "Error rpc connection" do
34
+ client = Rapuncel::Client.new :host => 'www.cice-online.net', :port => 80, :path => '/hullahoobahubbahooo', :raise_on => :both
35
+ proxy = client.proxy
36
+
37
+ assert_raise Rapuncel::Response::Error do
38
+ proxy.foo :bar, :baz
39
+ end
40
+ end
41
+
42
+ test "Error rpc connection without raise" do
43
+ client = Rapuncel::Client.new :host => 'www.google.de', :port => 80, :path => '/hullahoobahubbahooo'
44
+ proxy = client.proxy
45
+
46
+ assert_nothing_raised Rapuncel::Response::Error do
47
+ proxy.foo :bar, :baz
48
+ end
49
+
50
+ err = proxy.foo(:bar, :baz)
51
+ assert_kind_of Hash, err
52
+ assert_equal 404, err[:http_code]
53
+ end
23
54
  end