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.
Files changed (59) hide show
  1. data/Gemfile +3 -0
  2. data/README.md +35 -24
  3. data/Rakefile +11 -11
  4. data/lib/rapuncel.rb +9 -17
  5. data/lib/rapuncel/adapters/net_http_adapter.rb +7 -5
  6. data/lib/rapuncel/base_64_string.rb +31 -0
  7. data/lib/rapuncel/client.rb +2 -2
  8. data/lib/rapuncel/connection.rb +35 -58
  9. data/lib/rapuncel/request.rb +1 -38
  10. data/lib/rapuncel/response.rb +17 -30
  11. data/lib/rapuncel/xml_rpc_deserializer.rb +110 -0
  12. data/lib/rapuncel/xml_rpc_serializer.rb +148 -0
  13. data/rapuncel.gemspec +3 -2
  14. data/spec/functional/client_spec.rb +53 -0
  15. data/spec/spec_helper.rb +48 -0
  16. data/{test → spec}/test_server.rb +0 -1
  17. data/spec/unit/array_spec.rb +41 -0
  18. data/spec/unit/base64_spec.rb +30 -0
  19. data/spec/unit/boolean_spec.rb +39 -0
  20. data/spec/unit/connection_spec.rb +22 -0
  21. data/spec/unit/float_spec.rb +38 -0
  22. data/spec/unit/hash_spec.rb +54 -0
  23. data/spec/unit/int_spec.rb +23 -0
  24. data/spec/unit/nil_spec.rb +7 -0
  25. data/spec/unit/object_spec.rb +24 -0
  26. data/spec/unit/proxy_spec.rb +29 -0
  27. data/spec/unit/request_spec.rb +28 -0
  28. data/{test/unit/response_test.rb → spec/unit/response_spec.rb} +29 -44
  29. data/spec/unit/string_spec.rb +35 -0
  30. data/spec/unit/time_spec.rb +20 -0
  31. metadata +77 -104
  32. data/lib/rapuncel/base.rb +0 -7
  33. data/lib/rapuncel/core_ext/array.rb +0 -23
  34. data/lib/rapuncel/core_ext/big_decimal.rb +0 -7
  35. data/lib/rapuncel/core_ext/boolean.rb +0 -29
  36. data/lib/rapuncel/core_ext/float.rb +0 -11
  37. data/lib/rapuncel/core_ext/hash.rb +0 -32
  38. data/lib/rapuncel/core_ext/integer.rb +0 -11
  39. data/lib/rapuncel/core_ext/nil.rb +0 -7
  40. data/lib/rapuncel/core_ext/object.rb +0 -49
  41. data/lib/rapuncel/core_ext/string.rb +0 -12
  42. data/lib/rapuncel/core_ext/symbol.rb +0 -7
  43. data/lib/rapuncel/core_ext/time.rb +0 -14
  44. data/rapuncel-0.0.3.gem +0 -0
  45. data/test/functional/client_test.rb +0 -54
  46. data/test/functional_test_helper.rb +0 -13
  47. data/test/test_helper.rb +0 -38
  48. data/test/unit/array_test.rb +0 -97
  49. data/test/unit/boolean_test.rb +0 -34
  50. data/test/unit/connection_test.rb +0 -29
  51. data/test/unit/float_test.rb +0 -23
  52. data/test/unit/hash_test.rb +0 -54
  53. data/test/unit/int_test.rb +0 -27
  54. data/test/unit/nil_test.rb +0 -16
  55. data/test/unit/object_test.rb +0 -83
  56. data/test/unit/proxy_test.rb +0 -52
  57. data/test/unit/request_test.rb +0 -34
  58. data/test/unit/string_test.rb +0 -40
  59. 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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rapuncel"
3
- s.version = "0.0.4"
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 'mocha'
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
@@ -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
@@ -1,6 +1,5 @@
1
1
  require "xmlrpc/server"
2
2
  require 'rubygems'
3
- require 'faker'
4
3
 
5
4
  class Num
6
5
  INTERFACE = XMLRPC::interface("num") {
@@ -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