moxml 0.1.23 → 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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/lib/compat/opal/moxml_boot.rb +53 -0
  4. data/lib/moxml/adapter/base.rb +5 -5
  5. data/lib/moxml/adapter/customized_libxml/cdata.rb +0 -2
  6. data/lib/moxml/adapter/customized_libxml/comment.rb +0 -2
  7. data/lib/moxml/adapter/customized_libxml/element.rb +3 -5
  8. data/lib/moxml/adapter/customized_libxml/node.rb +2 -2
  9. data/lib/moxml/adapter/customized_libxml/processing_instruction.rb +0 -2
  10. data/lib/moxml/adapter/customized_libxml/text.rb +0 -2
  11. data/lib/moxml/adapter/customized_rexml/formatter.rb +3 -4
  12. data/lib/moxml/adapter/headed_ox.rb +1 -5
  13. data/lib/moxml/adapter/libxml.rb +12 -10
  14. data/lib/moxml/adapter/nokogiri.rb +4 -2
  15. data/lib/moxml/adapter/oga.rb +10 -10
  16. data/lib/moxml/adapter/ox.rb +29 -16
  17. data/lib/moxml/adapter/rexml.rb +5 -6
  18. data/lib/moxml/adapter.rb +12 -4
  19. data/lib/moxml/attribute.rb +5 -2
  20. data/lib/moxml/config.rb +1 -0
  21. data/lib/moxml/context.rb +8 -8
  22. data/lib/moxml/declaration.rb +2 -7
  23. data/lib/moxml/document.rb +0 -11
  24. data/lib/moxml/document_builder.rb +12 -8
  25. data/lib/moxml/element.rb +4 -4
  26. data/lib/moxml/node.rb +37 -14
  27. data/lib/moxml/node_set.rb +2 -4
  28. data/lib/moxml/sax/block_handler.rb +0 -2
  29. data/lib/moxml/sax/element_handler.rb +0 -2
  30. data/lib/moxml/sax/namespace_splitter.rb +5 -4
  31. data/lib/moxml/sax.rb +4 -25
  32. data/lib/moxml/version.rb +1 -1
  33. data/lib/moxml/xml_utils.rb +1 -3
  34. data/lib/moxml/xpath/compiler.rb +1 -49
  35. data/lib/moxml/xpath/conversion.rb +7 -6
  36. data/lib/moxml/xpath/ruby/generator.rb +12 -19
  37. data/lib/moxml/xpath/ruby/node.rb +1 -9
  38. data/lib/moxml/xpath.rb +6 -14
  39. data/lib/moxml.rb +67 -20
  40. data/spec/moxml/attribute_spec.rb +16 -0
  41. data/spec/moxml/context_spec.rb +14 -0
  42. data/spec/moxml/moxml_spec.rb +13 -0
  43. data/spec/moxml/node_spec.rb +58 -0
  44. data/spec/moxml/xpath/ruby/node_spec.rb +3 -3
  45. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 747d843c7a968e3a25871ac9e5c67dead62e5a494a23eed41af6e157df9d66e4
4
- data.tar.gz: a636fb5fc49e0c5ebcda6a16484e2685957d9e8666b0bf043d16e5f84bcb0e57
3
+ metadata.gz: 1c61c5dcecbe10b9f0f2850a134c9a8c5e2ec7d0be5b5b1dbb828dac25019a55
4
+ data.tar.gz: f418e1f7a7406bfcc0dd9e57bc2a6b372a332b0e6677821a9e2b7a8173871b45
5
5
  SHA512:
