shale 0.3.0 → 0.5.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 (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