sekken 0.1.0

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 (176) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.travis.yml +11 -0
  4. data/.yardopts +6 -0
  5. data/CONTRIBUTING.md +46 -0
  6. data/Gemfile +27 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +28 -0
  9. data/Rakefile +21 -0
  10. data/donate.png +0 -0
  11. data/lib/sekken.rb +75 -0
  12. data/lib/sekken/envelope.rb +92 -0
  13. data/lib/sekken/errors.rb +9 -0
  14. data/lib/sekken/example_message.rb +41 -0
  15. data/lib/sekken/httpclient.rb +35 -0
  16. data/lib/sekken/importer.rb +77 -0
  17. data/lib/sekken/message.rb +135 -0
  18. data/lib/sekken/operation.rb +100 -0
  19. data/lib/sekken/resolver.rb +20 -0
  20. data/lib/sekken/response.rb +50 -0
  21. data/lib/sekken/version.rb +5 -0
  22. data/lib/sekken/wsdl.rb +86 -0
  23. data/lib/sekken/wsdl/binding.rb +64 -0
  24. data/lib/sekken/wsdl/binding_operation.rb +85 -0
  25. data/lib/sekken/wsdl/document.rb +83 -0
  26. data/lib/sekken/wsdl/document_collection.rb +61 -0
  27. data/lib/sekken/wsdl/input_output.rb +84 -0
  28. data/lib/sekken/wsdl/message.rb +38 -0
  29. data/lib/sekken/wsdl/operation.rb +47 -0
  30. data/lib/sekken/wsdl/port.rb +29 -0
  31. data/lib/sekken/wsdl/port_type.rb +38 -0
  32. data/lib/sekken/wsdl/port_type_operation.rb +42 -0
  33. data/lib/sekken/wsdl/service.rb +55 -0
  34. data/lib/sekken/xml/attribute.rb +13 -0
  35. data/lib/sekken/xml/element.rb +82 -0
  36. data/lib/sekken/xml/element_builder.rb +220 -0
  37. data/lib/sekken/xs/schema.rb +57 -0
  38. data/lib/sekken/xs/schema_collection.rb +49 -0
  39. data/lib/sekken/xs/types.rb +272 -0
  40. data/sekken.gemspec +42 -0
  41. data/spec/fixtures/response/another_soap_fault.xml +14 -0
  42. data/spec/fixtures/response/authentication.xml +14 -0
  43. data/spec/fixtures/response/header.xml +13 -0
  44. data/spec/fixtures/response/list.xml +18 -0
  45. data/spec/fixtures/response/multi_ref.xml +39 -0
  46. data/spec/fixtures/response/soap_fault.xml +8 -0
  47. data/spec/fixtures/response/soap_fault12.xml +18 -0
  48. data/spec/fixtures/response/taxcloud.xml +1 -0
  49. data/spec/fixtures/wsdl/amazon.wsdl +1920 -0
  50. data/spec/fixtures/wsdl/arrays_with_attributes.wsdl +117 -0
  51. data/spec/fixtures/wsdl/authentication.wsdl +63 -0
  52. data/spec/fixtures/wsdl/awse.wsdl +1510 -0
  53. data/spec/fixtures/wsdl/betfair.wsdl +2981 -0
  54. data/spec/fixtures/wsdl/blz_service.wsdl +88 -0
  55. data/spec/fixtures/wsdl/bookt/bookt.wsdl +11 -0
  56. data/spec/fixtures/wsdl/bookt/bookt0.xsd +433 -0
  57. data/spec/fixtures/wsdl/bookt/bookt1.xsd +42 -0
  58. data/spec/fixtures/wsdl/bookt/bookt10.xsd +38 -0
  59. data/spec/fixtures/wsdl/bookt/bookt11.xsd +17 -0
  60. data/spec/fixtures/wsdl/bookt/bookt12.xsd +18 -0
  61. data/spec/fixtures/wsdl/bookt/bookt13.xsd +22 -0
  62. data/spec/fixtures/wsdl/bookt/bookt14.xsd +27 -0
  63. data/spec/fixtures/wsdl/bookt/bookt15.xsd +28 -0
  64. data/spec/fixtures/wsdl/bookt/bookt2.wsdl +243 -0
  65. data/spec/fixtures/wsdl/bookt/bookt2.xsd +81 -0
  66. data/spec/fixtures/wsdl/bookt/bookt3.wsdl +286 -0
  67. data/spec/fixtures/wsdl/bookt/bookt3.xsd +61 -0
  68. data/spec/fixtures/wsdl/bookt/bookt4.xsd +35 -0
  69. data/spec/fixtures/wsdl/bookt/bookt5.xsd +22 -0
  70. data/spec/fixtures/wsdl/bookt/bookt6.xsd +26 -0
  71. data/spec/fixtures/wsdl/bookt/bookt7.xsd +18 -0
  72. data/spec/fixtures/wsdl/bookt/bookt8.xsd +22 -0
  73. data/spec/fixtures/wsdl/bookt/bookt9.xsd +29 -0
  74. data/spec/fixtures/wsdl/bronto.wsdl +3285 -0
  75. data/spec/fixtures/wsdl/bydexchange/bydexchange.wsdl +104 -0
  76. data/spec/fixtures/wsdl/bydexchange/bydexchange0.xsd +1 -0
  77. data/spec/fixtures/wsdl/bydexchange/bydexchange1.xsd +1 -0
  78. data/spec/fixtures/wsdl/bydexchange/bydexchange2.wsdl +119 -0
  79. data/spec/fixtures/wsdl/bydexchange/bydexchange2.xsd +1 -0
  80. data/spec/fixtures/wsdl/bydexchange/bydexchange3.xsd +1 -0
  81. data/spec/fixtures/wsdl/bydexchange/bydexchange4.xsd +1 -0
  82. data/spec/fixtures/wsdl/bydexchange/bydexchange5.xsd +1 -0
  83. data/spec/fixtures/wsdl/bydexchange/bydexchange6.xsd +1 -0
  84. data/spec/fixtures/wsdl/bydexchange/bydexchange7.xsd +1 -0
  85. data/spec/fixtures/wsdl/bydexchange/bydexchange8.xsd +1 -0
  86. data/spec/fixtures/wsdl/crowd.wsdl +2437 -0
  87. data/spec/fixtures/wsdl/data_exchange.wsdl +98 -0
  88. data/spec/fixtures/wsdl/document_literal_wrapped.wsdl +153 -0
  89. data/spec/fixtures/wsdl/economic.wsdl +65660 -0
  90. data/spec/fixtures/wsdl/edialog.wsdl +13148 -0
  91. data/spec/fixtures/wsdl/email_verification.wsdl +394 -0
  92. data/spec/fixtures/wsdl/equifax.wsdl +794 -0
  93. data/spec/fixtures/wsdl/geotrust.wsdl +156 -0
  94. data/spec/fixtures/wsdl/interhome.wsdl +2137 -0
  95. data/spec/fixtures/wsdl/iws.wsdl +695 -0
  96. data/spec/fixtures/wsdl/jetairways.wsdl +156 -0
  97. data/spec/fixtures/wsdl/jira.wsdl +3890 -0
  98. data/spec/fixtures/wsdl/juniper.wsdl +215 -0
  99. data/spec/fixtures/wsdl/namespaced_actions.wsdl +307 -0
  100. data/spec/fixtures/wsdl/oracle.wsdl +3629 -0
  101. data/spec/fixtures/wsdl/ratp.wsdl +955 -0
  102. data/spec/fixtures/wsdl/rio2/rio2.wsdl +74 -0
  103. data/spec/fixtures/wsdl/rio2/rio2_0.wsdl +55 -0
  104. data/spec/fixtures/wsdl/rio2/rio2_0.xsd +58 -0
  105. data/spec/fixtures/wsdl/rio2/rio2_1.xsd +41 -0
  106. data/spec/fixtures/wsdl/rio2/rio2_2.xsd +222 -0
  107. data/spec/fixtures/wsdl/rio2/rio2_3.xsd +10 -0
  108. data/spec/fixtures/wsdl/rpc_literal.wsdl +105 -0
  109. data/spec/fixtures/wsdl/spyne.wsdl +70 -0
  110. data/spec/fixtures/wsdl/stockquote.wsdl +124 -0
  111. data/spec/fixtures/wsdl/taxcloud.wsdl +934 -0
  112. data/spec/fixtures/wsdl/team_software/team_software.wsdl +542 -0
  113. data/spec/fixtures/wsdl/team_software/team_software0.xsd +448 -0
  114. data/spec/fixtures/wsdl/team_software/team_software1.xsd +41 -0
  115. data/spec/fixtures/wsdl/team_software/team_software2.xsd +258 -0
  116. data/spec/fixtures/wsdl/team_software/team_software3.xsd +14 -0
  117. data/spec/fixtures/wsdl/telefonkatalogen.wsdl +45 -0
  118. data/spec/fixtures/wsdl/temperature.wsdl +136 -0
  119. data/spec/fixtures/wsdl/wasmuth/wasmuth.wsdl +157 -0
  120. data/spec/fixtures/wsdl/wasmuth/wasmuth1.xsd +210 -0
  121. data/spec/fixtures/wsdl/wasmuth/wasmuth2.xsd +549 -0
  122. data/spec/fixtures/wsdl/xignite.wsdl +3470 -0
  123. data/spec/fixtures/wsdl/yahoo.wsdl +2425 -0
  124. data/spec/fixtures/wsdl/zanox_export_service.xml +520 -0
  125. data/spec/integration/amazon_spec.rb +51 -0
  126. data/spec/integration/authentication_spec.rb +38 -0
  127. data/spec/integration/awse_spec.rb +95 -0
  128. data/spec/integration/betfair_spec.rb +179 -0
  129. data/spec/integration/blz_service_spec.rb +46 -0
  130. data/spec/integration/bookt_spec.rb +59 -0
  131. data/spec/integration/bronto_spec.rb +178 -0
  132. data/spec/integration/bydexchange_spec.rb +40 -0
  133. data/spec/integration/crowd_spec.rb +44 -0
  134. data/spec/integration/data_exchange_spec.rb +27 -0
  135. data/spec/integration/document_literal_spec.rb +104 -0
  136. data/spec/integration/economic_spec.rb +64 -0
  137. data/spec/integration/email_verification_spec.rb +82 -0
  138. data/spec/integration/equifax_spec.rb +216 -0
  139. data/spec/integration/geotrust_spec.rb +17 -0
  140. data/spec/integration/interhome_spec.rb +156 -0
  141. data/spec/integration/iws_spec.rb +27 -0
  142. data/spec/integration/jetairways_spec.rb +142 -0
  143. data/spec/integration/jira_spec.rb +27 -0
  144. data/spec/integration/juniper_spec.rb +20 -0
  145. data/spec/integration/namespaced_actions_spec.rb +37 -0
  146. data/spec/integration/oracle_spec.rb +61 -0
  147. data/spec/integration/ratp_spec.rb +178 -0
  148. data/spec/integration/rio2_spec.rb +56 -0
  149. data/spec/integration/rpc_literal_spec.rb +101 -0
  150. data/spec/integration/spyne_spec.rb +60 -0
  151. data/spec/integration/stockquote_spec.rb +46 -0
  152. data/spec/integration/taxcloud_spec.rb +45 -0
  153. data/spec/integration/team_software_spec.rb +51 -0
  154. data/spec/integration/telefonkatalogen_spec.rb +42 -0
  155. data/spec/integration/temperature_spec.rb +71 -0
  156. data/spec/integration/wasmuth_spec.rb +47 -0
  157. data/spec/integration/xignite_spec.rb +112 -0
  158. data/spec/integration/yahoo_spec.rb +116 -0
  159. data/spec/sekken/httpclient_spec.rb +41 -0
  160. data/spec/sekken/operation/build_spec.rb +308 -0
  161. data/spec/sekken/operation/document_literal_spec.rb +53 -0
  162. data/spec/sekken/operation/example_body_spec.rb +95 -0
  163. data/spec/sekken/operation/rpc_literal_spec.rb +50 -0
  164. data/spec/sekken/operation_spec.rb +169 -0
  165. data/spec/sekken/resolver_spec.rb +38 -0
  166. data/spec/sekken/response_spec.rb +17 -0
  167. data/spec/sekken/wsdl/document_spec.rb +191 -0
  168. data/spec/sekken/wsdl_spec.rb +46 -0
  169. data/spec/sekken/xs/complex_type_spec.rb +197 -0
  170. data/spec/sekken/xs/element_spec.rb +82 -0
  171. data/spec/sekken/xs/simple_type_spec.rb +39 -0
  172. data/spec/sekken_spec.rb +128 -0
  173. data/spec/spec_helper.rb +37 -0
  174. data/spec/support/fixture.rb +19 -0
  175. data/spec/support/http_mock.rb +43 -0
  176. metadata +344 -0
