shale 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/README.md +200 -21
  4. data/exe/shaleb +108 -36
  5. data/lib/shale/adapter/nokogiri/document.rb +87 -0
  6. data/lib/shale/adapter/nokogiri/node.rb +100 -0
  7. data/lib/shale/adapter/nokogiri.rb +11 -151
  8. data/lib/shale/adapter/ox/document.rb +80 -0
  9. data/lib/shale/adapter/ox/node.rb +88 -0
  10. data/lib/shale/adapter/ox.rb +9 -134
  11. data/lib/shale/adapter/rexml/document.rb +88 -0
  12. data/lib/shale/adapter/rexml/node.rb +99 -0
  13. data/lib/shale/adapter/rexml.rb +9 -150
  14. data/lib/shale/attribute.rb +6 -0
  15. data/lib/shale/error.rb +39 -0
  16. data/lib/shale/mapper.rb +8 -6
  17. data/lib/shale/schema/compiler/boolean.rb +21 -0
  18. data/lib/shale/schema/compiler/complex.rb +88 -0
  19. data/lib/shale/schema/compiler/date.rb +21 -0
  20. data/lib/shale/schema/compiler/float.rb +21 -0
  21. data/lib/shale/schema/compiler/integer.rb +21 -0
  22. data/lib/shale/schema/compiler/property.rb +70 -0
  23. data/lib/shale/schema/compiler/string.rb +21 -0
  24. data/lib/shale/schema/compiler/time.rb +21 -0
  25. data/lib/shale/schema/compiler/value.rb +21 -0
  26. data/lib/shale/schema/compiler/xml_complex.rb +50 -0
  27. data/lib/shale/schema/compiler/xml_property.rb +73 -0
  28. data/lib/shale/schema/json_compiler.rb +331 -0
  29. data/lib/shale/schema/{json → json_generator}/base.rb +2 -2
  30. data/lib/shale/schema/{json → json_generator}/boolean.rb +1 -1
  31. data/lib/shale/schema/{json → json_generator}/collection.rb +2 -2
  32. data/lib/shale/schema/{json → json_generator}/date.rb +1 -1
  33. data/lib/shale/schema/{json → json_generator}/float.rb +1 -1
  34. data/lib/shale/schema/{json → json_generator}/integer.rb +1 -1
  35. data/lib/shale/schema/{json → json_generator}/object.rb +5 -2
  36. data/lib/shale/schema/{json → json_generator}/ref.rb +1 -1
  37. data/lib/shale/schema/{json → json_generator}/schema.rb +7 -5
  38. data/lib/shale/schema/{json → json_generator}/string.rb +1 -1
  39. data/lib/shale/schema/{json → json_generator}/time.rb +1 -1
  40. data/lib/shale/schema/json_generator/value.rb +23 -0
  41. data/lib/shale/schema/{json.rb → json_generator.rb} +36 -36
  42. data/lib/shale/schema/xml_compiler.rb +919 -0
  43. data/lib/shale/schema/{xml → xml_generator}/attribute.rb +1 -1
  44. data/lib/shale/schema/{xml → xml_generator}/complex_type.rb +5 -2
  45. data/lib/shale/schema/{xml → xml_generator}/element.rb +1 -1
  46. data/lib/shale/schema/{xml → xml_generator}/import.rb +1 -1
  47. data/lib/shale/schema/{xml → xml_generator}/ref_attribute.rb +1 -1
  48. data/lib/shale/schema/{xml → xml_generator}/ref_element.rb +1 -1
  49. data/lib/shale/schema/{xml → xml_generator}/schema.rb +5 -5
  50. data/lib/shale/schema/{xml → xml_generator}/typed_attribute.rb +1 -1
  51. data/lib/shale/schema/{xml → xml_generator}/typed_element.rb +1 -1
  52. data/lib/shale/schema/{xml.rb → xml_generator.rb} +25 -26
  53. data/lib/shale/schema.rb +44 -5
  54. data/lib/shale/type/{composite.rb → complex.rb} +34 -22
  55. data/lib/shale/utils.rb +42 -7
  56. data/lib/shale/version.rb +1 -1
  57. data/lib/shale.rb +8 -19
  58. data/shale.gemspec +1 -1
  59. metadata +47 -27
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shale
4
+ module Adapter
5
+ module Nokogiri
6
+ # Wrapper around Nokogiri::XML::Node API
7
+ #
8
+ # @api private
9
+ class Node
10
+ # Initialize object with Nokogiri node
11
+ #
12
+ # @param [::Nokogiri::XML::Node] node Nokogiri node
13
+ #
14
+ # @api private
15
+ def initialize(node)
16
+ @node = node
17
+ end
18
+
19
+ # Return namespaces defined on document
20
+ #
21
+ # @return [Hash<String, String>]
22
+ #
23
+ # @example
24
+ # node.namespaces # => { 'foo' => 'http://foo.com', 'bar' => 'http://bar.com' }
25
+ #
26
+ # @api private
27
+ def namespaces
28
+ @node.namespaces.transform_keys { |e| e.sub('xmlns:', '') }
29
+ end
30
+
31
+ # Return name of the node in the format of
32
+ # namespace:name when the node is namespaced or just name when it's not
33
+ #
34
+ # @return [String]
35
+ #
36
+ # @example without namespace
37
+ # node.name # => Bar
38
+ #
39
+ # @example with namespace
40
+ # node.name # => http://foo:Bar
41
+ #
42
+ # @api private
43
+ def name
44
+ [@node.namespace&.href, @node.name].compact.join(':')
45
+ end
46
+
47
+ # Return all attributes associated with the node
48
+ #
49
+ # @return [Hash]
50
+ #
51
+ # @api private
52
+ def attributes
53
+ @node.attribute_nodes.each_with_object({}) do |node, hash|
54
+ name = [node.namespace&.href, node.name].compact.join(':')
55
+ hash[name] = node.value
56
+ end
57
+ end
58
+
59
+ # Return node's parent
60
+ #
61
+ # @return [Shale::Adapter::Nokogiri::Node, nil]
62
+ #
63
+ # @api private
64
+ def parent
65
+ if @node.respond_to?(:parent) && @node.parent && @node.parent.name != 'document'
66
+ self.class.new(@node.parent)
67
+ end
68
+ end
69
+
70
+ # Return node's element children
71
+ #
72
+ # @return [Array<Shale::Adapter::Nokogiri::Node>]
73
+ #
74
+ # @api private
75
+ def children
76
+ @node
77
+ .children
78
+ .to_a
79
+ .filter(&:element?)
80
+ .map { |e| self.class.new(e) }
81
+ end
82
+
83
+ # Return first text child of a node
84
+ #
85
+ # @return [String]
86
+ #
87
+ # @api private
88
+ def text
89
+ first = @node
90
+ .children
91
+ .to_a
92
+ .filter(&:text?)
93
+ .first
94
+
95
+ first&.text
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -2,6 +2,10 @@
2
2
 