6
- metadata.gz: 96248578b7b4294981437cd73baa349c99f9b01a259800c3ac48662e84fdfe4cc84fc34eef4f3d47e7f7be5517c3e4fa895e48021cd73166dcaf9867533a6461
7
- data.tar.gz: 733f99bd21656a83e17b8b6d52d08d12a19c7425364d2c8266cc2033962a8f506f8f0f603e0cdf5f0e17843dbf8b6a785b1e4462aef82c7aa173f4a8dc3d0ea2
6
+ metadata.gz: 44ccdeb45ec15bdadb02a470bd8506f051ee22385a0832b78d17b607ef32a192b28f0ec716f7910f2ba22c106b0a48ffec85aaaf1dbe33f7ce4a1f4fd7108695
7
+ data.tar.gz: 49ed1641cd66fe4efa02f0e072b07621f441e3936734aba4ebb58a2f9188fab5feae920c989b21476019b01fac1562afc36356059d4c979f9a41b8f3e29686ec
data/Rakefile CHANGED
@@ -35,7 +35,7 @@ namespace :spec do
35
35
 
36
36
  runner.default_path = "spec"
37
37
  runner.requires = %w[rexml_compat rexml/document rexml/xpath
38
- moxml/adapter/rexml spec_helper support/opal]
38
+ moxml_boot spec_helper support/opal]
39
39
  runner.files = Dir.glob("spec/moxml/*opal*_spec.rb") +
40
40
  Dir.glob("spec/moxml/native_attachment/opal_spec.rb") +
41
41
  Dir.glob("spec/moxml/adapter/shared_examples/*.rb")
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Opal doesn't support Ruby autoload. This file explicitly requires
4
+ # all Moxml modules that would normally be lazy-loaded via autoload
5
+ # in lib/moxml.rb. Loaded by the Opal Rakefile before spec_helper.
6
+
7
+ require "moxml/version"
8
+ require "moxml/error"
9
+ require "moxml/native_attachment"
10
+ require "moxml/native_attachment/opal"
11
+ require "moxml/xml_utils"
12
+ require "moxml/xml_utils/encoder"
13
+ require "moxml/node"
14
+ require "moxml/node_set"
15
+ require "moxml/document"
16
+ require "moxml/element"
17
+ require "moxml/attribute"
18
+ require "moxml/text"
19
+ require "moxml/cdata"
20
+ require "moxml/comment"
21
+ require "moxml/processing_instruction"
22
+ require "moxml/declaration"
23
+ require "moxml/namespace"
24
+ require "moxml/doctype"
25
+ require "moxml/entity_reference"
26
+ require "moxml/entity_registry"
27
+ require "moxml/adapter"
28
+ require "moxml/adapter/base"
29
+ require "moxml/adapter/customized_rexml"
30
+ require "moxml/adapter/customized_rexml/entity_reference"
31
+ require "moxml/adapter/customized_rexml/formatter"
32
+ require "moxml/sax"
33
+ require "moxml/sax/handler"
34
+ require "moxml/sax/element_handler"
35
+ require "moxml/sax/block_handler"
36
+ require "moxml/sax/namespace_splitter"
37
+ require "moxml/adapter/rexml"
38
+ require "moxml/document_builder"
39
+ require "moxml/builder"
40
+ require "moxml/context"
41
+ require "moxml/config"
42
+ require "moxml/xpath"
43
+ require "moxml/xpath/engine"
44
+ require "moxml/xpath/context"
45
+ require "moxml/xpath/conversion"
46
+ require "moxml/xpath/cache"
47
+ require "moxml/xpath/lexer"
48
+ require "moxml/xpath/parser"
49
+ require "moxml/xpath/compiler"
50
+ require "moxml/xpath/errors"
51
+ require "moxml/xpath/ast/node"
52
+ require "moxml/xpath/ruby/node"
53
+ require "moxml/xpath/ruby/generator"
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../xml_utils"
4
- require_relative "../document_builder"
5
-
6
3
  module Moxml
7
4
  module Adapter
8
5
  class Base
@@ -104,8 +101,7 @@ module Moxml
104
101
  #
105
102
  # @return [Boolean] true if SAX parsing is supported
106
103
  def sax_supported?
107
- respond_to?(:sax_parse) &&
108
- method(:sax_parse).owner != Moxml::Adapter::Base.singleton_class
104
+ method(:sax_parse).owner != Moxml::Adapter::Base.singleton_class
109
105
  end
110
106
 
