rapuncel 0.0.4 → 0.0.5.RC1
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.
- data/Gemfile +3 -0
- data/README.md +35 -24
- data/Rakefile +11 -11
- data/lib/rapuncel.rb +9 -17
- data/lib/rapuncel/adapters/net_http_adapter.rb +7 -5
- data/lib/rapuncel/base_64_string.rb +31 -0
- data/lib/rapuncel/client.rb +2 -2
- data/lib/rapuncel/connection.rb +35 -58
- data/lib/rapuncel/request.rb +1 -38
- data/lib/rapuncel/response.rb +17 -30
- data/lib/rapuncel/xml_rpc_deserializer.rb +110 -0
- data/lib/rapuncel/xml_rpc_serializer.rb +148 -0
- data/rapuncel.gemspec +3 -2
- data/spec/functional/client_spec.rb +53 -0
- data/spec/spec_helper.rb +48 -0
- data/{test → spec}/test_server.rb +0 -1
- data/spec/unit/array_spec.rb +41 -0
- data/spec/unit/base64_spec.rb +30 -0
- data/spec/unit/boolean_spec.rb +39 -0
- data/spec/unit/connection_spec.rb +22 -0
- data/spec/unit/float_spec.rb +38 -0
- data/spec/unit/hash_spec.rb +54 -0
- data/spec/unit/int_spec.rb +23 -0
- data/spec/unit/nil_spec.rb +7 -0
- data/spec/unit/object_spec.rb +24 -0
- data/spec/unit/proxy_spec.rb +29 -0
- data/spec/unit/request_spec.rb +28 -0
- data/{test/unit/response_test.rb → spec/unit/response_spec.rb} +29 -44
- data/spec/unit/string_spec.rb +35 -0
- data/spec/unit/time_spec.rb +20 -0
- metadata +77 -104
- data/lib/rapuncel/base.rb +0 -7
- data/lib/rapuncel/core_ext/array.rb +0 -23
- data/lib/rapuncel/core_ext/big_decimal.rb +0 -7
- data/lib/rapuncel/core_ext/boolean.rb +0 -29
- data/lib/rapuncel/core_ext/float.rb +0 -11
- data/lib/rapuncel/core_ext/hash.rb +0 -32
- data/lib/rapuncel/core_ext/integer.rb +0 -11
- data/lib/rapuncel/core_ext/nil.rb +0 -7
- data/lib/rapuncel/core_ext/object.rb +0 -49
- data/lib/rapuncel/core_ext/string.rb +0 -12
- data/lib/rapuncel/core_ext/symbol.rb +0 -7
- data/lib/rapuncel/core_ext/time.rb +0 -14
- data/rapuncel-0.0.3.gem +0 -0
- data/test/functional/client_test.rb +0 -54
- data/test/functional_test_helper.rb +0 -13
- data/test/test_helper.rb +0 -38
- data/test/unit/array_test.rb +0 -97
- data/test/unit/boolean_test.rb +0 -34
- data/test/unit/connection_test.rb +0 -29
- data/test/unit/float_test.rb +0 -23
- data/test/unit/hash_test.rb +0 -54
- data/test/unit/int_test.rb +0 -27
- data/test/unit/nil_test.rb +0 -16
- data/test/unit/object_test.rb +0 -83
- data/test/unit/proxy_test.rb +0 -52
- data/test/unit/request_test.rb +0 -34
- data/test/unit/string_test.rb +0 -40
- data/test/unit/time_test.rb +0 -23
@@ -0,0 +1,110 @@
|
|
1
|
+
module Rapuncel
|
2
|
+
class XmlRpcDeserializer
|
3
|
+
XML_ENCODING = 'UTF-8'
|
4
|
+
|
5
|
+
def initialize xml
|
6
|
+
@xml_doc = Nokogiri::XML.parse xml
|
7
|
+
end
|
8
|
+
|
9
|
+
def deserialize xml_node = root_node
|
10
|
+
send "deserialize_#{xml_node.name}", xml_node
|
11
|
+
end
|
12
|
+
|
13
|
+
def deserialize_base64 xml_node
|
14
|
+
Base64String.decode_base64 xml_node.text.strip
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize_string xml_node
|
18
|
+
xml_node.text.gsub(/(\r\n|\r)/, "\n")
|
19
|
+
end
|
20
|
+
|
21
|
+
def deserialize_array xml_node
|
22
|
+
values = xml_node.first_element_child.element_children
|
23
|
+
|
24
|
+
values.map do |value|
|
25
|
+
deserialize value.first_element_child
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def deserialize_boolean xml_node
|
30
|
+
xml_node.text == "1"
|
31
|
+
end
|
32
|
+
|
33
|
+
def deserialize_double xml_node
|
34
|
+
text = xml_node.text.strip
|
35
|
+
|
36
|
+
if double_as_bigdecimal?
|
37
|
+
BigDecimal.new text
|
38
|
+
else
|
39
|
+
text.to_f
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def deserialize_struct xml_node
|
44
|
+
keys_and_values = xml_node.element_children
|
45
|
+
|
46
|
+
{}.tap do |hash|
|
47
|
+
keys_and_values.each do |key_value|
|
48
|
+
key = key_value.first_element_child.text.strip
|
49
|
+
key = key.to_sym unless hash_keys_as_string?
|
50
|
+
|
51
|
+
value = deserialize key_value.last_element_child.first_element_child
|
52
|
+
|
53
|
+
hash[key] = value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def deserialize_int xml_node
|
59
|
+
xml_node.text.strip.to_i
|
60
|
+
end
|
61
|
+
alias_method :deserialize_i4, :deserialize_int
|
62
|
+
|
63
|
+
define_method "deserialize_dateTime.iso8601" do |xml_node|
|
64
|
+
Time.parse xml_node.text.strip
|
65
|
+
end
|
66
|
+
|
67
|
+
def deserialize_methodResponse xml_node
|
68
|
+
response = xml_node.first_element_child
|
69
|
+
|
70
|
+
if response.name == 'fault'
|
71
|
+
deserialize_methodResponse_fault response
|
72
|
+
else
|
73
|
+
deserialize_methodResponse_params response
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def deserialize_methodResponse_params xml_node
|
78
|
+
deserialize xml_node.first_element_child.first_element_child.first_element_child
|
79
|
+
end
|
80
|
+
|
81
|
+
def deserialize_methodResponse_fault xml_node
|
82
|
+
deserialize xml_node.first_element_child.first_element_child
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_ruby
|
86
|
+
deserialize
|
87
|
+
end
|
88
|
+
|
89
|
+
protected
|
90
|
+
def root_node
|
91
|
+
@xml_doc.root
|
92
|
+
end
|
93
|
+
|
94
|
+
def double_as_bigdecimal?
|
95
|
+
!!self.class.double_as_bigdecimal
|
96
|
+
end
|
97
|
+
|
98
|
+
def hash_keys_as_string?
|
99
|
+
!!self.class.hash_keys_as_string
|
100
|
+
end
|
101
|
+
|
102
|
+
class << self
|
103
|
+
attr_accessor :double_as_bigdecimal, :hash_keys_as_string
|
104
|
+
|
105
|
+
def [] xml
|
106
|
+
new(xml).to_ruby
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Rapuncel
|
5
|
+
class XmlRpcSerializer
|
6
|
+
XML_ENCODING = 'UTF-8'
|
7
|
+
|
8
|
+
attr_reader :builder
|
9
|
+
|
10
|
+
def initialize object
|
11
|
+
@builder = Nokogiri::XML::Builder.new :encoding => XML_ENCODING
|
12
|
+
|
13
|
+
serialize object
|
14
|
+
end
|
15
|
+
|
16
|
+
def serialize object
|
17
|
+
case object
|
18
|
+
when Base64String
|
19
|
+
serialize_base64 object
|
20
|
+
when Array
|
21
|
+
serialize_array object
|
22
|
+
when String, Symbol
|
23
|
+
serialize_string object.to_s
|
24
|
+
when TrueClass
|
25
|
+
serialize_true
|
26
|
+
when FalseClass
|
27
|
+
serialize_false
|
28
|
+
when Float
|
29
|
+
serialize_float object
|
30
|
+
when BigDecimal
|
31
|
+
serialize_big_decimal object
|
32
|
+
when Hash
|
33
|
+
serialize_hash object
|
34
|
+
when Integer
|
35
|
+
serialize_integer object
|
36
|
+
when NilClass
|
37
|
+
serialize_nil
|
38
|
+
when Time
|
39
|
+
serialize_time object
|
40
|
+
when Request
|
41
|
+
serialize_request object
|
42
|
+
else
|
43
|
+
if object.respond_to?(:acts_like?) && object.acts_like?(:time)
|
44
|
+
serialize_time object
|
45
|
+
else
|
46
|
+
serialize_hash instance_variable_hash(object)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def serialize_base64 string
|
54
|
+
builder.base64 string.base64_encoded
|
55
|
+
end
|
56
|
+
|
57
|
+
def serialize_array array
|
58
|
+
builder.array do |builder|
|
59
|
+
builder.data do |builder|
|
60
|
+
array.each do |element|
|
61
|
+
builder.value do |_|
|
62
|
+
serialize element
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def serialize_string string
|
70
|
+
builder.string string
|
71
|
+
end
|
72
|
+
|
73
|
+
def serialize_true
|
74
|
+
builder.boolean "1"
|
75
|
+
end
|
76
|
+
|
77
|
+
def serialize_false
|
78
|
+
builder.boolean "0"
|
79
|
+
end
|
80
|
+
alias_method :serialize_nil, :serialize_false
|
81
|
+
|
82
|
+
def serialize_float float
|
83
|
+
builder.double float.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
def serialize_big_decimal big_decimal
|
87
|
+
builder.double big_decimal.to_s("F")
|
88
|
+
end
|
89
|
+
|
90
|
+
def serialize_hash hash
|
91
|
+
builder.struct do |builder|
|
92
|
+
hash.each_pair do |key, value|
|
93
|
+
builder.member do |builder|
|
94
|
+
# Get a better string representation of BigDecimals
|
95
|
+
key = key.to_s("F") if BigDecimal === key
|
96
|
+
builder.name key.to_s
|
97
|
+
|
98
|
+
builder.value do |builder|
|
99
|
+
serialize value
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def serialize_integer int
|
107
|
+
builder.int int.to_s
|
108
|
+
end
|
109
|
+
|
110
|
+
def serialize_time time
|
111
|
+
builder.send "dateTime.iso8601", time.iso8601
|
112
|
+
end
|
113
|
+
|
114
|
+
def serialize_request request
|
115
|
+
builder.methodCall do |builder|
|
116
|
+
builder.methodName request.method_name
|
117
|
+
builder.params do |builder|
|
118
|
+
request.arguments.each do |argument|
|
119
|
+
builder.param do |builder|
|
120
|
+
builder.value do |builder|
|
121
|
+
serialize argument
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def to_xml
|
130
|
+
@builder.to_xml
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
def instance_variable_hash object
|
135
|
+
{}.tap do |hash|
|
136
|
+
object.instance_variables.each do |ivar|
|
137
|
+
hash[ivar[1..-1]] = object.instance_variable_get ivar
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class << self
|
143
|
+
def [] object
|
144
|
+
new(object).to_xml
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/rapuncel.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "rapuncel"
|
3
|
-
s.version = "0.0.
|
3
|
+
s.version = "0.0.5.RC1"
|
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'
|
@@ -18,5 +18,6 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.add_dependency 'nokogiri'
|
19
19
|
s.add_dependency 'activesupport', '>= 3.0.0'
|
20
20
|
|
21
|
-
s.add_development_dependency '
|
21
|
+
s.add_development_dependency 'rspec', '>= 2.6.0'
|
22
|
+
s.add_development_dependency 'rake'
|
22
23
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rapuncel::Client do
|
4
|
+
it 'simple XMLRPC' do
|
5
|
+
client = Rapuncel::Client.new :port => 9485
|
6
|
+
proxy = client.proxy_for 'num'
|
7
|
+
|
8
|
+
proxy.add(40, 2).should == 42
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'fault XMLRPC without raising' do
|
12
|
+
client = Rapuncel::Client.new :port => 9485
|
13
|
+
proxy = client.proxy_for 'num'
|
14
|
+
|
15
|
+
lambda do
|
16
|
+
proxy.add 20, 20, 2
|
17
|
+
end.should_not raise_error Rapuncel::Response::Exception
|
18
|
+
|
19
|
+
|
20
|
+
proxy.add(20, 20, 2).should be_a Hash
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'fault XMLRPC with raising' do
|
24
|
+
client = Rapuncel::Client.new :port => 9485, :raise_on => :both
|
25
|
+
proxy = client.proxy_for 'num'
|
26
|
+
|
27
|
+
lambda do
|
28
|
+
proxy.add 20, 20, 2
|
29
|
+
end.should raise_error Rapuncel::Response::Fault
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'error in connection without raising' do
|
33
|
+
client = Rapuncel::Client.new :host => 'www.google.de', :port => 80, :path => '/somefoobarurl'
|
34
|
+
proxy = client.proxy
|
35
|
+
|
36
|
+
lambda do
|
37
|
+
proxy.foo :bar, :baz
|
38
|
+
end.should_not raise_error Rapuncel::Response::Error
|
39
|
+
|
40
|
+
err = proxy.foo(:bar, :baz)
|
41
|
+
err.should be_a Hash
|
42
|
+
err[:http_code].should == 404
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'error in connection with raising' do
|
46
|
+
client = Rapuncel::Client.new :host => 'www.google.de', :port => 80, :path => '/somefoobarurl', :raise_on => :both
|
47
|
+
proxy = client.proxy
|
48
|
+
|
49
|
+
lambda do
|
50
|
+
proxy.foo :bar
|
51
|
+
end.should raise_error Rapuncel::Response::Error
|
52
|
+
end
|
53
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'rapuncel'
|
6
|
+
require 'test_server'
|
7
|
+
|
8
|
+
module SpecHelper
|
9
|
+
def anythings count
|
10
|
+
[anything] * count
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.before :all do
|
16
|
+
@server = TestServer.new
|
17
|
+
@server.start
|
18
|
+
end
|
19
|
+
|
20
|
+
config.after :all do
|
21
|
+
@server.stop
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
String.class_eval do
|
26
|
+
def has_xpath? xpath, options = {}
|
27
|
+
eq = options[:content]
|
28
|
+
count = options[:count] || 1
|
29
|
+
doc = Nokogiri::XML.parse self, nil, nil, Nokogiri::XML::ParseOptions::STRICT
|
30
|
+
res = doc.xpath xpath
|
31
|
+
|
32
|
+
unless eq
|
33
|
+
res.size.should == count
|
34
|
+
else
|
35
|
+
eq = eq.strip
|
36
|
+
|
37
|
+
if count
|
38
|
+
res.to_a.select{ |node|
|
39
|
+
node.text.strip == eq
|
40
|
+
}.size.should == count
|
41
|
+
else
|
42
|
+
res.to_a.all?{ |node|
|
43
|
+
node.text.strip == eq
|
44
|
+
}.should == true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Array do
|
4
|
+
describe "Serialization" do
|
5
|
+
before do
|
6
|
+
@array = (1..10).to_a.map &:to_s
|
7
|
+
|
8
|
+
@xml = Rapuncel::XmlRpcSerializer[@array]
|
9
|
+
end
|
10
|
+
|
11
|
+
it "yields a value tag for each array element" do
|
12
|
+
@xml.should have_xpath('/array/data/value', :count => 10)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "preserves the element order" do
|
16
|
+
@xml.should have_xpath('/array/data/value[4]/string', :content => "4")
|
17
|
+
@xml.should have_xpath('/array/data/value[5]/string', :content => "5")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "Deserialization" do
|
22
|
+
before do
|
23
|
+
@xml = <<-XML
|
24
|
+
<array><data>
|
25
|
+
<value><string>1</string></value>
|
26
|
+
<value><string>2</string></value>
|
27
|
+
<value><string>3</string></value>
|
28
|
+
</data></array>
|
29
|
+
XML
|
30
|
+
@array = Rapuncel::XmlRpcDeserializer[@xml]
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'has correct number of elements' do
|
34
|
+
@array.count.should == 3
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'preserves element order' do
|
38
|
+
@array.should == ["1", "2", "3"]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Base64" do
|
4
|
+
def to_base64 string
|
5
|
+
if RUBY_VERSION =~ /^1\.9/
|
6
|
+
[string].pack('m')
|
7
|
+
else
|
8
|
+
require 'base64'
|
9
|
+
Base64.encode64(string)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should encode Base64 marked strings as Base64' do
|
14
|
+
string = "abcdefghABCDEFGH1234567890".as_base64
|
15
|
+
string.should be_a Rapuncel::Base64String
|
16
|
+
|
17
|
+
xml = Rapuncel::XmlRpcSerializer[string]
|
18
|
+
xml.should have_xpath('/base64', :content => to_base64(string))
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should decode base64 as Base64Strings' do
|
22
|
+
xml = <<-XML
|
23
|
+
<base64>YWJjZGVmZ2hBQkNERUZHSDEyMzQ1Njc4OTA=</base64>
|
24
|
+
XML
|
25
|
+
|
26
|
+
string = Rapuncel::XmlRpcDeserializer[xml]
|
27
|
+
string.should be_a Rapuncel::Base64String
|
28
|
+
string.should == "abcdefghABCDEFGH1234567890"
|
29
|
+
end
|
30
|
+
end
|