3
3
  require 'nokogiri'
4
4
 
5
+ require_relative '../error'
6
+ require_relative 'nokogiri/document'
7
+ require_relative 'nokogiri/node'
8
+
5
9
  module Shale
6
10
  module Adapter
7
11
  # Nokogiri adapter
@@ -12,7 +16,9 @@ module Shale
12
16
  #
13
17
  # @param [String] xml XML document
14
18
  #
15
- # @return [::Nokogiri::XML::Document]
19
+ # @raise [ParseError] when XML document has errors
20
+ #
21
+ # @return [Shale::Adapter::Nokogiri::Node]
16
22
  #
17
23
  # @api private
18
24
  def self.load(xml)
@@ -20,6 +26,10 @@ module Shale
20
26
  config.noblanks
21
27
  end
22
28
 
29
+ unless doc.errors.empty?
30
+ raise ParseError, "Document is invalid: #{doc.errors}"
31
+ end
32
+
23
33
  Node.new(doc.root)
24
34
  end
25
35
 
@@ -57,156 +67,6 @@ module Shale
57
67
  def self.create_document
58
68
  Document.new
59
69
  end
60
-
61
- # Wrapper around Nokogiri API
62
- #
63
- # @api private
64
- class Document
65
- # Initialize object
66
- #
67
- # @api private
68
- def initialize
69
- @doc = ::Nokogiri::XML::Document.new
70
- @namespaces = {}
71
- end
72
-
73
- # Return Nokogiri document
74
- #
75
- # @return [::Nokogiri::XML::Document]
76
- #
77
- # @api private
78
- def doc
79
- if @doc.root
80
- @namespaces.each do |prefix, namespace|
81
- @doc.root.add_namespace(prefix, namespace)
82
- end
83
- end
84
-
85
- @doc
86
- end
87
-
88
- # Create Nokogiri element
89
- #
90
- # @param [String] name Name of the XML element
91
- #
92
- # @return [::Nokogiri::XML::Element]
93
- #
94
- # @api private
95
- def create_element(name)
96
- ::Nokogiri::XML::Element.new(name, @doc)
97
- end
98
-
99
- # Add XML namespace to document
100
- #
101
- # @param [String] prefix
102
- # @param [String] namespace
103
- #
104
- # @api private
105
- def add_namespace(prefix, namespace)
106
- @namespaces[prefix] = namespace if prefix && namespace
107
- end
108
-
109
- # Add attribute to Nokogiri element
110
- #
111
- # @param [::Nokogiri::XML::Element] element Nokogiri element
112
- # @param [String] name Name of the XML attribute
113
- # @param [String] value Value of the XML attribute
114
- #
115
- # @api private
116
- def add_attribute(element, name, value)
117
- element[name] = value
118
- end
119
-
120
- # Add child element to Nokogiri element
121
- #
122
- # @param [::Nokogiri::XML::Element] element Nokogiri parent element
123
- # @param [::Nokogiri::XML::Element] child Nokogiri child element
124
- #
125
- # @api private
126
- def add_element(element, child)
127
- element.add_child(child)
128
- end
129
-
130
- # Add text node to Nokogiri element
131
- #
132
- # @param [::Nokogiri::XML::Element] element Nokogiri element
133
- # @param [String] text Text to add
134
- #
135
- # @api private
136
- def add_text(element, text)
137
- element.content = text
138
- end
139
- end
140
-
141
- # Wrapper around Nokogiri::XML::Node API
142
- #
143
- # @api private
144
- class Node
145
- # Initialize object with Nokogiri node
146
- #
147
- # @param [::Nokogiri::XML::Node] node Nokogiri node
148
- #
149
- # @api private
150
- def initialize(node)
151
- @node = node
152
- end
153
-
154
- # Return name of the node in the format of
155
- # namespace:name when the node is namespaced or just name when it's not
156
- #
157
- # @return [String]
158
- #
159
- # @example without namespace
160
- # node.name # => Bar
161
- #
162
- # @example with namespace
163
- # node.name # => http://foo:Bar
164
- #
165
- # @api private
166
- def name
167
- [@node.namespace&.href, @node.name].compact.join(':')
168
- end
169
-
170
- # Return all attributes associated with the node
171
- #
172
- # @return [Hash]
173
- #
174
- # @api private
175
- def attributes
176
- @node.attribute_nodes.each_with_object({}) do |node, hash|
177
- name = [node.namespace&.href, node.name].compact.join(':')
178
- hash[name] = node.value
179
- end
180
- end
181
-
182
- # Return node's element children
183
- #
184
- # @return [Array<Shale::Adapter::Nokogiri::Node>]
185
- #
186
- # @api private
187
- def children
188
- @node
189
- .children
190
- .to_a
191
- .filter(&:element?)
192
- .map { |e| self.class.new(e) }
193
- end
194
-
195
- # Return first text child of a node
196
- #
197
- # @return [String]
198
- #
199
- # @api private
200
- def text
201
- first = @node
202
- .children
203
- .to_a
204
- .filter(&:text?)
205
- .first
206
-
207
- first&.text
208
- end
209
- end
210
70
  end
