moxml 0.1.2 → 0.1.4
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/.rubocop_todo.yml +50 -14
- data/Gemfile +1 -0
- data/LICENSE.md +33 -0
- data/README.adoc +69 -21
- data/lib/moxml/adapter/base.rb +12 -0
- data/lib/moxml/adapter/customized_rexml/formatter.rb +195 -0
- data/lib/moxml/adapter/nokogiri.rb +1 -1
- data/lib/moxml/adapter/oga.rb +21 -7
- data/lib/moxml/adapter/rexml.rb +458 -0
- data/lib/moxml/adapter.rb +1 -1
- data/lib/moxml/attribute.rb +2 -2
- data/lib/moxml/config.rb +12 -3
- data/lib/moxml/document_builder.rb +13 -17
- data/lib/moxml/element.rb +4 -4
- data/lib/moxml/namespace.rb +1 -1
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/adapter/rexml_spec.rb +14 -0
- data/spec/moxml/all_with_adapters_spec.rb +0 -2
- data/spec/moxml_spec.rb +1 -1
- data/spec/support/shared_examples/declaration.rb +1 -0
- data/spec/support/shared_examples/edge_cases.rb +3 -3
- data/spec/support/shared_examples/element.rb +3 -3
- data/spec/support/shared_examples/examples/readme_examples.rb +72 -15
- data/spec/support/shared_examples/integration.rb +2 -2
- data/spec/support/shared_examples/xml_adapter.rb +169 -7
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65fadaecfecbc8142d11c82c79c20ea9811e3b58e21b94f335ba822bdfbfc852
|
4
|
+
data.tar.gz: e0189194a594ff763ffec269d15e56dadf91b22369d2efb399e29e3c993f1ace
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c20b0290ed6a6f9ea904564b7619c05f1aec9b9ebe9274a6ea042b9756caeb8792a4d6f45224af8019e2760b8d752ff20a9ef9c4f8f4acb893be069b7f95a92
|
7
|
+
data.tar.gz: 05d046559db87598410f122e51fa7a2276d975f40eabe19f32c65de6c2459238976551496937e33a2a319e3a7ff110bdc782af8ce9942bfcc24840f292d79150
|
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2025-
|
3
|
+
# on 2025-05-09 11:52:41 UTC using RuboCop version 1.68.0.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -17,39 +17,45 @@ Lint/MissingCopEnableDirective:
|
|
17
17
|
Exclude:
|
18
18
|
- 'lib/moxml/adapter/customized_oga/xml_generator.rb'
|
19
19
|
|
20
|
-
# Offense count:
|
20
|
+
# Offense count: 2
|
21
21
|
# Configuration parameters: AllowedParentClasses.
|
22
22
|
Lint/MissingSuper:
|
23
23
|
Exclude:
|
24
24
|
- 'lib/moxml/adapter/customized_oga/xml_declaration.rb'
|
25
|
+
- 'lib/moxml/adapter/customized_rexml/formatter.rb'
|
25
26
|
|
26
|
-
# Offense count:
|
27
|
+
# Offense count: 8
|
27
28
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
28
29
|
Metrics/AbcSize:
|
29
|
-
Max:
|
30
|
+
Max: 39
|
30
31
|
|
31
|
-
# Offense count:
|
32
|
+
# Offense count: 53
|
32
33
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
33
34
|
# AllowedMethods: refine
|
34
35
|
Metrics/BlockLength:
|
35
|
-
Max:
|
36
|
+
Max: 270
|
36
37
|
|
37
|
-
# Offense count:
|
38
|
+
# Offense count: 5
|
38
39
|
# Configuration parameters: CountComments, CountAsOne.
|
39
40
|
Metrics/ClassLength:
|
40
|
-
Max:
|
41
|
+
Max: 335
|
41
42
|
|
42
|
-
# Offense count:
|
43
|
+
# Offense count: 7
|
43
44
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
44
45
|
Metrics/CyclomaticComplexity:
|
45
|
-
Max:
|
46
|
+
Max: 15
|
46
47
|
|
47
|
-
# Offense count:
|
48
|
+
# Offense count: 15
|
48
49
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
49
50
|
Metrics/MethodLength:
|
50
|
-
Max:
|
51
|
+
Max: 23
|
51
52
|
|
52
|
-
# Offense count:
|
53
|
+
# Offense count: 3
|
54
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
55
|
+
Metrics/PerceivedComplexity:
|
56
|
+
Max: 15
|
57
|
+
|
58
|
+
# Offense count: 5
|
53
59
|
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
|
54
60
|
# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
|
55
61
|
Naming/MethodParameterName:
|
@@ -57,9 +63,39 @@ Naming/MethodParameterName:
|
|
57
63
|
- 'lib/moxml/adapter/customized_oga/xml_generator.rb'
|
58
64
|
- 'lib/moxml/adapter/nokogiri.rb'
|
59
65
|
- 'lib/moxml/adapter/ox.rb'
|
66
|
+
- 'lib/moxml/adapter/rexml.rb'
|
60
67
|
- 'lib/moxml/attribute.rb'
|
61
68
|
|
62
|
-
# Offense count:
|
69
|
+
# Offense count: 1
|
70
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
71
|
+
Style/CombinableLoops:
|
72
|
+
Exclude:
|
73
|
+
- 'lib/moxml/adapter/customized_rexml/formatter.rb'
|
74
|
+
|
75
|
+
# Offense count: 28
|
63
76
|
# Configuration parameters: AllowedConstants.
|
64
77
|
Style/Documentation:
|
65
78
|
Enabled: false
|
79
|
+
|
80
|
+
# Offense count: 1
|
81
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
82
|
+
# Configuration parameters: EnforcedStyle.
|
83
|
+
# SupportedStyles: always, always_true, never
|
84
|
+
Style/FrozenStringLiteralComment:
|
85
|
+
Exclude:
|
86
|
+
- 'lib/moxml/adapter/customized_rexml/formatter.rb'
|
87
|
+
|
88
|
+
# Offense count: 2
|
89
|
+
# Configuration parameters: MinBranchesCount.
|
90
|
+
Style/HashLikeCase:
|
91
|
+
Exclude:
|
92
|
+
- 'lib/moxml/adapter/customized_rexml/formatter.rb'
|
93
|
+
|
94
|
+
# Offense count: 2
|
95
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
96
|
+
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
|
97
|
+
# AllowedMethods: present?, blank?, presence, try, try!
|
98
|
+
Style/SafeNavigation:
|
99
|
+
Exclude:
|
100
|
+
- 'lib/moxml/adapter/customized_rexml/formatter.rb'
|
101
|
+
- 'lib/moxml/adapter/rexml.rb'
|
data/Gemfile
CHANGED
data/LICENSE.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
Licenses & Copyright
|
2
|
+
====================
|
3
|
+
|
4
|
+
This license file adheres to the formatting guidelines of
|
5
|
+
[readable-licenses](https://github.com/nevir/readable-licenses).
|
6
|
+
|
7
|
+
|
8
|
+
Ribose's BSD 2-Clause License
|
9
|
+
-----------------------------
|
10
|
+
|
11
|
+
Copyright (c) 2024, [Ribose Inc](https://www.ribose.com).
|
12
|
+
All rights reserved.
|
13
|
+
|
14
|
+
Redistribution and use in source and binary forms, with or without modification,
|
15
|
+
are permitted provided that the following conditions are met:
|
16
|
+
|
17
|
+
1. Redistributions of source code must retain the above copyright notice,
|
18
|
+
this list of conditions and the following disclaimer.
|
19
|
+
|
20
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
21
|
+
this list of conditions and the following disclaimer in the documentation
|
22
|
+
and/or other materials provided with the distribution.
|
23
|
+
|
24
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
25
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
26
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
27
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
28
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
29
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
30
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
31
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
32
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
33
|
+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.adoc
CHANGED
@@ -24,15 +24,35 @@ Key features:
|
|
24
24
|
* Easy switching between XML processing engines
|
25
25
|
* Clean separation between interface and implementation
|
26
26
|
|
27
|
+
== Supported XML libraries
|
28
|
+
|
29
|
+
Moxml supports the following XML libraries:
|
30
|
+
|
31
|
+
REXML:: (default) https://github.com/ruby/rexml[REXML], a pure Ruby XML parser
|
32
|
+
distributed with standard Ruby. Not the fastest, but always available.
|
33
|
+
|
34
|
+
Nokogiri:: (recommended) https://github.com/sparklemotion/nokogiri[Nokogiri], a
|
35
|
+
widely used implementation which wraps around the performant
|
36
|
+
https://github.com/GNOME/libxml2[libxml2] C library.
|
37
|
+
|
38
|
+
Oga:: https://github.com/yorickpeterse/oga[Oga], a pure Ruby XML parser.
|
39
|
+
Recommended when you need a pure Ruby solution say for
|
40
|
+
https://github.com/opal/opal[Opal].
|
41
|
+
|
42
|
+
Ox:: https://github.com/ohler55/ox[Ox], a fast XML parser (to be supported)
|
43
|
+
|
44
|
+
|
27
45
|
== Getting started
|
28
46
|
|
47
|
+
=== Installation
|
48
|
+
|
29
49
|
Install the gem and at least one supported XML library:
|
30
50
|
|
31
51
|
[source,ruby]
|
32
52
|
----
|
33
53
|
# In your Gemfile
|
34
54
|
gem 'moxml'
|
35
|
-
gem 'nokogiri' # Or '
|
55
|
+
gem 'nokogiri' # Or 'oga', 'rexml', or 'ox' when it is integrated
|
36
56
|
----
|
37
57
|
|
38
58
|
=== Basic document creation
|
@@ -45,7 +65,7 @@ require 'moxml'
|
|
45
65
|
doc = Moxml.new.create_document
|
46
66
|
|
47
67
|
# Add XML declaration
|
48
|
-
doc.
|
68
|
+
doc.add_child(doc.create_declaration("1.0", "UTF-8"))
|
49
69
|
|
50
70
|
# Create root element with namespace
|
51
71
|
root = doc.create_element('book')
|
@@ -69,7 +89,7 @@ The builder pattern provides a clean DSL for creating XML documents:
|
|
69
89
|
|
70
90
|
[source,ruby]
|
71
91
|
----
|
72
|
-
doc = Moxml.new.build do
|
92
|
+
doc = Moxml::Builder.new(Moxml.new).build do
|
73
93
|
declaration version: "1.0", encoding: "UTF-8"
|
74
94
|
|
75
95
|
element 'library', xmlns: 'http://example.org/library' do
|
@@ -98,11 +118,12 @@ end
|
|
98
118
|
doc = Moxml.new.create_document
|
99
119
|
|
100
120
|
# Add declaration
|
101
|
-
doc.
|
121
|
+
doc.add_child(doc.create_declaration("1.0", "UTF-8"))
|
102
122
|
|
103
123
|
# Create root with namespace
|
104
124
|
root = doc.create_element('library')
|
105
125
|
root.add_namespace(nil, 'http://example.org/library')
|
126
|
+
root.add_namespace("dc", "http://purl.org/dc/elements/1.1/")
|
106
127
|
doc.add_child(root)
|
107
128
|
|
108
129
|
# Add elements with attributes
|
@@ -131,23 +152,23 @@ doc = Moxml.new.create_document
|
|
131
152
|
doc = Moxml.new.parse(xml_string)
|
132
153
|
|
133
154
|
# Document properties and methods
|
134
|
-
doc.encoding
|
135
|
-
doc.encoding = "UTF-8"
|
155
|
+
doc.encoding # Get document encoding
|
156
|
+
doc.encoding = "UTF-8" # Set document encoding
|
136
157
|
doc.version # Get XML version
|
137
158
|
doc.version = "1.1" # Set XML version
|
138
159
|
doc.standalone # Get standalone declaration
|
139
160
|
doc.standalone = "yes" # Set standalone declaration
|
140
161
|
|
141
162
|
# Document structure
|
142
|
-
doc.root
|
163
|
+
doc.root # Get root element
|
143
164
|
doc.children # Get all top-level nodes
|
144
165
|
doc.add_child(node) # Add a child node
|
145
166
|
doc.remove_child(node) # Remove a child node
|
146
167
|
|
147
168
|
# Node creation methods
|
148
|
-
doc.create_element(name)
|
149
|
-
doc.create_text(content)
|
150
|
-
doc.create_cdata(content)
|
169
|
+
doc.create_element(name) # Create new element
|
170
|
+
doc.create_text(content) # Create text node
|
171
|
+
doc.create_cdata(content) # Create CDATA section
|
151
172
|
doc.create_comment(content) # Create comment
|
152
173
|
doc.create_processing_instruction(target, content) # Create PI
|
153
174
|
|
@@ -172,8 +193,8 @@ element.name = "new_name" # Set element name
|
|
172
193
|
element.text # Get text content
|
173
194
|
element.text = "content" # Set text content
|
174
195
|
element.inner_text # Get text content for current node only
|
175
|
-
element.
|
176
|
-
element.
|
196
|
+
element.inner_xml # Get inner XML content
|
197
|
+
element.inner_xml = xml # Set inner XML content
|
177
198
|
|
178
199
|
# Attributes
|
179
200
|
element[name] # Get attribute value
|
@@ -444,6 +465,9 @@ rescue Moxml::ValidationError => e
|
|
444
465
|
rescue Moxml::XPathError => e
|
445
466
|
# Handles XPath expression errors
|
446
467
|
puts "XPath error: #{e.message}"
|
468
|
+
rescue Moxml::NamespaceError => e
|
469
|
+
# Handles namespace errors
|
470
|
+
puts "Namespace error: #{e.message}"
|
447
471
|
rescue Moxml::Error => e
|
448
472
|
# Handles other Moxml-specific errors
|
449
473
|
puts "Error: #{e.message}"
|
@@ -452,7 +476,9 @@ end
|
|
452
476
|
|
453
477
|
== Configuration
|
454
478
|
|
455
|
-
|
479
|
+
=== General
|
480
|
+
|
481
|
+
Moxml can be configured globally or per instance.
|
456
482
|
|
457
483
|
[source,ruby]
|
458
484
|
----
|
@@ -465,11 +491,32 @@ end
|
|
465
491
|
|
466
492
|
# Instance configuration
|
467
493
|
moxml = Moxml.new do |config|
|
468
|
-
config.adapter = :
|
494
|
+
config.adapter = :oga
|
469
495
|
config.strict = false
|
470
496
|
end
|
471
497
|
----
|
472
498
|
|
499
|
+
=== Default adapter selection
|
500
|
+
|
501
|
+
To select a non-default adapter, set it before processing any input using the
|
502
|
+
following syntax.
|
503
|
+
|
504
|
+
[source,ruby]
|
505
|
+
----
|
506
|
+
Moxml::Config.default_adapter = <adapter-symbol>
|
507
|
+
----
|
508
|
+
|
509
|
+
Where, `<adapter-symbol>` is one of the following:
|
510
|
+
|
511
|
+
`:rexml`:: REXML (default)
|
512
|
+
|
513
|
+
`:nokogiri`:: Nokogiri
|
514
|
+
|
515
|
+
`:oga`:: Oga
|
516
|
+
|
517
|
+
`:ox`:: Ox (not yet supported)
|
518
|
+
|
519
|
+
|
473
520
|
== Thread safety
|
474
521
|
|
475
522
|
Moxml is thread-safe when used properly. Each instance maintains its own state
|
@@ -563,15 +610,16 @@ end
|
|
563
610
|
|
564
611
|
== Contributing
|
565
612
|
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
613
|
+
. Fork the repository
|
614
|
+
. Create your feature branch (`git checkout -b feature/my-new-feature`)
|
615
|
+
. Commit your changes (`git commit -am 'Add some feature'`)
|
616
|
+
. Push to the branch (`git push origin feature/my-new-feature`)
|
617
|
+
. Create a new Pull Request
|
571
618
|
|
572
619
|
== License
|
573
620
|
|
574
|
-
Copyright (c)
|
621
|
+
Copyright (c) Ribose Inc.
|
575
622
|
|
576
|
-
This project is licensed under the BSD-2-Clause License. See the
|
623
|
+
This project is licensed under the BSD-2-Clause License. See the
|
624
|
+
link:LICENSE.md[] file for details.
|
577
625
|
|
data/lib/moxml/adapter/base.rb
CHANGED
@@ -63,6 +63,18 @@ module Moxml
|
|
63
63
|
create_native_namespace(element, prefix, uri)
|
64
64
|
end
|
65
65
|
|
66
|
+
def set_attribute_name(attribute, name)
|
67
|
+
attribute.name = name
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_attribute_value(attribute, value)
|
71
|
+
attribute.value = value
|
72
|
+
end
|
73
|
+
|
74
|
+
def duplicate_node(node)
|
75
|
+
node.dup
|
76
|
+
end
|
77
|
+
|
66
78
|
protected
|
67
79
|
|
68
80
|
def create_native_element(name)
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require "rexml/formatters/pretty"
|
2
|
+
|
3
|
+
module Moxml
|
4
|
+
module Adapter
|
5
|
+
module CustomizedRexml
|
6
|
+
# Custom REXML formatter that fixes indentation and wrapping issues
|
7
|
+
class Formatter < ::REXML::Formatters::Pretty
|
8
|
+
def initialize(indentation: 2, self_close_empty: false)
|
9
|
+
@indentation = " " * indentation
|
10
|
+
@level = 0
|
11
|
+
@compact = true
|
12
|
+
@width = -1 # Disable line wrapping
|
13
|
+
@self_close_empty = self_close_empty
|
14
|
+
end
|
15
|
+
|
16
|
+
def write(node, output)
|
17
|
+
case node
|
18
|
+
when ::REXML::XMLDecl
|
19
|
+
write_declaration(node, output)
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def write_element(node, output)
|
26
|
+
# output << ' ' * @level
|
27
|
+
output << "<#{node.expanded_name}"
|
28
|
+
write_attributes(node, output)
|
29
|
+
|
30
|
+
if node.children.empty? && @self_close_empty
|
31
|
+
output << "/>"
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
output << ">"
|
36
|
+
|
37
|
+
# Check for mixed content
|
38
|
+
has_text = node.children.any? { |c| c.is_a?(::REXML::Text) && !c.to_s.strip.empty? }
|
39
|
+
has_elements = node.children.any? { |c| c.is_a?(::REXML::Element) }
|
40
|
+
mixed = has_text && has_elements
|
41
|
+
|
42
|
+
# Handle children based on content type
|
43
|
+
unless node.children.empty?
|
44
|
+
@level += @indentation.length unless mixed
|
45
|
+
|
46
|
+
node.children.each_with_index do |child, _index|
|
47
|
+
# Skip insignificant whitespace
|
48
|
+
next if child.is_a?(::REXML::Text) &&
|
49
|
+
child.to_s.strip.empty? &&
|
50
|
+
!(child.next_sibling.nil? && child.previous_sibling.nil?)
|
51
|
+
|
52
|
+
# Indent non-text nodes in non-mixed content
|
53
|
+
# if !mixed && !child.is_a?(::REXML::Text)
|
54
|
+
# output << ' ' * @level
|
55
|
+
# end
|
56
|
+
|
57
|
+
write(child, output)
|
58
|
+
|
59
|
+
# Add newlines between elements in non-mixed content
|
60
|
+
# if !mixed && !child.is_a?(::REXML::Text) && index < node.children.size - 1
|
61
|
+
# output << "\n"
|
62
|
+
# end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Reset indentation for closing tag in non-mixed content
|
66
|
+
unless mixed
|
67
|
+
@level -= @indentation.length
|
68
|
+
# output << ' ' * @level
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
output << "</#{node.expanded_name}>"
|
73
|
+
# output << "\n" unless mixed
|
74
|
+
end
|
75
|
+
|
76
|
+
def write_text(node, output)
|
77
|
+
text = node.value
|
78
|
+
return if text.empty?
|
79
|
+
|
80
|
+
output << escape_text(text)
|
81
|
+
end
|
82
|
+
|
83
|
+
def escape_text(text)
|
84
|
+
text.to_s.gsub(/[<>&]/) do |match|
|
85
|
+
case match
|
86
|
+
when "<" then "<"
|
87
|
+
when ">" then ">"
|
88
|
+
when "&" then "&"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def find_significant_sibling(node, direction)
|
96
|
+
method = direction == :next ? :next_sibling : :previous_sibling
|
97
|
+
sibling = node.send(method)
|
98
|
+
sibling = sibling.send(method) while sibling && sibling.is_a?(::REXML::Text) && sibling.to_s.strip.empty?
|
99
|
+
sibling
|
100
|
+
end
|
101
|
+
|
102
|
+
def write_cdata(node, output)
|
103
|
+
# output << ' ' * @level
|
104
|
+
output << "<![CDATA["
|
105
|
+
output << node.to_s.gsub("]]>", "]]]]><![CDATA[>")
|
106
|
+
output << "]]>"
|
107
|
+
# output << "\n"
|
108
|
+
end
|
109
|
+
|
110
|
+
def write_comment(node, output)
|
111
|
+
# output << ' ' * @level
|
112
|
+
output << "<!--"
|
113
|
+
output << node.to_s
|
114
|
+
output << "-->"
|
115
|
+
# output << "\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
def write_instruction(node, output)
|
119
|
+
# output << ' ' * @level
|
120
|
+
output << "<?"
|
121
|
+
output << node.target
|
122
|
+
output << " "
|
123
|
+
output << node.content if node.content
|
124
|
+
output << "?>"
|
125
|
+
# output << "\n"
|
126
|
+
end
|
127
|
+
|
128
|
+
def write_document(node, output)
|
129
|
+
node.children.each do |child|
|
130
|
+
write(child, output)
|
131
|
+
# output << "\n" unless child == node.children.last
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def write_doctype(node, output)
|
136
|
+
output << "<!DOCTYPE "
|
137
|
+
output << node.name
|
138
|
+
output << " "
|
139
|
+
output << node.external_id if node.external_id
|
140
|
+
output << ">"
|
141
|
+
# output << "\n"
|
142
|
+
end
|
143
|
+
|
144
|
+
def write_declaration(node, output)
|
145
|
+
output << "<?xml"
|
146
|
+
output << %( version="#{node.version}") if node.version
|
147
|
+
output << %( encoding="#{node.encoding.to_s.upcase}") if node.writeencoding
|
148
|
+
output << %( standalone="#{node.standalone}") if node.standalone
|
149
|
+
output << "?>"
|
150
|
+
# output << "\n"
|
151
|
+
end
|
152
|
+
|
153
|
+
def write_attributes(node, output)
|
154
|
+
# First write namespace declarations
|
155
|
+
node.attributes.each do |name, attr|
|
156
|
+
next unless name.to_s.start_with?("xmlns:") || name.to_s == "xmlns"
|
157
|
+
|
158
|
+
name = "xmlns" if name.to_s == "xmlns:" # convert the default namespace
|
159
|
+
value = attr.respond_to?(:value) ? attr.value : attr
|
160
|
+
output << " #{name}=\"#{value}\""
|
161
|
+
end
|
162
|
+
|
163
|
+
# Then write regular attributes
|
164
|
+
node.attributes.each do |name, attr|
|
165
|
+
next if name.to_s.start_with?("xmlns:") || name.to_s == "xmlns"
|
166
|
+
|
167
|
+
output << " "
|
168
|
+
output << if attr.respond_to?(:prefix) && attr.prefix
|
169
|
+
"#{attr.prefix}:#{attr.name}"
|
170
|
+
else
|
171
|
+
name.to_s
|
172
|
+
end
|
173
|
+
|
174
|
+
output << "=\""
|
175
|
+
value = attr.respond_to?(:value) ? attr.value : attr
|
176
|
+
output << escape_attribute_value(value.to_s)
|
177
|
+
output << "\""
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def escape_attribute_value(value)
|
182
|
+
value.to_s.gsub(/[<>&"]/) do |match|
|
183
|
+
case match
|
184
|
+
when "<" then "<"
|
185
|
+
when ">" then ">"
|
186
|
+
when "&" then "&"
|
187
|
+
when '"' then """
|
188
|
+
# when "'" then '''
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
data/lib/moxml/adapter/oga.rb
CHANGED
@@ -213,11 +213,25 @@ module Moxml
|
|
213
213
|
end
|
214
214
|
|
215
215
|
def add_previous_sibling(node, sibling)
|
216
|
-
node.
|
216
|
+
if node.parent == sibling.parent
|
217
|
+
# Oga doesn't manipulate children of the same parent
|
218
|
+
dup_sibling = node.node_set.delete(sibling)
|
219
|
+
index = node.node_set.index(node)
|
220
|
+
node.node_set.insert(index, dup_sibling)
|
221
|
+
else
|
222
|
+
node.before(sibling)
|
223
|
+
end
|
217
224
|
end
|
218
225
|
|
219
226
|
def add_next_sibling(node, sibling)
|
220
|
-
node.
|
227
|
+
if node.parent == sibling.parent
|
228
|
+
# Oga doesn't manipulate children of the same parent
|
229
|
+
dup_sibling = node.node_set.delete(sibling)
|
230
|
+
index = node.node_set.index(node) + 1
|
231
|
+
node.node_set.insert(index, dup_sibling)
|
232
|
+
else
|
233
|
+
node.after(sibling)
|
234
|
+
end
|
221
235
|
end
|
222
236
|
|
223
237
|
def remove(node)
|
@@ -229,7 +243,7 @@ module Moxml
|
|
229
243
|
end
|
230
244
|
|
231
245
|
def replace_children(node, new_children)
|
232
|
-
node.
|
246
|
+
node.children = []
|
233
247
|
new_children.each { |child| add_child(node, child) }
|
234
248
|
end
|
235
249
|
|
@@ -296,14 +310,14 @@ module Moxml
|
|
296
310
|
node.namespaces.values
|
297
311
|
end
|
298
312
|
|
299
|
-
def xpath(node, expression,
|
300
|
-
node.xpath(expression).to_a
|
313
|
+
def xpath(node, expression, namespaces = nil)
|
314
|
+
node.xpath(expression, {}, namespaces: namespaces&.transform_keys(&:to_s)).to_a
|
301
315
|
rescue ::LL::ParserError => e
|
302
316
|
raise Moxml::XPathError, e.message
|
303
317
|
end
|
304
318
|
|
305
|
-
def at_xpath(node, expression,
|
306
|
-
node.at_xpath(expression)
|
319
|
+
def at_xpath(node, expression, namespaces = nil)
|
320
|
+
node.at_xpath(expression, namespaces: namespaces)
|
307
321
|
rescue ::Oga::XPath::Error => e
|
308
322
|
raise Moxml::XPathError, e.message
|
309
323
|
end
|