@@ -0,0 +1,29 @@
1
+ class Sekken
2
+ class WSDL
3
+ class Port
4
+
5
+ def initialize(port_node, soap_node)
6
+ @name = port_node['name']
7
+ @binding = port_node['binding']
8
+
9
+ @type = soap_node.namespace.href
10
+ @location = soap_node['location']
11
+ end
12
+
13
+ attr_reader :name, :binding, :type, :location
14
+
15
+ def fetch_binding(documents)
16
+ binding_local = @binding.split(':').last
17
+
18
+ documents.bindings.fetch(binding_local) {
19
+ raise "Unable to find binding #{binding_local.inspect} for port #{@name.inspect}"
20
+ }
21
+ end
22
+
23
+ def to_hash
24
+ { name => { type: type, location: location } }
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,38 @@
1
+ require 'sekken/wsdl/port_type_operation'
2
+
3
+ class Sekken
4
+ class WSDL
5
+ class PortType
6
+
7
+ def initialize(port_type_node)
8
+ @port_type_node = port_type_node
9
+ end
10
+
11
+ def name
12
+ @port_type_node['name']
13
+ end
14
+
15
+ def operations
16
+ @operations ||= operations!
17
+ end
18
+
19
+ private
20
+
21
+ def operations!
22
+ operations = {}
23
+
24
+ @port_type_node.element_children.each do |operation_node|
25
+ next unless operation_node.name == 'operation'
26
+
27
+ operation_name = operation_node['name']
28
+ operation = PortTypeOperation.new(operation_node)
29
+
30
+ operations[operation_name] = operation
31
+ end
32
+
33
+ operations
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,42 @@
1
+ class Sekken
2
+ class WSDL
3
+ class PortTypeOperation
4
+
5
+ def initialize(operation_node)
6
+ @operation_node = operation_node
7
+
8
+ @name = operation_node['name']
9
+ @input_node = find_node('input')
10
+ @output_node = find_node('output')
11
+ end
12
+
13
+ attr_reader :name
14
+
15
+ def input
16
+ return @input if defined? @input
17
+ @input = parse_node(@input_node)
18
+ end
19
+
20
+ def output
21
+ return @output if defined? @output
22
+ @output = parse_node(@output_node)
23
+ end
24
+
25
+ private
26
+
27
+ def find_node(node_name)
28
+ @operation_node.element_children.find { |node| node.name == node_name }
29
+ end
30
+
31
+ def parse_node(node)
32
+ input = {}
33
+
34
+ input[:name] = node['name']
35
+ input[:message] = node['message']
36
+
37
+ input
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,55 @@
1
+ require 'sekken/wsdl/port'
2
+
3
+ class Sekken
4
+ class WSDL
5
+ class Service
6
+
7
+ def initialize(service_node)
8
+ @service_node = service_node
9
+ end
10
+
11
+ def name
12
+ @service_node['name']
13
+ end
14
+
15
+ def ports
16
+ @ports ||= ports!
17
+ end
18
+
19
+ def to_hash
20
+ port_hash = ports.values.inject({}) { |memo, port| memo.merge port.to_hash }
21
+ { name => { ports: port_hash } }
22
+ end
23
+
24
+ private
25
+
26
+ def ports!
27
+ ports = {}
28
+
29
+ @service_node.element_children.each do |port_node|
30
+ next unless port_node.name == 'port'
31
+
32
+ soap_node = port_node.element_children.find { |node|
33
+ namespace = node.namespace.href
34
+
35
+ soap_1_1 = namespace == Sekken::NS_SOAP_1_1
36
+ soap_1_2 = namespace == Sekken::NS_SOAP_1_2
37
+ address = node.name == 'address'
38
+
39
+ (soap_1_1 || soap_1_2) && address
40
+ }
41
+
42
+ next unless soap_node
43
+
44
+ port_name = port_node['name']
45
+ port = Port.new(port_node, soap_node)
46
+
47
+ ports[port_name] = port
48
+ end
49
+
50
+ ports
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,13 @@
1
+ class Sekken
2
+ class XML
3
+ class Attribute
4
+
5
+ attr_accessor :name, :base_type, :use
6
+
7
+ def optional?
8
+ use == 'optional'
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,82 @@
1
+ class Sekken
2
+ class XML
3
+ class Element
4
+
5
+ def initialize
6
+ @children = []
7
+ @attributes = {}
8
+ @recursive = false
9
+ @singular = true
10
+ end
11
+
12
+ attr_accessor :parent, :name, :namespace, :form
13
+
14
+ # Public: Whether this element is a simple type.
15
+ def simple_type?
16
+ !!base_type
17
+ end
18
+
19
+ # Public: Whether this element is a complex type.
20
+ def complex_type?
21
+ !simple_type?
22
+ end
23
+
24
+ # Public: The base name for a simple type element.
25
+ attr_accessor :base_type
26
+
27
+ # Public: Accessor for whether this is a single element.
28
+ attr_accessor :singular
29
+ alias_method :singular?, :singular
30
+
31
+ # Public: Whether or not this element's type is defined recursively,
32
+ # meaning one of this element's parents is of the same type as this element.
33
+ def recursive?
34
+ !!recursive_type
35
+ end
36
+
37
+ # Public: The name of the recursive type definition if any.
38
+ attr_accessor :recursive_type
39
+
40
+ # Private: The complex type id for this element to track recursive type definitions.
41
+ attr_accessor :complex_type_id
42
+
43
+ # Public: The child elements.
44
+ attr_accessor :children
45
+
46
+ # Public: The attributes.
47
+ attr_accessor :attributes
48
+
49
+ # Public: Returns this element and its children as an Array of Hashes for inspection.
50
+ def to_a(memo = [], stack = [])
51
+ new_stack = stack + [name]
52
+ data = { namespace: namespace, form: form, singular: singular? }
53
+
54
+ unless attributes.empty?
55
+ data[:attributes] = attributes.each_with_object({}) do |attribute, memo|
56
+ memo[attribute.name] = { optional: attribute.optional? }
57
+ end
58
+ end
59
+
60
+ if recursive?
61
+ data[:recursive_type] = recursive_type
62
+ memo << [new_stack, data]
63
+
64
+ elsif simple_type?
65
+ data[:type] = base_type
66
+ memo << [new_stack, data]
67
+
68
+ elsif complex_type?
69
+ memo << [new_stack, data]
70
+
71
+ children.each do |child|
72
+ child.to_a(memo, new_stack)
73
+ end
74
+
75
+ end
76
+
77
+ memo
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,220 @@
1
+ require 'sekken/xml/element'
2
+ require 'sekken/xml/attribute'
3
+
4
+ class Sekken
5
+ class XML
6
+ class ElementBuilder
7
+
8
+ def initialize(schemas)
9
+ @logger = Logging.logger[self]
10
+ @schemas = schemas
11
+ end
12
+
13
+ def build(parts)
14
+ parts.map { |part|
15
+ case
16
+ when part[:type] then build_type_element(part)
17
+ when part[:element] then build_element(part)
18
+ end
19
+ }.compact
20
+ end
21
+
22
+ private
23
+
24
+ # Private: Expects a part with a @type attribute, resolves the type
25
+ # and returns an Element with that type.
26
+ def build_type_element(part)
27
+ type = find_type part[:type], part[:namespaces]
28
+
29
+ element = Element.new
30
+ element.name = part[:name]
31
+ element.form = 'unqualified'
32
+
33
+ handle_type(element, type)
34
+ element
35
+ end
36
+
37
+ # Private: Expects a part with an @element attribute, resolves the element
38
+ # and its type and returns an Element with that type.
39
+ def build_element(part)
40
+ local, namespace = expand_qname(part[:element], part[:namespaces])
41
+ schema = @schemas.find_by_namespace(namespace)
42
+ raise "Unable to find schema for #{namespace.inspect}" unless schema
43
+
44
+ xs_element = schema.elements.fetch(local)
45
+ type = find_type_for_element(xs_element)
46
+
47
+ element = Element.new
48
+ element.name = xs_element.name
49
+ element.form = 'qualified'
50
+ element.namespace = namespace
51
+
52
+ handle_type(element, type)
53
+ element
54
+ end
55
+
56
+ def handle_type(element, type)
57
+ case type
58
+
59
+ when XS::ComplexType
60
+ element.complex_type_id = type.id
61
+ element.children = child_elements(element, type)
62
+ element.attributes = element_attributes(type)
63
+
64
+ when XS::SimpleType
65
+ element.base_type = type.base
66
+
67
+ when String
68
+ element.base_type = type
69
+
70
+ end
71
+ end
72
+
73
+ def handle_simple_type(attribute, type)
74
+ case type
75
+ when XS::SimpleType then attribute.base_type = type.base
76
+ when String then attribute.base_type = type
77
+ end
78
+ end
79
+
80
+ def element_attributes(type)
81
+ type.attributes.map { |attribute|
82
+ attr = Attribute.new
83
+
84
+ if attribute.ref
85
+ local, namespace = expand_qname(attribute.ref, attribute.namespaces)
86
+ schema = find_schema(namespace)
87
+
88
+ if schema
89
+ attribute = schema.attributes[local]
90
+ else
91
+ @logger.debug("Unable to find schema for attribute@ref #{attribute.ref.inspect}")
92
+ next
93
+ end
94
+ end
95
+
96
+ type = find_type_for_attribute(attribute)
97
+ handle_simple_type(attr, type)
98
+
99
+ attr.name = attribute.name
100
+ attr.use = attribute.use
101
+
102
+ attr
103
+ }.compact
104
+ end
105
+
106
+ def child_elements(parent, type)
107
+ type.elements.map { |child_element|
108
+ el = Element.new
109
+ el.parent = parent
110
+
111
+ max_occurs = child_element['maxOccurs'].to_s
112
+ el.singular = max_occurs.empty? || max_occurs == '1'
113
+
114
+ if child_element.ref
115
+ child_element = find_element(child_element.ref, child_element.namespaces)
116
+ el.form = 'qualified'
117
+ else
118
+ el.form = child_element.form
119
+ end
120
+
121
+ el.name = child_element.name
122
+ el.namespace = child_element.namespace
123
+
124
+ # prevent recursion
125
+ if recursive_child_definition? parent, child_element
126
+ el.recursive_type = child_element.type
127
+ else
128
+ type = find_type_for_element(child_element)
129
+ handle_type(el, type)
130
+ end
131
+
132
+ el
133
+ }
134
+ end
135
+
136
+ # Private: Accepts an Element and figures out if its type is already defined in this
137
+ # elements (self) ancestors. Used to prevent recursive child element definitions.
138
+ def recursive_child_definition?(parent, element)
139
+ return false unless element.type
140
+
141
+ local, namespace = expand_qname(element.type, element.namespaces)
142
+ id = [namespace, local].join(':')
143
+
144
+ current_parent = parent
145
+
146
+ while current_parent
147
+ return true if current_parent.complex_type_id == id
148
+ current_parent = current_parent.parent
149
+ end
150
+
151
+ false
152
+ end
153
+
154
+ def find_type_for_element(element)
155
+ if element.type
156
+ find_type(element.type, element.namespaces)
157
+ else
158
+ element.inline_type
159
+ end
160
+ end
161
+
162
+ alias_method :find_type_for_attribute, :find_type_for_element
163
+
164
+ def find_type(qname, namespaces)
165
+ local, namespace = expand_qname(qname, namespaces)
166
+
167
+ # assume built-in or unknown type for unqualified type qnames for now.
168
+ # we could fallback to the element's default namespace. needs tests.
169
+ return qname unless namespace
170
+
171
+ schema = find_schema(namespace)
172
+
173
+ # custom type
174
+ if schema
175
+
176
+ # complex type
177
+ if complex_type = schema.complex_types[local]
178
+ complex_type
179
+
180
+ # simple type
181
+ elsif simple_type = schema.simple_types[local]
182
+ simple_type
183
+
184
+ end
185
+
186
+ # built-in or unknown type
187
+ else
188
+ qname
189
+
190
+ end
191
+ end
192
+
193
+ def find_element(qname, namespaces)
194
+ local, namespace = expand_qname(qname, namespaces)
195
+ @schemas.element(namespace, local)
196
+ end
197
+
198
+ def find_attribute(qname, namespaces)
199
+ local, namespace = expand_qname(qname, namespaces)
200
+ @schemas.attribute(namespace, local)
201
+ end
202
+
203
+ def find_schema(namespace)
204
+ @schemas.find_by_namespace(namespace)
205
+ end
206
+
207
+ def split_qname(qname)
208
+ qname.split(':').reverse
209
+ end
210
+
211
+ def expand_qname(qname, namespaces)
212
+ local, nsid = split_qname(qname)
213
+ namespace = namespaces["xmlns:#{nsid}"]
214
+
215
+ [local, namespace]
216
+ end
217
+
218
+ end
219
+ end
220
+ end