211
71
  end
212
72
  end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shale
4
+ module Adapter
5
+ module Ox
6
+ # Wrapper around Ox API
7
+ #
8
+ # @api private
9
+ class Document
10
+ # Return Ox document
11
+ #
12
+ # @return [::Ox::Document]
13
+ #
14
+ # @api private
15
+ attr_reader :doc
16
+
17
+ # Initialize object
18
+ #
19
+ # @api private
20
+ def initialize
21
+ @doc = ::Ox::Document.new
22
+ end
23
+
24
+ # Create Ox element
25
+ #
26
+ # @param [String] name Name of the XML element
27
+ #
28
+ # @return [::Ox::Element]
29
+ #
30
+ # @api private
31
+ def create_element(name)
32
+ ::Ox::Element.new(name)
33
+ end
34
+
35
+ # Add XML namespace to document
36
+ #
37
+ # Ox doesn't support XML namespaces so this method does nothing.
38
+ #
39
+ # @param [String] prefix
40
+ # @param [String] namespace
41
+ #
42
+ # @api private
43
+ def add_namespace(prefix, namespace)
44
+ # :noop:
45
+ end
46
+
47
+ # Add attribute to Ox element
48
+ #
49
+ # @param [::Ox::Element] element Ox element
50
+ # @param [String] name Name of the XML attribute
51
+ # @param [String] value Value of the XML attribute
52
+ #
53
+ # @api private
54
+ def add_attribute(element, name, value)
55
+ element[name] = value
56
+ end
57
+
58
+ # Add child element to Ox element
59
+ #
60
+ # @param [::Ox::Element] element Ox parent element
61
+ # @param [::Ox::Element] child Ox child element
62
+ #
63
+ # @api private
64
+ def add_element(element, child)
65
+ element << child
66
+ end
67
+
68
+ # Add text node to Ox element
69
+ #
70
+ # @param [::Ox::Element] element Ox element
71
+ # @param [String] text Text to add
72
+ #
73
+ # @api private
74
+ def add_text(element, text)
75
+ element << text
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shale
4
+ module Adapter
5
+ module Ox
6
+ # Wrapper around Ox::Element API
7
+ #
8
+ # @api private
9
+ class Node
10
+ # Initialize object with Ox element
11
+ #
12
+ # @param [::Ox::Element] node Ox element
13
+ #
14
+ # @api private
15
+ def initialize(node)
16
+ @node = node
17
+ end
18
+
19
+ # Return namespaces defined on document (Not supported by Ox)
20
+ #
21
+ # @return [Hash<String, String>]
22
+ #
23
+ # @example
24
+ # node.namespaces # => {}
25
+ #
26
+ # @api private
27
+ def namespaces
28
+ {}
29
+ end
30
+
31
+ # Return name of the node in the format of
32
+ # prefix:name when the node is namespaced or just name when it's not
33
+ #
34
+ # @return [String]
35
+ #
36
+ # @example without namespace
37
+ # node.name # => Bar
38
+ #
39
+ # @example with namespace
40
+ # node.name # => foo:Bar
41
+ #
42
+ # @api private
43
+ def name
44
+ @node.name
45
+ end
46
+
47
+ # Return all attributes associated with the node
48
+ #
49
+ # @return [Hash]
50
+ #
51
+ # @api private
52
+ def attributes
53
+ @node.attributes
54
+ end
55
+
56
+ # Return node's parent (Not supported by OX)
57
+ #
58
+ # @return [nil]
59
+ #
60
+ # @api private
61
+ def parent
62
+ # :noop:
63
+ end
64
+
65
+ # Return node's element children
66
+ #
67
+ # @return [Array<Shale::Adapter::Ox::Node>]
68
+ #
69
+ # @api private
70
+ def children
71
+ @node
72
+ .nodes
73
+ .filter { |e| e.is_a?(::Ox::Element) }
74
+ .map { |e| self.class.new(e) }
75
+ end
76
+
77
+ # Return first text child of a node
78
+ #
79
+ # @return [String]
80
+ #
81
+ # @api private
82
+ def text
83
+ @node.text
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -2,6 +2,10 @@
2
2
 
