moxml 0.1.22 → 0.1.24
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/.gitignore +1 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +680 -110
- data/Rakefile +12 -9
- data/lib/compat/opal/moxml_boot.rb +53 -0
- data/lib/compat/opal/rexml/namespace.rb +8 -5
- data/lib/compat/opal/rexml/parsers/baseparser.rb +276 -212
- data/lib/compat/opal/rexml/source.rb +28 -27
- data/lib/compat/opal/rexml/text.rb +112 -104
- data/lib/compat/opal/rexml/xmltokens.rb +8 -8
- data/lib/compat/opal/rexml_compat.rb +12 -11
- data/lib/moxml/adapter/base.rb +5 -5
- data/lib/moxml/adapter/customized_libxml/cdata.rb +0 -2
- data/lib/moxml/adapter/customized_libxml/comment.rb +0 -2
- data/lib/moxml/adapter/customized_libxml/element.rb +3 -5
- data/lib/moxml/adapter/customized_libxml/node.rb +2 -2
- data/lib/moxml/adapter/customized_libxml/processing_instruction.rb +0 -2
- data/lib/moxml/adapter/customized_libxml/text.rb +0 -2
- data/lib/moxml/adapter/customized_oga/xml_declaration.rb +8 -1
- data/lib/moxml/adapter/customized_rexml/formatter.rb +7 -8
- data/lib/moxml/adapter/headed_ox.rb +1 -5
- data/lib/moxml/adapter/libxml/entity_ref_registry.rb +4 -2
- data/lib/moxml/adapter/libxml/entity_restorer.rb +3 -1
- data/lib/moxml/adapter/libxml.rb +29 -14
- data/lib/moxml/adapter/nokogiri.rb +21 -17
- data/lib/moxml/adapter/oga.rb +48 -67
- data/lib/moxml/adapter/ox.rb +61 -31
- data/lib/moxml/adapter/rexml.rb +5 -6
- data/lib/moxml/adapter.rb +13 -5
- data/lib/moxml/attribute.rb +5 -2
- data/lib/moxml/config.rb +16 -2
- data/lib/moxml/context.rb +8 -8
- data/lib/moxml/declaration.rb +2 -7
- data/lib/moxml/document.rb +2 -19
- data/lib/moxml/document_builder.rb +12 -8
- data/lib/moxml/element.rb +4 -4
- data/lib/moxml/entity_registry.rb +8 -4
- data/lib/moxml/entity_registry_opal_data.rb +3 -2
- data/lib/moxml/node.rb +45 -14
- data/lib/moxml/node_set.rb +2 -4
- data/lib/moxml/sax/block_handler.rb +0 -2
- data/lib/moxml/sax/element_handler.rb +0 -2
- data/lib/moxml/sax/namespace_splitter.rb +5 -4
- data/lib/moxml/sax.rb +4 -25
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xml_utils.rb +2 -3
- data/lib/moxml/xpath/compiler.rb +1 -49
- data/lib/moxml/xpath/conversion.rb +7 -6
- data/lib/moxml/xpath/ruby/generator.rb +12 -19
- data/lib/moxml/xpath/ruby/node.rb +1 -9
- data/lib/moxml/xpath.rb +6 -14
- data/lib/moxml.rb +74 -20
- data/spec/integration/all_adapters_spec.rb +1 -0
- data/spec/integration/shared_examples/line_ending_behavior.rb +56 -0
- data/spec/moxml/adapter/libxml_internals_spec.rb +4 -2
- data/spec/moxml/adapter/platform_spec.rb +2 -1
- data/spec/moxml/attribute_spec.rb +16 -0
- data/spec/moxml/config_spec.rb +33 -0
- data/spec/moxml/context_spec.rb +14 -0
- data/spec/moxml/moxml_spec.rb +13 -0
- data/spec/moxml/node_spec.rb +58 -0
- data/spec/moxml/xpath/ruby/node_spec.rb +3 -3
- metadata +4 -2
data/lib/moxml/adapter/ox.rb
CHANGED
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
return if RUBY_ENGINE == "opal"
|
|
4
4
|
|
|
5
|
-
require_relative "base"
|
|
6
5
|
require "ox"
|
|
7
6
|
require "stringio"
|
|
8
|
-
require_relative "customized_ox"
|
|
9
|
-
require_relative "../sax/namespace_splitter"
|
|
10
7
|
|
|
11
8
|
# insert :parent methods to all Ox classes inherit the Node class
|
|
12
9
|
Ox::Node.attr_accessor :parent
|
|
@@ -19,7 +16,16 @@ module Moxml
|
|
|
19
16
|
end
|
|
20
17
|
|
|
21
18
|
def set_root(doc, element)
|
|
22
|
-
|
|
19
|
+
existing_root = root(doc)
|
|
20
|
+
element.parent = doc if element.is_a?(::Ox::Node)
|
|
21
|
+
if existing_root
|
|
22
|
+
# Replace the existing root element, preserving other children
|
|
23
|
+
idx = doc.nodes.index(existing_root)
|
|
24
|
+
doc.nodes[idx] = element
|
|
25
|
+
else
|
|
26
|
+
# No root yet, just append the element
|
|
27
|
+
doc << element
|
|
28
|
+
end
|
|
23
29
|
end
|
|
24
30
|
|
|
25
31
|
def parse(xml, options = {}, _context = nil)
|
|
@@ -101,9 +107,14 @@ module Moxml
|
|
|
101
107
|
end
|
|
102
108
|
|
|
103
109
|
def create_native_doctype(name, external_id, system_id)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
110
|
+
value = if external_id
|
|
111
|
+
"#{name} PUBLIC \"#{external_id}\" \"#{system_id}\""
|
|
112
|
+
elsif system_id
|
|
113
|
+
"#{name} SYSTEM \"#{system_id}\""
|
|
114
|
+
else
|
|
115
|
+
name.to_s
|
|
116
|
+
end
|
|
117
|
+
::Ox::DocType.new(value)
|
|
107
118
|
end
|
|
108
119
|
|
|
109
120
|
def create_native_processing_instruction(target, content)
|
|
@@ -257,7 +268,7 @@ module Moxml
|
|
|
257
268
|
# Ox doesn't set parent references during parsing.
|
|
258
269
|
# Set them here so parent/sibling navigation works.
|
|
259
270
|
result.each do |child|
|
|
260
|
-
child.parent = node if child.
|
|
271
|
+
child.parent = node if child.is_a?(::Ox::Element)
|
|
261
272
|
end
|
|
262
273
|
result
|
|
263
274
|
end
|
|
@@ -368,27 +379,32 @@ module Moxml
|
|
|
368
379
|
end
|
|
369
380
|
|
|
370
381
|
def add_child(element, child)
|
|
371
|
-
# Special handling for declarations on Ox documents
|
|
372
382
|
if element.is_a?(::Ox::Document) && child.is_a?(::Ox::Instruct) && child.target == "xml"
|
|
373
|
-
# Transfer declaration attributes to document
|
|
374
383
|
element.attributes ||= {}
|
|
375
|
-
if child.attributes["version"]
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
if child.attributes["standalone"]
|
|
384
|
-
element.attributes[:standalone] =
|
|
385
|
-
child.attributes["standalone"]
|
|
386
|
-
end
|
|
384
|
+
element.attributes[:version] = child.attributes["version"] if child.attributes["version"]
|
|
385
|
+
element.attributes[:encoding] = child.attributes["encoding"] if child.attributes["encoding"]
|
|
386
|
+
element.attributes[:standalone] = child.attributes["standalone"] if child.attributes["standalone"]
|
|
387
|
+
attachments.set(element, :decl_explicit, {
|
|
388
|
+
encoding: child.attributes.key?("encoding") ? child.attributes["encoding"] : nil,
|
|
389
|
+
standalone: child.attributes.key?("standalone") ? child.attributes["standalone"] : nil,
|
|
390
|
+
})
|
|
391
|
+
return
|
|
387
392
|
end
|
|
388
393
|
|
|
389
394
|
child.parent = element if child.is_a?(::Ox::Node)
|
|
390
395
|
element.nodes ||= []
|
|
391
|
-
|
|
396
|
+
|
|
397
|
+
# Insert doctype before root element in document
|
|
398
|
+
if element.is_a?(::Ox::Document) && child.is_a?(::Ox::DocType)
|
|
399
|
+
root_idx = element.nodes.index { |n| n.is_a?(::Ox::Element) }
|
|
400
|
+
if root_idx
|
|
401
|
+
element.nodes.insert(root_idx, child)
|
|
402
|
+
else
|
|
403
|
+
element.nodes << child
|
|
404
|
+
end
|
|
405
|
+
else
|
|
406
|
+
element.nodes << child
|
|
407
|
+
end
|
|
392
408
|
|
|
393
409
|
# Mark document if EntityReference is added (avoids tree scan in serialize)
|
|
394
410
|
if child.is_a?(::Moxml::Adapter::CustomizedOx::EntityReference)
|
|
@@ -464,8 +480,8 @@ module Moxml
|
|
|
464
480
|
end
|
|
465
481
|
|
|
466
482
|
def assign_parents(node, parent = nil)
|
|
467
|
-
node.parent = parent if node.
|
|
468
|
-
return unless node.
|
|
483
|
+
node.parent = parent if node.is_a?(::Ox::Element) && parent
|
|
484
|
+
return unless node.is_a?(::Ox::Element) || node.is_a?(::Ox::Document)
|
|
469
485
|
|
|
470
486
|
node.nodes&.each do |child|
|
|
471
487
|
assign_parents(child, node)
|
|
@@ -671,12 +687,26 @@ module Moxml
|
|
|
671
687
|
end
|
|
672
688
|
|
|
673
689
|
def has_declaration?(native_doc, _wrapper)
|
|
674
|
-
# Ox stores declaration in document attributes
|
|
675
690
|
native_doc[:version] || native_doc[:encoding] || native_doc[:standalone]
|
|
676
691
|
end
|
|
677
692
|
|
|
693
|
+
def remove_declaration(native_doc)
|
|
694
|
+
native_doc.attributes&.delete(:version)
|
|
695
|
+
native_doc.attributes&.delete(:encoding)
|
|
696
|
+
native_doc.attributes&.delete(:standalone)
|
|
697
|
+
attachments.delete(native_doc, :decl_explicit)
|
|
698
|
+
end
|
|
699
|
+
|
|
678
700
|
private
|
|
679
701
|
|
|
702
|
+
def resolve_decl_attr(node, attr, fallback)
|
|
703
|
+
if attachments.key?(node, :decl_explicit)
|
|
704
|
+
attachments.get(node, :decl_explicit)[attr]
|
|
705
|
+
else
|
|
706
|
+
node[attr] || fallback
|
|
707
|
+
end
|
|
708
|
+
end
|
|
709
|
+
|
|
680
710
|
def serialize_standard(node, options = {})
|
|
681
711
|
output = ""
|
|
682
712
|
if node.is_a?(::Ox::Document)
|
|
@@ -688,8 +718,8 @@ module Moxml
|
|
|
688
718
|
|
|
689
719
|
if should_include_decl
|
|
690
720
|
version = node[:version] || "1.0"
|
|
691
|
-
encoding =
|
|
692
|
-
standalone = node
|
|
721
|
+
encoding = resolve_decl_attr(node, :encoding, options[:encoding])
|
|
722
|
+
standalone = resolve_decl_attr(node, :standalone, nil)
|
|
693
723
|
|
|
694
724
|
decl = create_native_declaration(version, encoding, standalone)
|
|
695
725
|
output = ::Ox.dump(::Ox::Document.new << decl).strip
|
|
@@ -698,7 +728,7 @@ module Moxml
|
|
|
698
728
|
|
|
699
729
|
ox_options = {
|
|
700
730
|
indent: -1,
|
|
701
|
-
with_instructions:
|
|
731
|
+
with_instructions: false,
|
|
702
732
|
encoding: options[:encoding],
|
|
703
733
|
no_empty: options[:expand_empty],
|
|
704
734
|
}
|
|
@@ -750,8 +780,8 @@ module Moxml
|
|
|
750
780
|
end
|
|
751
781
|
if should_include_decl
|
|
752
782
|
version = node[:version] || "1.0"
|
|
753
|
-
encoding =
|
|
754
|
-
standalone = node
|
|
783
|
+
encoding = resolve_decl_attr(node, :encoding, options[:encoding])
|
|
784
|
+
standalone = resolve_decl_attr(node, :standalone, nil)
|
|
755
785
|
output << "<?xml version=\"#{version}\""
|
|
756
786
|
output << " encoding=\"#{encoding}\"" if encoding
|
|
757
787
|
output << " standalone=\"#{standalone}\"" if standalone
|
data/lib/moxml/adapter/rexml.rb
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "base"
|
|
4
3
|
require "rexml/document"
|
|
5
4
|
require "rexml/xpath"
|
|
6
5
|
require "set" unless RUBY_ENGINE == "opal"
|
|
7
6
|
require "stringio" if RUBY_ENGINE == "opal"
|
|
8
|
-
require_relative "customized_rexml"
|
|
9
|
-
require_relative "../sax/namespace_splitter"
|
|
10
7
|
|
|
11
8
|
module Moxml
|
|
12
9
|
module Adapter
|
|
@@ -173,7 +170,7 @@ module Moxml
|
|
|
173
170
|
end
|
|
174
171
|
|
|
175
172
|
def duplicate_node(node)
|
|
176
|
-
if node.
|
|
173
|
+
if node.is_a?(::REXML::Parent)
|
|
177
174
|
node.deep_clone
|
|
178
175
|
else
|
|
179
176
|
Marshal.load(Marshal.dump(node))
|
|
@@ -620,9 +617,7 @@ module Moxml
|
|
|
620
617
|
def has_declaration?(native_doc, wrapper)
|
|
621
618
|
xml_decl = attachments.get(native_doc, :xml_declaration)
|
|
622
619
|
if xml_decl.nil?
|
|
623
|
-
# Attachment key doesn't exist - check native doc or wrapper flag
|
|
624
620
|
if attachments.key?(native_doc, :xml_declaration)
|
|
625
|
-
# Explicitly set to nil (was removed)
|
|
626
621
|
false
|
|
627
622
|
else
|
|
628
623
|
wrapper.has_xml_declaration
|
|
@@ -632,6 +627,10 @@ module Moxml
|
|
|
632
627
|
end
|
|
633
628
|
end
|
|
634
629
|
|
|
630
|
+
def remove_declaration(native_doc)
|
|
631
|
+
attachments.set(native_doc, :xml_declaration, nil)
|
|
632
|
+
end
|
|
633
|
+
|
|
635
634
|
private
|
|
636
635
|
|
|
637
636
|
def write_with_formatter(node, output, indent = 2)
|
data/lib/moxml/adapter.rb
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "adapter/base"
|
|
4
|
-
|
|
5
3
|
module Moxml
|
|
6
4
|
module Adapter
|
|
5
|
+
autoload :Base, "moxml/adapter/base"
|
|
6
|
+
autoload :CustomizedOga, "moxml/adapter/customized_oga"
|
|
7
|
+
autoload :CustomizedOx, "moxml/adapter/customized_ox"
|
|
8
|
+
autoload :CustomizedRexml, "moxml/adapter/customized_rexml"
|
|
9
|
+
autoload :CustomizedLibxml, "moxml/adapter/customized_libxml"
|
|
10
|
+
|
|
7
11
|
AVAILABLE_ADAPTERS = %i[nokogiri oga rexml ox headed_ox libxml].freeze
|
|
8
12
|
|
|
9
13
|
# Adapters that work under the Opal (JavaScript) runtime.
|
|
@@ -46,7 +50,7 @@ module Moxml
|
|
|
46
50
|
def validate_platform!(name)
|
|
47
51
|
return if platform_adapters.include?(name.to_sym)
|
|
48
52
|
|
|
49
|
-
available = platform_adapters.
|
|
53
|
+
available = platform_adapters.join(", ")
|
|
50
54
|
raise Moxml::AdapterError.new(
|
|
51
55
|
"The '#{name}' adapter is not available on this platform. Available: #{available}",
|
|
52
56
|
adapter: name,
|
|
@@ -59,11 +63,15 @@ module Moxml
|
|
|
59
63
|
end
|
|
60
64
|
|
|
61
65
|
def require_adapter(name)
|
|
62
|
-
require
|
|
66
|
+
# Opal pre-loads all dependencies via the Rakefile; skip runtime require.
|
|
67
|
+
return if RUBY_ENGINE == "opal"
|
|
68
|
+
|
|
69
|
+
require "moxml/adapter/base"
|
|
70
|
+
require "moxml/adapter/#{name}"
|
|
63
71
|
rescue LoadError
|
|
64
72
|
begin
|
|
65
73
|
require name.to_s
|
|
66
|
-
require "
|
|
74
|
+
require "moxml/adapter/#{name}"
|
|
67
75
|
rescue LoadError => e
|
|
68
76
|
raise Moxml::AdapterError.new(
|
|
69
77
|
"Failed to load #{name} adapter",
|
data/lib/moxml/attribute.rb
CHANGED
|
@@ -21,6 +21,8 @@ module Moxml
|
|
|
21
21
|
adapter.restore_entities(val)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
alias content value
|
|
25
|
+
|
|
24
26
|
# Returns raw native value without entity marker restoration.
|
|
25
27
|
def raw_value
|
|
26
28
|
@native.value
|
|
@@ -46,11 +48,12 @@ module Moxml
|
|
|
46
48
|
end
|
|
47
49
|
|
|
48
50
|
def element
|
|
49
|
-
adapter.attribute_element(@native)
|
|
51
|
+
native_elem = adapter.attribute_element(@native)
|
|
52
|
+
native_elem && Moxml::Node.wrap(native_elem, context)
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
def remove
|
|
53
|
-
adapter.remove_attribute(
|
|
56
|
+
adapter.remove_attribute(adapter.attribute_element(@native), name)
|
|
54
57
|
if @parent_node.is_a?(Moxml::Element)
|
|
55
58
|
@parent_node.invalidate_attribute_cache!
|
|
56
59
|
end
|
data/lib/moxml/config.rb
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Moxml
|
|
4
4
|
class Config
|
|
5
|
+
LINE_ENDING_LF = "\n"
|
|
6
|
+
LINE_ENDING_CRLF = "\r\n"
|
|
7
|
+
VALID_LINE_ENDINGS = [LINE_ENDING_LF, LINE_ENDING_CRLF].freeze
|
|
5
8
|
VALID_ADAPTERS = %i[nokogiri oga rexml ox headed_ox libxml].freeze
|
|
6
9
|
DEFAULT_ADAPTER = :nokogiri
|
|
7
10
|
OPAL_DEFAULT_ADAPTER = :rexml
|
|
@@ -46,7 +49,7 @@ module Moxml
|
|
|
46
49
|
# - :strict — only restore DTD-declared entities (falls back to lenient until DTD parsing is implemented)
|
|
47
50
|
ENTITY_RESTORATION_MODES = %i[strict lenient].freeze
|
|
48
51
|
|
|
49
|
-
attr_reader :adapter_name
|
|
52
|
+
attr_reader :adapter_name, :default_line_ending
|
|
50
53
|
attr_accessor :strict_parsing,
|
|
51
54
|
:default_encoding,
|
|
52
55
|
:entity_encoding,
|
|
@@ -58,13 +61,23 @@ module Moxml
|
|
|
58
61
|
:namespace_validation_mode,
|
|
59
62
|
:entity_restoration_mode
|
|
60
63
|
|
|
64
|
+
def default_line_ending=(value)
|
|
65
|
+
unless VALID_LINE_ENDINGS.include?(value)
|
|
66
|
+
raise ArgumentError,
|
|
67
|
+
"Invalid line_ending: #{value.inspect}. " \
|
|
68
|
+
"Must be Config::LINE_ENDING_LF or Config::LINE_ENDING_CRLF"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
@default_line_ending = value
|
|
72
|
+
end
|
|
73
|
+
|
|
61
74
|
def initialize(adapter_name = nil, strict_parsing = nil,
|
|
62
75
|
default_encoding = nil)
|
|
63
76
|
self.adapter = adapter_name || Config.default.adapter_name
|
|
64
77
|
@strict_parsing = strict_parsing || Config.default.strict_parsing
|
|
65
78
|
@default_encoding = default_encoding || Config.default.default_encoding
|
|
66
|
-
# reserved for future use
|
|
67
79
|
@default_indent = 2
|
|
80
|
+
@default_line_ending = LINE_ENDING_LF
|
|
68
81
|
@entity_encoding = :basic
|
|
69
82
|
@restore_entities = false
|
|
70
83
|
@preload_entity_sets = []
|
|
@@ -100,6 +113,7 @@ module Moxml
|
|
|
100
113
|
end
|
|
101
114
|
|
|
102
115
|
def entity_load_mode=(mode)
|
|
116
|
+
mode = mode.to_sym
|
|
103
117
|
unless ENTITY_LOAD_MODES.include?(mode)
|
|
104
118
|
raise ArgumentError,
|
|
105
119
|
"Invalid entity_load_mode: #{mode}. Must be one of: #{ENTITY_LOAD_MODES.join(', ')}"
|
data/lib/moxml/context.rb
CHANGED
|
@@ -17,13 +17,12 @@ module Moxml
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def parse(xml, options = {})
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
xml_string = if xml.is_a?(String)
|
|
21
|
+
xml
|
|
22
|
+
else
|
|
22
23
|
xml.read.tap do
|
|
23
|
-
xml.rewind if xml.
|
|
24
|
+
xml.rewind if xml.is_a?(IO) || xml.is_a?(StringIO)
|
|
24
25
|
end
|
|
25
|
-
else
|
|
26
|
-
xml.to_s
|
|
27
26
|
end
|
|
28
27
|
has_declaration = xml_string.strip.start_with?("<?xml")
|
|
29
28
|
|
|
@@ -59,9 +58,6 @@ module Moxml
|
|
|
59
58
|
# end
|
|
60
59
|
#
|
|
61
60
|
def sax_parse(xml, handler = nil, &block)
|
|
62
|
-
# Load SAX module if not already loaded
|
|
63
|
-
require_relative "sax" unless defined?(Moxml::SAX)
|
|
64
|
-
|
|
65
61
|
# Create block handler if block given
|
|
66
62
|
handler ||= SAX::BlockHandler.new(&block) if block
|
|
67
63
|
|
|
@@ -75,6 +71,10 @@ module Moxml
|
|
|
75
71
|
config.adapter.sax_parse(xml, handler)
|
|
76
72
|
end
|
|
77
73
|
|
|
74
|
+
def build(&block)
|
|
75
|
+
Builder.new(self).build(&block)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
78
|
private
|
|
79
79
|
|
|
80
80
|
def build_entity_registry
|
data/lib/moxml/declaration.rb
CHANGED
|
@@ -34,13 +34,8 @@ module Moxml
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def remove
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
native_doc = adapter.document(@native)
|
|
40
|
-
if native_doc && adapter.respond_to?(:attachments)
|
|
41
|
-
adapter.attachments.set(native_doc, :has_declaration, false)
|
|
42
|
-
end
|
|
43
|
-
|
|
37
|
+
native_doc = @parent_node&.native
|
|
38
|
+
adapter.remove_declaration(native_doc) if native_doc
|
|
44
39
|
super
|
|
45
40
|
end
|
|
46
41
|
|
data/lib/moxml/document.rb
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "node"
|
|
4
|
-
require_relative "element"
|
|
5
|
-
require_relative "text"
|
|
6
|
-
require_relative "cdata"
|
|
7
|
-
require_relative "comment"
|
|
8
|
-
require_relative "processing_instruction"
|
|
9
|
-
require_relative "declaration"
|
|
10
|
-
require_relative "namespace"
|
|
11
|
-
require_relative "doctype"
|
|
12
|
-
require_relative "entity_reference"
|
|
13
|
-
|
|
14
3
|
module Moxml
|
|
15
4
|
class Document < Node
|
|
16
5
|
attr_accessor :has_xml_declaration
|
|
@@ -81,14 +70,8 @@ module Moxml
|
|
|
81
70
|
if node.is_a?(Declaration)
|
|
82
71
|
# Mark that document now has a declaration
|
|
83
72
|
@has_xml_declaration = true
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
adapter.add_child(@native, node.native)
|
|
87
|
-
else
|
|
88
|
-
adapter.add_previous_sibling(adapter.children(@native).first,
|
|
89
|
-
node.native)
|
|
90
|
-
end
|
|
91
|
-
elsif root && !node.is_a?(ProcessingInstruction) && !node.is_a?(Comment)
|
|
73
|
+
adapter.add_child(@native, node.native)
|
|
74
|
+
elsif root && !node.is_a?(ProcessingInstruction) && !node.is_a?(Comment) && !node.is_a?(Doctype)
|
|
92
75
|
raise Error, "Document already has a root element"
|
|
93
76
|
else
|
|
94
77
|
adapter.add_child(@native, node.native)
|
|
@@ -13,15 +13,13 @@ module Moxml
|
|
|
13
13
|
@current_doc = context.create_document(native_doc)
|
|
14
14
|
|
|
15
15
|
# Transfer has_declaration flag if present in attachments
|
|
16
|
-
if adapter.
|
|
17
|
-
adapter.attachments.key?(native_doc, :has_declaration)
|
|
16
|
+
if adapter.attachments.key?(native_doc, :has_declaration)
|
|
18
17
|
has_declaration = adapter.attachments.get(native_doc, :has_declaration)
|
|
19
18
|
@current_doc.has_xml_declaration = has_declaration
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
# Transfer DOCTYPE from parsed document if it exists in attachments
|
|
23
|
-
if adapter.
|
|
24
|
-
adapter.attachments.key?(native_doc, :doctype)
|
|
22
|
+
if adapter.attachments.key?(native_doc, :doctype)
|
|
25
23
|
doctype = adapter.attachments.get(native_doc, :doctype)
|
|
26
24
|
if doctype
|
|
27
25
|
adapter.attachments.set(@current_doc.native, :doctype, doctype)
|
|
@@ -35,10 +33,16 @@ module Moxml
|
|
|
35
33
|
private
|
|
36
34
|
|
|
37
35
|
def visit_node(node)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
case node_type(node)
|
|
37
|
+
when :document then visit_document(node)
|
|
38
|
+
when :element then visit_element(node)
|
|
39
|
+
when :text then visit_text(node)
|
|
40
|
+
when :cdata then visit_cdata(node)
|
|
41
|
+
when :comment then visit_comment(node)
|
|
42
|
+
when :processing_instruction then visit_processing_instruction(node)
|
|
43
|
+
when :doctype then visit_doctype(node)
|
|
44
|
+
when :entity_reference then visit_entity_reference(node)
|
|
45
|
+
end
|
|
42
46
|
end
|
|
43
47
|
|
|
44
48
|
def visit_document(doc)
|
data/lib/moxml/element.rb
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "attribute"
|
|
4
|
-
require_relative "namespace"
|
|
5
|
-
|
|
6
3
|
module Moxml
|
|
7
4
|
class Element < Node
|
|
8
5
|
def name
|
|
@@ -137,6 +134,8 @@ module Moxml
|
|
|
137
134
|
adapter.restore_entities(val)
|
|
138
135
|
end
|
|
139
136
|
|
|
137
|
+
alias content text
|
|
138
|
+
|
|
140
139
|
def text=(content)
|
|
141
140
|
adapter.set_text_content(@native, normalize_xml_value(content))
|
|
142
141
|
invalidate_children_cache!
|
|
@@ -158,7 +157,8 @@ module Moxml
|
|
|
158
157
|
end
|
|
159
158
|
|
|
160
159
|
def inner_xml=(xml)
|
|
161
|
-
|
|
160
|
+
wrapper = "_moxml_inner_#{Process.pid}_#{object_id}"
|
|
161
|
+
doc = context.parse("<#{wrapper}>#{xml}</#{wrapper}>")
|
|
162
162
|
adapter.replace_children(@native, doc.root.children.map(&:native))
|
|
163
163
|
invalidate_children_cache!
|
|
164
164
|
end
|
|
@@ -226,7 +226,8 @@ module Moxml
|
|
|
226
226
|
# kept for backward compatibility.
|
|
227
227
|
# @return [self]
|
|
228
228
|
def load_html5
|
|
229
|
-
warn "EntityRegistry#load_html5 is a no-op (all entities load during initialize)",
|
|
229
|
+
warn "EntityRegistry#load_html5 is a no-op (all entities load during initialize)",
|
|
230
|
+
uplevel: 1
|
|
230
231
|
self
|
|
231
232
|
end
|
|
232
233
|
|
|
@@ -235,7 +236,8 @@ module Moxml
|
|
|
235
236
|
# kept for backward compatibility.
|
|
236
237
|
# @return [self]
|
|
237
238
|
def load_mathml
|
|
238
|
-
warn "EntityRegistry#load_mathml is a no-op (all entities load during initialize)",
|
|
239
|
+
warn "EntityRegistry#load_mathml is a no-op (all entities load during initialize)",
|
|
240
|
+
uplevel: 1
|
|
239
241
|
self
|
|
240
242
|
end
|
|
241
243
|
|
|
@@ -245,7 +247,8 @@ module Moxml
|
|
|
245
247
|
# @param _set_name [Symbol] (ignored, all loaded together)
|
|
246
248
|
# @return [self]
|
|
247
249
|
def load_iso(_set_name = :iso8879)
|
|
248
|
-
warn "EntityRegistry#load_iso is a no-op (all entities load during initialize)",
|
|
250
|
+
warn "EntityRegistry#load_iso is a no-op (all entities load during initialize)",
|
|
251
|
+
uplevel: 1
|
|
249
252
|
self
|
|
250
253
|
end
|
|
251
254
|
|
|
@@ -254,7 +257,8 @@ module Moxml
|
|
|
254
257
|
# kept for backward compatibility.
|
|
255
258
|
# @return [self]
|
|
256
259
|
def load_all
|
|
257
|
-
warn "EntityRegistry#load_all is a no-op (all entities load during initialize)",
|
|
260
|
+
warn "EntityRegistry#load_all is a no-op (all entities load during initialize)",
|
|
261
|
+
uplevel: 1
|
|
258
262
|
self
|
|
259
263
|
end
|
|
260
264
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
#
|
|
3
4
|
# Auto-generated entity data for Opal runtime.
|
|
4
|
-
#
|
|
5
|
-
#
|
|
5
|
+
# Source: data/w3c_entities.json (2125 entities)
|
|
6
|
+
# Regenerate with: rake opal:generate_entity_data
|
|
6
7
|
|
|
7
8
|
module Moxml
|
|
8
9
|
class EntityRegistry
|
data/lib/moxml/node.rb
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "xml_utils"
|
|
4
|
-
require_relative "node_set"
|
|
5
|
-
|
|
6
3
|
module Moxml
|
|
7
4
|
class Node
|
|
8
5
|
include XmlUtils
|
|
6
|
+
include Enumerable
|
|
9
7
|
|
|
10
8
|
TYPES = %i[
|
|
11
9
|
element text cdata comment processing_instruction document
|
|
@@ -98,6 +96,7 @@ module Moxml
|
|
|
98
96
|
serialize_options[:no_declaration] = !should_include_declaration?(options)
|
|
99
97
|
|
|
100
98
|
result = adapter.serialize(@native, serialize_options)
|
|
99
|
+
result = apply_line_ending(result, serialize_options[:line_ending])
|
|
101
100
|
|
|
102
101
|
# Restore entity markers to named entity references
|
|
103
102
|
adapter.restore_entities(result)
|
|
@@ -142,13 +141,14 @@ module Moxml
|
|
|
142
141
|
""
|
|
143
142
|
end
|
|
144
143
|
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
144
|
+
# Returns the content/value of this node as a string.
|
|
145
|
+
# Each subclass overrides this with type-specific semantics:
|
|
146
|
+
# - Text, Comment, Cdata: raw text content
|
|
147
|
+
# - ProcessingInstruction: instruction content
|
|
148
|
+
# - Attribute: attribute value
|
|
149
|
+
# - Element: delegates to text (descendant text concatenation)
|
|
150
|
+
def content
|
|
151
|
+
""
|
|
152
152
|
end
|
|
153
153
|
|
|
154
154
|
# Returns the namespace of this node
|
|
@@ -175,15 +175,39 @@ module Moxml
|
|
|
175
175
|
def each_node(&block)
|
|
176
176
|
children.each do |child|
|
|
177
177
|
yield child
|
|
178
|
-
child.each_node(&block)
|
|
178
|
+
child.each_node(&block)
|
|
179
179
|
end
|
|
180
180
|
end
|
|
181
181
|
|
|
182
|
-
#
|
|
183
|
-
def
|
|
182
|
+
# Yield direct children, enabling Enumerable on the node.
|
|
183
|
+
def each(&block)
|
|
184
|
+
return to_enum(:each) unless block
|
|
185
|
+
|
|
186
|
+
children.each(&block)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def outer_xml
|
|
190
|
+
to_xml
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def before(node)
|
|
194
|
+
add_previous_sibling(node)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def after(node)
|
|
198
|
+
add_next_sibling(node)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def blank?
|
|
202
|
+
text.strip.empty?
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Deep copy of the node (both dup and clone create deep copies for XML nodes)
|
|
206
|
+
def dup
|
|
184
207
|
Moxml::Node.wrap(adapter.dup(@native), context)
|
|
185
208
|
end
|
|
186
|
-
|
|
209
|
+
|
|
210
|
+
alias clone dup
|
|
187
211
|
|
|
188
212
|
def ==(other)
|
|
189
213
|
self.class == other.class && @native == other.native
|
|
@@ -279,6 +303,7 @@ module Moxml
|
|
|
279
303
|
{
|
|
280
304
|
encoding: context.config.default_encoding,
|
|
281
305
|
indent: context.config.default_indent,
|
|
306
|
+
line_ending: context.config.default_line_ending,
|
|
282
307
|
# The short format of empty tags in Oga and Nokogiri isn't configurable
|
|
283
308
|
# Oga: <empty /> (with a space)
|
|
284
309
|
# Nokogiri: <empty/> (without a space)
|
|
@@ -294,5 +319,11 @@ module Moxml
|
|
|
294
319
|
# For Document nodes, delegate to adapter for native state check
|
|
295
320
|
adapter.has_declaration?(@native, self)
|
|
296
321
|
end
|
|
322
|
+
|
|
323
|
+
def apply_line_ending(xml, line_ending)
|
|
324
|
+
return xml if line_ending == Config::LINE_ENDING_LF || !xml.include?("\n")
|
|
325
|
+
|
|
326
|
+
xml.gsub(/\r?\n/, line_ending)
|
|
327
|
+
end
|
|
297
328
|
end
|
|
298
329
|
end
|
data/lib/moxml/node_set.rb
CHANGED
|
@@ -68,8 +68,7 @@ module Moxml
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def <<(node)
|
|
71
|
-
|
|
72
|
-
native_node = node.respond_to?(:native) ? node.native : node
|
|
71
|
+
native_node = node.is_a?(Node) ? node.native : node
|
|
73
72
|
@nodes << native_node
|
|
74
73
|
@wrapped << nil
|
|
75
74
|
self
|
|
@@ -113,8 +112,7 @@ module Moxml
|
|
|
113
112
|
# Delete a node from the set
|
|
114
113
|
# Accepts both wrapped Moxml nodes and native nodes
|
|
115
114
|
def delete(node)
|
|
116
|
-
|
|
117
|
-
native_node = node.respond_to?(:native) ? node.native : node
|
|
115
|
+
native_node = node.is_a?(Node) ? node.native : node
|
|
118
116
|
idx = @nodes.index(native_node)
|
|
119
117
|
if idx
|
|
120
118
|
@nodes.delete_at(idx)
|