shale 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +93 -7
- data/exe/shaleb +30 -6
- data/lib/shale/adapter/nokogiri/document.rb +87 -0
- data/lib/shale/adapter/nokogiri/node.rb +100 -0
- data/lib/shale/adapter/nokogiri.rb +11 -151
- data/lib/shale/adapter/ox/document.rb +80 -0
- data/lib/shale/adapter/ox/node.rb +88 -0
- data/lib/shale/adapter/ox.rb +9 -134
- data/lib/shale/adapter/rexml/document.rb +88 -0
- data/lib/shale/adapter/rexml/node.rb +99 -0
- data/lib/shale/adapter/rexml.rb +9 -150
- data/lib/shale/error.rb +35 -2
- data/lib/shale/mapper.rb +2 -2
- data/lib/shale/schema/{json_compiler → compiler}/boolean.rb +1 -1
- data/lib/shale/schema/{json_compiler/object.rb → compiler/complex.rb} +11 -8
- data/lib/shale/schema/{json_compiler → compiler}/date.rb +1 -1
- data/lib/shale/schema/{json_compiler → compiler}/float.rb +1 -1
- data/lib/shale/schema/{json_compiler → compiler}/integer.rb +1 -1
- data/lib/shale/schema/{json_compiler → compiler}/property.rb +6 -6
- data/lib/shale/schema/{json_compiler → compiler}/string.rb +1 -1
- data/lib/shale/schema/{json_compiler → compiler}/time.rb +1 -1
- data/lib/shale/schema/compiler/value.rb +21 -0
- data/lib/shale/schema/compiler/xml_complex.rb +50 -0
- data/lib/shale/schema/compiler/xml_property.rb +73 -0
- data/lib/shale/schema/json_compiler.rb +32 -34
- data/lib/shale/schema/json_generator.rb +3 -3
- data/lib/shale/schema/xml_compiler.rb +919 -0
- data/lib/shale/schema/xml_generator.rb +7 -7
- data/lib/shale/schema.rb +16 -0
- data/lib/shale/type/{composite.rb → complex.rb} +20 -2
- data/lib/shale/utils.rb +42 -7
- data/lib/shale/version.rb +1 -1
- data/lib/shale.rb +8 -19
- data/shale.gemspec +1 -1
- metadata +23 -15
- data/lib/shale/schema/json_compiler/utils.rb +0 -52
- data/lib/shale/schema/json_compiler/value.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aaa3043e612d332fab30ff87ecab722e3812e7769eba982db27c38f74c205ce1
|
4
|
+
data.tar.gz: bb5cdb963bce5756c48edb37d28bddc40a816901e87b4714ea512f07f8ec4c28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed1f1ffc88cabb5f9403ff957c02efc4565aee73b9a8a1fd2297ff13bfbb6bdf0e94664b865f8d2d812ddd66fe97d154e34982eea3318ba01c6d16b4a6922c70
|
7
|
+
data.tar.gz: d41392de36bcace9efee12a869f49beffba235ae09e09f33c6973159b96320e3f657a6edd4d634e53da430f8127e74fa1025fa2f5c75dad2e7568abe6a0794ce
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## [0.5.0] - 2022-06-28
|
2
|
+
|
3
|
+
### Added
|
4
|
+
- Allow to generate Shale model from XML Schema
|
5
|
+
|
6
|
+
### Changed
|
7
|
+
- Shale doesn't defaults to REXML anymore - XML adapter needs to be set explicitly
|
8
|
+
- Rename "JSONSchemaError" to "SchemaError"
|
9
|
+
- Rename "Composite" type to "Complex"
|
10
|
+
- Drop support for Ruby 2.6
|
11
|
+
|
1
12
|
## [0.4.0] - 2022-05-30
|
2
13
|
|
3
14
|
### Added
|
data/README.md
CHANGED
@@ -4,18 +4,20 @@ Shale is a Ruby object mapper and serializer for JSON, YAML and XML.
|
|
4
4
|
It allows you to parse JSON, YAML and XML data and convert it into Ruby data structures,
|
5
5
|
as well as serialize data structures into JSON, YAML or XML.
|
6
6
|
|
7
|
+
Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
|
8
|
+
|
7
9
|
## Features
|
8
10
|
|
9
11
|
* Convert JSON, YAML and XML to Ruby data model
|
10
12
|
* Convert Ruby data model to JSON, YAML and XML
|
11
13
|
* Generate JSON and XML Schema from Ruby models
|
12
|
-
* Compile JSON Schema into Ruby models
|
14
|
+
* Compile JSON and XML Schema into Ruby models
|
13
15
|
* Out of the box support for JSON, YAML, Nokogiri, REXML and Ox parsers
|
14
16
|
* Support for custom adapters
|
15
17
|
|
16
18
|
## Installation
|
17
19
|
|
18
|
-
Shale supports Ruby (MRI) 2.
|
20
|
+
Shale supports Ruby (MRI) 2.7+
|
19
21
|
|
20
22
|
Add this line to your application's Gemfile:
|
21
23
|
|
@@ -60,11 +62,10 @@ $ gem install shale
|
|
60
62
|
* [Generating JSON Schema](#generating-json-schema)
|
61
63
|
* [Compiling JSON Schema into Shale model](#compiling-json-schema-into-shale-model)
|
62
64
|
* [Generating XML Schema](#generating-xml-schema)
|
65
|
+
* [Compiling XML Schema into Shale model](#compiling-xml-schema-into-shale-model)
|
63
66
|
|
64
67
|
## Usage
|
65
68
|
|
66
|
-
Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
|
67
|
-
|
68
69
|
### Simple use case
|
69
70
|
|
70
71
|
```ruby
|
@@ -230,6 +231,15 @@ person.to_hash
|
|
230
231
|
|
231
232
|
### Converting XML to object
|
232
233
|
|
234
|
+
To use XML with Shale you have to set adapter you want to use.
|
235
|
+
Shale comes with adapters for REXML, Nokogiri and OX parsers.
|
236
|
+
For details see [Adapters](#adapters) section.
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
require 'shale/adapter/rexml'
|
240
|
+
Shale.xml_adapter = Shale::Adapter::REXML
|
241
|
+
```
|
242
|
+
|
233
243
|
```ruby
|
234
244
|
person = Person.from_xml(<<~DATA)
|
235
245
|
<person>
|
@@ -588,8 +598,7 @@ end
|
|
588
598
|
### Adapters
|
589
599
|
|
590
600
|
Shale uses adapters for parsing and generating documents.
|
591
|
-
By default Ruby's standard JSON
|
592
|
-
REXML for XML.
|
601
|
+
By default Ruby's standard JSON and YAML parsers are used for handling JSON and YAML documents.
|
593
602
|
|
594
603
|
You can change it by providing your own adapter. For JSON and YAML, adapter must implement
|
595
604
|
`.load` and `.dump` class methods.
|
@@ -602,6 +611,7 @@ Shale.json_adapter = MultiJson
|
|
602
611
|
Shale.yaml_adapter = MyYamlAdapter
|
603
612
|
```
|
604
613
|
|
614
|
+
To handle XML documents you have to explicitly set XML adapter.
|
605
615
|
Shale provides adapters for most popular Ruby XML parsers:
|
606
616
|
|
607
617
|
:warning: **Ox doesn't support XML namespaces**
|
@@ -609,7 +619,7 @@ Shale provides adapters for most popular Ruby XML parsers:
|
|
609
619
|
```ruby
|
610
620
|
require 'shale'
|
611
621
|
|
612
|
-
#
|
622
|
+
# if you want to use REXML:
|
613
623
|
|
614
624
|
require 'shale/adapter/rexml'
|
615
625
|
Shale.xml_adapter = Shale::Adapter::REXML
|
@@ -842,6 +852,82 @@ end
|
|
842
852
|
Shale::Schema::XMLGenerator.register_xml_type(MyEmailType, 'myEmailXMLType')
|
843
853
|
```
|
844
854
|
|
855
|
+
### Compiling XML Schema into Shale model
|
856
|
+
|
857
|
+
To generate Shale data model from XML Schema use:
|
858
|
+
|
859
|
+
```ruby
|
860
|
+
require 'shale/schema'
|
861
|
+
|
862
|
+
schema = <<~SCHEMA
|
863
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
864
|
+
<xs:element name="Person" type="Person" />
|
865
|
+
|
866
|
+
<xs:complexType name="Person">
|
867
|
+
<xs:sequence>
|
868
|
+
<xs:element name="FirstName" type="xs:string" />
|
869
|
+
<xs:element name="LastName" type="xs:string" />
|
870
|
+
<xs:element name="Address" type="Address" />
|
871
|
+
</xs:sequence>
|
872
|
+
</xs:complexType>
|
873
|
+
|
874
|
+
<xs:complexType name="Address">
|
875
|
+
<xs:sequence>
|
876
|
+
<xs:element name="Street" type="xs:string" />
|
877
|
+
<xs:element name="City" type="xs:string" />
|
878
|
+
</xs:sequence>
|
879
|
+
</xs:complexType>
|
880
|
+
</xs:schema>
|
881
|
+
SCHEMA
|
882
|
+
|
883
|
+
Shale::Schema.from_xml([schema])
|
884
|
+
|
885
|
+
# =>
|
886
|
+
#
|
887
|
+
# {
|
888
|
+
# "address" => "
|
889
|
+
# require 'shale'
|
890
|
+
#
|
891
|
+
# class Address < Shale::Mapper
|
892
|
+
# attribute :street, Shale::Type::String
|
893
|
+
# attribute :city, Shale::Type::String
|
894
|
+
#
|
895
|
+
# xml do
|
896
|
+
# root 'Address'
|
897
|
+
#
|
898
|
+
# map_element 'Street', to: :street
|
899
|
+
# map_element 'City', to: :city
|
900
|
+
# end
|
901
|
+
# end
|
902
|
+
# ",
|
903
|
+
# "person" => "
|
904
|
+
# require 'shale'
|
905
|
+
#
|
906
|
+
# require_relative 'address'
|
907
|
+
#
|
908
|
+
# class Person < Shale::Mapper
|
909
|
+
# attribute :first_name, Shale::Type::String
|
910
|
+
# attribute :last_name, Shale::Type::String
|
911
|
+
# attribute :address, Address
|
912
|
+
#
|
913
|
+
# xml do
|
914
|
+
# root 'Person'
|
915
|
+
#
|
916
|
+
# map_element 'FirstName', to: :first_name
|
917
|
+
# map_element 'LastName', to: :last_name
|
918
|
+
# map_element 'Address', to: :address
|
919
|
+
# end
|
920
|
+
# end
|
921
|
+
# "
|
922
|
+
# }
|
923
|
+
```
|
924
|
+
|
925
|
+
You can also use a command line tool to do it:
|
926
|
+
|
927
|
+
```
|
928
|
+
$ shaleb -c -f xml -i schema.xml
|
929
|
+
```
|
930
|
+
|
845
931
|
## Contributing
|
846
932
|
|
847
933
|
Bug reports and pull requests are welcome on GitHub at https://github.com/kgiszczak/shale.
|
data/exe/shaleb
CHANGED
@@ -4,12 +4,29 @@
|
|
4
4
|
require 'fileutils'
|
5
5
|
require 'optparse'
|
6
6
|
|
7
|
-
|
7
|
+
def require_local_or_global(path)
|
8
|
+
base_path = File.expand_path('../lib', __dir__)
|
8
9
|
|
9
|
-
if File.exist?(base_path)
|
10
|
-
|
11
|
-
else
|
12
|
-
|
10
|
+
if File.exist?(base_path)
|
11
|
+
require_relative "../lib/#{path}"
|
12
|
+
else
|
13
|
+
require path
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require_local_or_global('shale/schema')
|
18
|
+
|
19
|
+
def load_xml_parser
|
20
|
+
require_local_or_global('shale/adapter/nokogiri')
|
21
|
+
Shale.xml_adapter = Shale::Adapter::Nokogiri
|
22
|
+
rescue LoadError
|
23
|
+
begin
|
24
|
+
require_local_or_global('shale/adapter/rexml')
|
25
|
+
Shale.xml_adapter = Shale::Adapter::REXML
|
26
|
+
rescue LoadError
|
27
|
+
puts "Can't load XML parser. Make sure Nokogiri or REXML is installed on your system!"
|
28
|
+
exit
|
29
|
+
end
|
13
30
|
end
|
14
31
|
|
15
32
|
params = {}
|
@@ -54,7 +71,12 @@ if params[:compile]
|
|
54
71
|
end
|
55
72
|
end
|
56
73
|
|
57
|
-
|
74
|
+
if params[:format] == 'xml'
|
75
|
+
load_xml_parser
|
76
|
+
models = Shale::Schema.from_xml(schemas)
|
77
|
+
else
|
78
|
+
models = Shale::Schema.from_json(schemas, root_name: params[:root])
|
79
|
+
end
|
58
80
|
|
59
81
|
if params[:output]
|
60
82
|
dir = File.expand_path(params[:output], Dir.pwd)
|
@@ -94,6 +116,8 @@ else
|
|
94
116
|
klass = Object.const_get(params[:root])
|
95
117
|
|
96
118
|
if params[:format] == 'xml'
|
119
|
+
load_xml_parser
|
120
|
+
|
97
121
|
if params[:output]
|
98
122
|
base_name = File.basename(params[:output], File.extname(params[:output]))
|
99
123
|
schemas = Shale::Schema.to_xml(klass, base_name, pretty: params[:pretty])
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shale
|
4
|
+
module Adapter
|
5
|
+
module Nokogiri
|
6
|
+
# Wrapper around Nokogiri API
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class Document
|
10
|
+
# Initialize object
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def initialize
|
14
|
+
@doc = ::Nokogiri::XML::Document.new
|
15
|
+
@namespaces = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return Nokogiri document
|
19
|
+
#
|
20
|
+
# @return [::Nokogiri::XML::Document]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
def doc
|
24
|
+
if @doc.root
|
25
|
+
@namespaces.each do |prefix, namespace|
|
26
|
+
@doc.root.add_namespace(prefix, namespace)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
@doc
|
31
|
+
end
|
32
|
+
|
33
|
+
# Create Nokogiri element
|
34
|
+
#
|
35
|
+
# @param [String] name Name of the XML element
|
36
|
+
#
|
37
|
+
# @return [::Nokogiri::XML::Element]
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
def create_element(name)
|
41
|
+
::Nokogiri::XML::Element.new(name, @doc)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Add XML namespace to document
|
45
|
+
#
|
46
|
+
# @param [String] prefix
|
47
|
+
# @param [String] namespace
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
def add_namespace(prefix, namespace)
|
51
|
+
@namespaces[prefix] = namespace if prefix && namespace
|
52
|
+
end
|
53
|
+
|
54
|
+
# Add attribute to Nokogiri element
|
55
|
+
#
|
56
|
+
# @param [::Nokogiri::XML::Element] element Nokogiri element
|
57
|
+
# @param [String] name Name of the XML attribute
|
58
|
+
# @param [String] value Value of the XML attribute
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
def add_attribute(element, name, value)
|
62
|
+
element[name] = value
|
63
|
+
end
|
64
|
+
|
65
|
+
# Add child element to Nokogiri element
|
66
|
+
#
|
67
|
+
# @param [::Nokogiri::XML::Element] element Nokogiri parent element
|
68
|
+
# @param [::Nokogiri::XML::Element] child Nokogiri child element
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
def add_element(element, child)
|
72
|
+
element.add_child(child)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Add text node to Nokogiri element
|
76
|
+
#
|
77
|
+
# @param [::Nokogiri::XML::Element] element Nokogiri element
|
78
|
+
# @param [String] text Text to add
|
79
|
+
#
|
80
|
+
# @api private
|
81
|
+
def add_text(element, text)
|
82
|
+
element.content = text
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -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
|
-
# @
|
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
|