3
3
  require 'ox'
4
4
 
5
+ require_relative '../error'
6
+ require_relative 'ox/document'
7
+ require_relative 'ox/node'
8
+
5
9
  module Shale
6
10
  module Adapter
7
11
  # Ox adapter
@@ -12,11 +16,15 @@ module Shale
12
16
  #
13
17
  # @param [String] xml XML document
14
18
  #
15
- # @return [::Ox::Document, ::Ox::Element]
19
+ # @raise [ParseError] when XML document has errors
20
+ #
21
+ # @return [Shale::Adapter::Ox::Node]
16
22
  #
17
23
  # @api private
18
24
  def self.load(xml)
19
25
  Node.new(::Ox.parse(xml))
26
+ rescue ::Ox::ParseError => e
27
+ raise ParseError, "Document is invalid: #{e.message}"
20
28
  end
21
29
 
22
30
  # Serialize Ox document into XML
@@ -48,139 +56,6 @@ module Shale
48
56
  def self.create_document
49
57
  Document.new
50
58
  end
51
-
52
- # Wrapper around Ox API
53
- #
54
- # @api private
55
- class Document
56
- # Return Ox document
57
- #
58
- # @return [::Ox::Document]
59
- #
60
- # @api private
61
- attr_reader :doc
62
-
63
- # Initialize object
64
- #
65
- # @api private
66
- def initialize
67
- @doc = ::Ox::Document.new
68
- end
69
-
70
- # Create Ox element
71
- #
72
- # @param [String] name Name of the XML element
73
- #
74
- # @return [::Ox::Element]
75
- #
76
- # @api private
77
- def create_element(name)
78
- ::Ox::Element.new(name)
79
- end
80
-
81
- # Add XML namespace to document
82
- #
83
- # Ox doesn't support XML namespaces so this method does nothing.
84
- #
85
- # @param [String] prefix
86
- # @param [String] namespace
87
- #
88
- # @api private
89
- def add_namespace(prefix, namespace)
90
- # :noop:
91
- end
92
-
93
- # Add attribute to Ox element
94
- #
95
- # @param [::Ox::Element] element Ox element
96
- # @param [String] name Name of the XML attribute
97
- # @param [String] value Value of the XML attribute
98
- #
99
- # @api private
100
- def add_attribute(element, name, value)
101
- element[name] = value
102
- end
103
-
104
- # Add child element to Ox element
105
- #
106
- # @param [::Ox::Element] element Ox parent element
107
- # @param [::Ox::Element] child Ox child element
108
- #
109
- # @api private
110
- def add_element(element, child)
111
- element << child
112
- end
113
-
114
- # Add text node to Ox element
115
- #
116
- # @param [::Ox::Element] element Ox element
117
- # @param [String] text Text to add
118
- #
119
- # @api private
120
- def add_text(element, text)
121
- element << text
122
- end
123
- end
124
-
125
- # Wrapper around Ox::Element API
126
- #
127
- # @api private
128
- class Node
129
- # Initialize object with Ox element
130
- #
131
- # @param [::Ox::Element] node Ox element
132
- #
133
- # @api private
134
- def initialize(node)
135
- @node = node
136
- end
137
-
138
- # Return name of the node in the format of
139
- # prefix:name when the node is namespaced or just name when it's not
140
- #
141
- # @return [String]
142
- #
143
- # @example without namespace
144
- # node.name # => Bar
145
- #
146
- # @example with namespace
147
- # node.name # => foo:Bar
148
- #
149
- # @api private
150
- def name
151
- @node.name
152
- end
153
-
154
- # Return all attributes associated with the node
155
- #
156
- # @return [Hash]
157
- #
158
- # @api private
159
- def attributes
160
- @node.attributes
161
- end
162
-
163
- # Return node's element children
164
- #
165
- # @return [Array<Shale::Adapter::Ox::Node>]
166
- #
167
- # @api private
168
- def children
169
- @node
170
- .nodes
171
- .filter { |e| e.is_a?(::Ox::Element) }
172
- .map { |e| self.class.new(e) }
173
- end
174
-
175
- # Return first text child of a node
176
- #
177
- # @return [String]
178
- #
179
- # @api private
180
- def text
181
- @node.text
182
- end
183
- end
184
59
  end
