shale 0.3.1 → 0.6.0

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