111
107
  def create_document(_native_doc = nil)
@@ -212,6 +208,10 @@ namespace_validation_mode: :strict)
212
208
  wrapper.has_xml_declaration
213
209
  end
214
210
 
211
+ # Clear the declaration state from the native document.
212
+ # Called when a Declaration node is removed from a document.
213
+ def remove_declaration(_native_doc); end
214
+
215
215
  # Return the actual native node after an add_child operation.
216
216
  # Override for adapters where node identity may change (e.g., LibXML doc.root=).
217
217
  def actual_native(child_native, _parent_native)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "node"
4
-
5
3
  module Moxml
6
4
  module Adapter
7
5
  module CustomizedLibxml
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "node"
4
-
5
3
  module Moxml
6
4
  module Adapter
7
5
  module CustomizedLibxml
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "node"
4
-
5
3
  module Moxml
6
4
  module Adapter
7
5
  module CustomizedLibxml
@@ -12,7 +10,7 @@ module Moxml
12
10
  class Element < Node
13
11
  # Add a child to this element, handling document import automatically
14
12
  def add_child(child)
15
- child_native = child.respond_to?(:native) ? child.native : child
13
+ child_native = child.is_a?(Node) ? child.native : child
16
14
 
17
15
  # Check if child needs to be imported
18
16
  if needs_import?(child_native)
@@ -26,9 +24,9 @@ module Moxml
26
24
  private
27
25
 
28
26
  def needs_import?(child_node)
29
- return false unless @native.respond_to?(:doc)
27
+ return false unless @native.is_a?(::LibXML::XML::Node)
30
28
  return false unless @native.doc
31
- return false unless child_node.respond_to?(:doc)
29
+ return false unless child_node.is_a?(::LibXML::XML::Node)
32
30
  return false unless child_node.doc
33
31
 
34
32
  child_node.doc != @native.doc
@@ -22,7 +22,7 @@ module Moxml
22
22
  def ==(other)
23
23
  return false unless other
24
24
 
25
- other_native = other.respond_to?(:native) ? other.native : other
25
+ other_native = other.is_a?(self.class) ? other.native : other
26
26
  @native == other_native
27
27
  end
28
28
 
@@ -34,7 +34,7 @@ module Moxml
34
34
 
35
35
  # Check if node has a document
36
36
  def document_present?
37
- @native.respond_to?(:doc) && !@native.doc.nil?
37
+ @native.is_a?(::LibXML::XML::Node) && !@native.doc.nil?
38
38
  end
39
39
 
40
40
  # Get the document this node belongs to
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "node"
4
-
5
3
  module Moxml
6
4
  module Adapter
7
5
  module CustomizedLibxml
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "node"
4
-
5
3
  module Moxml
6
4
  module Adapter
7
5
  module CustomizedLibxml
@@ -185,9 +185,8 @@ module Moxml
185
185
  node.attributes.each do |name, attr|
186
186
  next unless name.to_s.start_with?("xmlns:") || name.to_s == "xmlns"
187
187
 
188
- # convert the default namespace
189
188
  name = "xmlns" if name.to_s == "xmlns:"
190
- value = attr.respond_to?(:value) ? attr.value : attr
189
+ value = attr.is_a?(::REXML::Attribute) ? attr.value : attr
191
190
  output << " #{name}=\"#{value}\""
192
191
  end
193
192
 
@@ -196,14 +195,14 @@ module Moxml
196
195
  next if name.to_s.start_with?("xmlns:") || name.to_s == "xmlns"
197
196
 
198
197
  output << " "
199
- output << if attr.respond_to?(:prefix) && attr.prefix
198
+ output << if attr.is_a?(::REXML::Attribute) && attr.prefix
200
199
  "#{attr.prefix}:#{attr.name}"
201
200
  else
202
201
  name.to_s
203
202
  end
204
203
 
205
204
  output << "=\""
206
- value = attr.respond_to?(:value) ? attr.value : attr
205
+ value = attr.is_a?(::REXML::Attribute) ? attr.value : attr
207
206
  output << escape_attribute_value(value.to_s)
