rapuncel 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +39 -33
- data/{Rakefile.rb → Rakefile} +1 -1
- data/lib/rapuncel.rb +2 -1
- data/lib/rapuncel/adapters/net_http_adapter.rb +6 -6
- data/lib/rapuncel/base.rb +2 -2
- data/lib/rapuncel/client.rb +21 -13
- data/lib/rapuncel/connection.rb +2 -2
- data/lib/rapuncel/core_ext/array.rb +9 -16
- data/lib/rapuncel/core_ext/big_decimal.rb +7 -0
- data/lib/rapuncel/core_ext/boolean.rb +10 -13
- data/lib/rapuncel/core_ext/float.rb +4 -7
- data/lib/rapuncel/core_ext/hash.rb +17 -27
- data/lib/rapuncel/core_ext/integer.rb +2 -10
- data/lib/rapuncel/core_ext/nil.rb +7 -0
- data/lib/rapuncel/core_ext/object.rb +6 -6
- data/lib/rapuncel/core_ext/string.rb +5 -7
- data/lib/rapuncel/core_ext/symbol.rb +2 -3
- data/lib/rapuncel/core_ext/time.rb +5 -8
- data/lib/rapuncel/proxy.rb +39 -14
- data/lib/rapuncel/request.rb +7 -6
- data/lib/rapuncel/response.rb +60 -31
- data/rapuncel.gemspec +2 -3
- data/test/functional/client_test.rb +38 -7
- data/test/functional_test_helper.rb +2 -2
- data/test/test_server.rb +3 -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/nil_test.rb +16 -0
- data/test/unit/object_test.rb +29 -29
- data/test/unit/proxy_test.rb +9 -9
- data/test/unit/response_test.rb +69 -5
- data/test/unit/string_test.rb +15 -7
- metadata +13 -53
- data/lib/rapuncel/adapters/typhoeus_adapter.rb +0 -33
- data/lib/rapuncel/fault.rb +0 -10
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-adapters-typhoeus_adapter_rb.html +0 -231
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-base_rb.html +0 -105
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-client_rb.html +0 -321
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-connection_rb.html +0 -513
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-array_rb.html +0 -303
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-boolean_rb.html +0 -255
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-float_rb.html +0 -177
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-hash_rb.html +0 -297
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-integer_rb.html +0 -177
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-object_rb.html +0 -297
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-string_rb.html +0 -159
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-core_ext-time_rb.html +0 -165
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-fault_rb.html +0 -93
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-proxy_rb.html +0 -393
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-request_rb.html +0 -351
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel-response_rb.html +0 -399
- data/test/coverage/-Users-mariantheisen-Projects-Kayoom-rapuncel-lib-rapuncel_rb.html +0 -201
- data/test/coverage/functional_test_helper_rb.html +0 -141
- data/test/coverage/index.html +0 -410
- data/test/coverage/jquery-1.3.2.min.js +0 -19
- data/test/coverage/jquery.tablesorter.min.js +0 -15
- data/test/coverage/print.css +0 -12
- data/test/coverage/rcov.js +0 -42
- data/test/coverage/screen.css +0 -270
- data/test/coverage/test_helper_rb.html +0 -291
- 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
|
-
|
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
|
@@ -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
|
-
|
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,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.
|
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
|
-
#
|
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
|
data/lib/rapuncel/proxy.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
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 =
|
37
|
-
|
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
|
data/lib/rapuncel/request.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
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
|
data/lib/rapuncel/response.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
49
|
-
|
50
|
-
|
51
|
-
|
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.
|
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 = "
|
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
|
-
|
17
|
+
assert_nothing_raised Rapuncel::Response::Exception do
|
18
|
+
proxy.add 20, 20, 2
|
19
|
+
end
|
18
20
|
|
19
|
-
assert_kind_of
|
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
|