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
@@ -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