208
207
  output << "\""
209
208
  end # rubocop:enable Style/CombinableLoops
@@ -2,11 +2,7 @@
2
2
 
3
3
  return if RUBY_ENGINE == "opal"
4
4
 
5
- require_relative "ox"
6
- require_relative "../xpath"
7
- # Force load XPath modules (autoload doesn't work well with relative requires in examples)
8
- require_relative "../xpath/parser"
9
- require_relative "../xpath/compiler"
5
+ require "moxml/adapter/ox"
10
6
 
11
7
  module Moxml
12
8
  module Adapter
@@ -2,14 +2,14 @@
2
2
 
3
3
  return if RUBY_ENGINE == "opal"
4
4
 
5
- require_relative "base"
6
5
  require "libxml"
7
- require_relative "customized_libxml"
8
- require_relative "../sax/namespace_splitter"
9
6
 
10
7
  module Moxml
11
8
  module Adapter
12
9
  class Libxml < Base
10
+ autoload :EntityRefRegistry, "moxml/adapter/libxml/entity_ref_registry"
11
+ autoload :EntityRestorer, "moxml/adapter/libxml/entity_restorer"
12
+
13
13
  # Wrapper class to store DOCTYPE information
14
14
  class DoctypeWrapper
15
15
  attr_reader :native_doc
@@ -239,7 +239,7 @@ module Moxml
239
239
  # Duck-typed fallback for libxml types that aren't ::Node
240
240
  # subclasses but still expose node_type (e.g. ::Attr).
241
241
  native = unpatch_node(node)
242
- return :unknown unless native.respond_to?(:node_type)
242
+ return :unknown unless native.is_a?(::LibXML::XML::Node)
243
243
 
244
244
  NATIVE_NODE_TYPE_MAP[native.node_type] || :unknown
245
245
  end
@@ -836,7 +836,7 @@ module Moxml
836
836
  return [] unless namespaces
837
837
 
838
838
  namespace_list =
839
- if namespaces.respond_to?(:definitions)
839
+ if namespaces.is_a?(::LibXML::XML::Namespaces)
840
840
  namespaces.definitions
841
841
  else
842
842
  namespaces
@@ -1076,6 +1076,11 @@ module Moxml
1076
1076
  end
1077
1077
  end
1078
1078
 
1079
+ def remove_declaration(native_doc)
1080
+ decl = attachments.get(native_doc, :declaration)
1081
+ decl&.removed = true
1082
+ end
1083
+
1079
1084
  # LibXML's doc.root= creates a new Ruby wrapper with different object_id.
1080
1085
  # Return the actual root node so attachments are stored on the correct object.
1081
1086
  def actual_native(child_native, parent_native)
@@ -1319,7 +1324,7 @@ module Moxml
1319
1324
  return unless elem.is_a?(::LibXML::XML::Node)
1320
1325
 
1321
1326
  ns_list = elem.namespaces
1322
- return unless ns_list.respond_to?(:definitions)
1327
+ return unless ns_list.is_a?(::LibXML::XML::Namespaces)
1323
1328
 
1324
1329
  definitions = ns_list.definitions
1325
1330
  return if definitions.empty?
@@ -1551,7 +1556,7 @@ module Moxml
1551
1556
 
1552
1557
  # Also check if this element has an active namespace (inherited or own)
1553
1558
  # This catches cases where elements inherit namespaces from parents
1554
- if node.is_a?(::LibXML::XML::Node) && node.namespaces.respond_to?(:namespace)
1559
+ if node.is_a?(::LibXML::XML::Node) && node.namespaces.is_a?(::LibXML::XML::Namespaces)
1555
1560
  active_ns = node.namespaces.namespace
1556
1561
  if active_ns
1557
1562
  prefix = active_ns.prefix
@@ -1720,6 +1725,3 @@ module Moxml
1720
1725
  end
1721
1726
  end
1722
1727
  end