185
60
  end
186
61
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shale
4
+ module Adapter
5
+ module REXML
6
+ # Wrapper around REXML API
7
+ #
8
+ # @api private
9
+ class Document
10
+ # Initialize object
11
+ #
12
+ # @api private
13
+ def initialize
14
+ context = { attribute_quote: :quote, prologue_quote: :quote }
15
+ @doc = ::REXML::Document.new(nil, context)
16
+ @namespaces = {}
17
+ end
18
+
19
+ # Return REXML document
20
+ #
21
+ # @return [::REXML::Document]
22
+ #
23
+ # @api private
24
+ def doc
25
+ if @doc.root
26
+ @namespaces.each do |prefix, namespace|
27
+ @doc.root.add_namespace(prefix, namespace)
28
+ end
29
+ end
30
+
31
+ @doc
32
+ end
33
+
34
+ # Create REXML element
35
+ #
36
+ # @param [String] name Name of the XML element
37
+ #
38
+ # @return [::REXML::Element]
39
+ #
40
+ # @api private
41
+ def create_element(name)
42
+ ::REXML::Element.new(name, nil, attribute_quote: :quote)
43
+ end
44
+
45
+ # Add XML namespace to document
46
+ #
47
+ # @param [String] prefix
48
+ # @param [String] namespace
49
+ #
50
+ # @api private
51
+ def add_namespace(prefix, namespace)
52
+ @namespaces[prefix] = namespace if prefix && namespace
53
+ end
54
+
55
+ # Add attribute to REXML element
56
+ #
57
+ # @param [::REXML::Element] element REXML element
58
+ # @param [String] name Name of the XML attribute
59
+ # @param [String] value Value of the XML attribute
60
+ #
61
+ # @api private
62
+ def add_attribute(element, name, value)
63
+ element.add_attribute(name, value)
64
+ end
65
+
66
+ # Add child element to REXML element
67
+ #
68
+ # @param [::REXML::Element] element REXML parent element
69
+ # @param [::REXML::Element] child REXML child element
70
+ #
71
+ # @api private
72
+ def add_element(element, child)
73
+ element.add_element(child)
74
+ end
75
+
76
+ # Add text node to REXML element
77
+ #
78
+ # @param [::REXML::Element] element REXML element
79
+ # @param [String] text Text to add
80
+ #
81
+ # @api private
82
+ def add_text(element, text)
83
+ element.add_text(text)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end