1723
-
1724
- require_relative "libxml/entity_ref_registry"
1725
- require_relative "libxml/entity_restorer"
@@ -2,9 +2,7 @@
2
2
 
3
3
  return if RUBY_ENGINE == "opal"
4
4
 
5
- require_relative "base"
6
5
  require "nokogiri"
7
- require_relative "../sax/namespace_splitter"
8
6
 
9
7
  module Moxml
10
8
  module Adapter
@@ -426,6 +424,10 @@ module Moxml
426
424
  end
427
425
  end
428
426
 
427
+ def remove_declaration(native_doc)
428
+ attachments.set(native_doc, :xml_decl, nil)
429
+ end
430
+
429
431
  private
430
432
 
431
433
  def build_declaration_attrs(version, encoding, standalone)
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
- require_relative "customized_oga"
5
3
  require "oga"
6
- require_relative "../sax/namespace_splitter"
7
4
 
8
5
  module Moxml
9
6
  module Adapter
@@ -470,13 +467,16 @@ module Moxml
470
467
  def has_declaration?(native_doc, _wrapper)
471
468
  decl = attachments.get(native_doc, :xml_declaration)
472
469
  if decl.nil? && !attachments.key?(native_doc, :xml_declaration)
473
- # No attachment entry - check native doc (for parsed documents)
474
- native_doc.respond_to?(:xml_declaration) && !native_doc.xml_declaration.nil?
470
+ native_doc.is_a?(::Oga::XML::Document) && !native_doc.xml_declaration.nil?
475
471
  else
476
472
  !decl.nil?
477
473
  end
478
474
  end
479
475
 
476
+ def remove_declaration(native_doc)
477
+ attachments.set(native_doc, :xml_declaration, nil)
478
+ end
479
+
480
480
  private
481
481
 
482
482
  def declaration_to_xml(decl)
@@ -503,11 +503,11 @@ module Moxml
503
503
 
504
504
  if should_include_decl
505
505
  decl = effective_xml_declaration || node.xml_declaration
506
- if decl
507
- output << declaration_to_xml(decl)
508
- else
509
- output << '<?xml version="1.0" encoding="UTF-8"?>'
510
- end
506
+ output << if decl
507
+ declaration_to_xml(decl)
508
+ else
509
+ '<?xml version="1.0" encoding="UTF-8"?>'
510
+ end
511
511
  output << "\n"
512
512
  end
513
513
 
@@ -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
@@ -20,14 +17,13 @@ module Moxml
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)
23
21
  if existing_root
24
22
  # Replace the existing root element, preserving other children
25
- element.parent = doc if element.is_a?(::Ox::Node)
26
23
  idx = doc.nodes.index(existing_root)
27
24
  doc.nodes[idx] = element
28
25
  else
29
26
  # No root yet, just append the element
30
- element.parent = doc if element.is_a?(::Ox::Node)
31
27
  doc << element
32
28
  end
33
29
  end
@@ -116,7 +112,7 @@ module Moxml
116
112
  elsif system_id
117
113
  "#{name} SYSTEM \"#{system_id}\""
118
114
  else
119
- "#{name}"
115
+ name.to_s
120
116
  end
121
117
  ::Ox::DocType.new(value)
122
118
  end
@@ -272,7 +268,7 @@ module Moxml
272
268
  # Ox doesn't set parent references during parsing.
273
269
  # Set them here so parent/sibling navigation works.
274
270
  result.each do |child|
275
- child.parent = node if child.respond_to?(:parent=)
271
+ child.parent = node if child.is_a?(::Ox::Element)
276
272
  end
277
273
  result
278
274
  end
@@ -383,12 +379,15 @@ module Moxml
383
379
  end
384
380
 
385
381
  def add_child(element, child)
386
- # Special handling for declarations on Ox documents
387
382
  if element.is_a?(::Ox::Document) && child.is_a?(::Ox::Instruct) && child.target == "xml"
388
383
  element.attributes ||= {}
389
384
  element.attributes[:version] = child.attributes["version"] if child.attributes["version"]
390
385
  element.attributes[:encoding] = child.attributes["encoding"] if child.attributes["encoding"]
391
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
+ })
392
391
  return
393
392
  end
394
393
 
@@ -481,8 +480,8 @@ module Moxml
481
480
  end
482
481
 
483
482
  def assign_parents(node, parent = nil)
484
- node.parent = parent if node.respond_to?(:parent=) && parent
485
- return unless node.respond_to?(:nodes)
483
+ node.parent = parent if node.is_a?(::Ox::Element) && parent
484
+ return unless node.is_a?(::Ox::Element) || node.is_a?(::Ox::Document)
486
485
 
487
486
  node.nodes&.each do |child|
488
487
  assign_parents(child, node)
@@ -688,12 +687,26 @@ module Moxml
688
687
  end
689
688
 
690
689
  def has_declaration?(native_doc, _wrapper)
691
- # Ox stores declaration in document attributes
692
690
  native_doc[:version] || native_doc[:encoding] || native_doc[:standalone]
693
691
  end
694
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
+
695
700
  private
696
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
+
697
710
  def serialize_standard(node, options = {})
698
711
  output = ""
699
712
  if node.is_a?(::Ox::Document)
@@ -705,8 +718,8 @@ module Moxml
705
718
 
706
719
  if should_include_decl
707
720
  version = node[:version] || "1.0"
708
- encoding = options[:encoding] || node[:encoding]
709
- standalone = node[:standalone]
721
+ encoding = resolve_decl_attr(node, :encoding, options[:encoding])
722
+ standalone = resolve_decl_attr(node, :standalone, nil)
710
723
 
711
724
  decl = create_native_declaration(version, encoding, standalone)
712
725
  output = ::Ox.dump(::Ox::Document.new << decl).strip
@@ -715,7 +728,7 @@ module Moxml
715
728
 
716
729
  ox_options = {
717
730
  indent: -1,
718
- with_instructions: true,
731
+ with_instructions: false,
719
732
  encoding: options[:encoding],
720
733
  no_empty: options[:expand_empty],
721
734
  }
@@ -767,8 +780,8 @@ module Moxml
767
780
  end
768
781
  if should_include_decl
769
782
  version = node[:version] || "1.0"
770
- encoding = options[:encoding] || node[:encoding]
771
- standalone = node[:standalone]
783
+ encoding = resolve_decl_attr(node, :encoding, options[:encoding])
784
+ standalone = resolve_decl_attr(node, :standalone, nil)
772
785
  output << "<?xml version=\"#{version}\""
773
786
  output << " encoding=\"#{encoding}\"" if encoding
774
787
  output << " standalone=\"#{standalone}\"" if standalone
@@ -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.respond_to?(:deep_clone)
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.
@@ -59,11 +63,15 @@ module Moxml
59
63
  end
60
64
 
61
65
  def require_adapter(name)
62
- require "#{__dir__}/adapter/#{name}"
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 "#{__dir__}/adapter/#{name}"
74
+ require "moxml/adapter/#{name}"
67
75
  rescue LoadError => e
68
76
  raise Moxml::AdapterError.new(
69
77
  "Failed to load #{name} adapter",
@@ -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(element, name)
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
@@ -113,6 +113,7 @@ module Moxml
113
113
  end
114
114
 
115
115
  def entity_load_mode=(mode)
116
+ mode = mode.to_sym
116
117
  unless ENTITY_LOAD_MODES.include?(mode)
117
118
  raise ArgumentError,
118
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
- # Detect if input has XML declaration
21
- xml_string = if xml.respond_to?(:read)
20
+ xml_string = if xml.is_a?(String)
21
+ xml
22
+ else
22
23
  xml.read.tap do
23
- xml.rewind if xml.respond_to?(:rewind)
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
@@ -34,13 +34,8 @@ module Moxml
34
34
  end
35
35
 
36
36
  def remove
37
- # Mark document as having no declaration when declaration is removed
38
- # Store in adapter's attachment map so all wrappers see it
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