moxml 0.5.51 → 0.5.52

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb379e02cd764ce5e8cae044e9adc1724f8efe0ba4e5d218f7a8a5621363fa6d
4
- data.tar.gz: ebd5156591fe35f41b6f67d4f54577485615a5d777abb072ad2413d1f02a7a59
3
+ metadata.gz: 5b9e4f8b355a983117cbc435343e414c731791232b8ecee131f5de68fe8e7ce8
4
+ data.tar.gz: 9409d7e0b355d76423acb9abbc812a09665110c53ba29e04cf838d486d257c4e
5
5
  SHA512:
6
- metadata.gz: d8090ca95f64b7d6a42449e2159cac6c68ecef2520c87c231311c2598a8de0a6b35a41f0656068a2816a6f72bbb6cb636033a80f0b94a0c0f903e17fc112b0bc
7
- data.tar.gz: c9c1b1766519455679f8b1fdda3003c5ade352e31846bf2443875944656e5457b08f9607eb603d2a26e5096638fb15eb515537a040b39c7b219829804f3a94e8
6
+ metadata.gz: '028a06c0b8f3e041f4ac257df4104052137f4d64d590d1be2c58f093dbf6909685767924c08914e1624daeb7bb8d419e2ad034ae61003a5099e7c673c7ced58b'
7
+ data.tar.gz: 0f737e2a0055194c05492bfebee65253e5a45fffecdd610e0c1f46b53728e23483acc44cdd48165313c213b8fca597685e1bf8e0f28db6afa8a67c28ae1c4167
@@ -213,9 +213,9 @@ namespace_validation_mode: :strict)
213
213
  create_native_namespace(element, prefix, uri)
214
214
  end
215
215
 
216
- def create_entity_reference(name)
216
+ def create_entity_reference(name, owner_doc = nil)
217
217
  validate_entity_reference_name(name)
218
- create_native_entity_reference(name)
218
+ create_native_entity_reference(name, owner_doc)
219
219
  end
220
220
 
221
221
  # Mutation return contract: protocol methods that may change
@@ -103,6 +103,14 @@ module Moxml
103
103
  NN_ADD_CHILD = NN.instance_method(:add_child)
104
104
  end
105
105
 
106
+ # Entity-reference preservation (leptris-ruby#212 / upstream
107
+ # #1094): 1.9.177 ships ParseOptions.keep_entity_refs plus
108
+ # first-class EntityReference nodes — the marker machinery
109
+ # becomes bypassable when the Context's entity_mode is :keep.
110
+ NATIVE_ENTITY_REFS =
111
+ defined?(::Leptris::XML::EntityReference) &&
112
+ Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.177")
113
+
106
114
  # 1.9.163.2 moved the native bulk children into C
107
115
  # (Native.bulk_children) and fixed the 512 truncation
108
116
  # (leptris-ruby#202). The 162.6-163.1 window carries the
@@ -493,7 +501,15 @@ module Moxml
493
501
  xml_string = xml.is_a?(IO) || xml.is_a?(StringIO) ? xml.read : xml.to_s
494
502
  # The marker flag rides preprocess's own `&` scan — no
495
503
  # second full-buffer probe (issue #132 parse-side note).
496
- processed, entity_markers = Entity.preprocess_with_marker_flag(xml_string)
504
+ entity_mode_keep =
505
+ NATIVE_ENTITY_REFS &&
506
+ (_context&.config&.entity_mode == :keep || options[:keep_entity_refs] == true)
507
+ processed, entity_markers =
508
+ if entity_mode_keep
509
+ [xml_string, false]
510
+ else
511
+ Entity.preprocess_with_marker_flag(xml_string)
512
+ end
497
513
 
498
514
  # readonly: true (issue #133): the binding memoizes reads and
499
515
  # refuses mutations — the parse-and-read lifecycle for
@@ -504,7 +520,7 @@ module Moxml
504
520
  ::Leptris::XML::Document.parse(
505
521
  processed,
506
522
  readonly: options[:readonly] == true,
507
- options: parse_flags(options),
523
+ options: parse_flags(options, context: _context),
508
524
  )
509
525
  rescue ::Leptris::XML::ParseError => e
510
526
  # libleptris has no recovery mode that survives unclosed
@@ -534,6 +550,7 @@ module Moxml
534
550
 
535
551
  record_source_declaration(native_doc, processed)
536
552
  attachments.set(native_doc, :entity_markers, entity_markers)
553
+ attachments.set(native_doc, :keep_entity_refs, entity_mode_keep)
537
554
  bump_serialize_generation
538
555
  attachments.set(native_doc, :parse_errors, recover_errors) if recover_errors
539
556
 
@@ -601,9 +618,12 @@ module Moxml
601
618
  # Nokogiri semantics: blanks kept, no ATTLIST defaults).
602
619
  # noblanks forwards only once the engine flag is libxml2-safe
603
620
  # (see ENGINE_NOBLANKS_SAFE); otherwise it is moxml-side.
604
- def parse_flags(options)
621
+ def parse_flags(options, context: nil)
605
622
  flags = 0
606
623
  flags |= ::Leptris::XML::ParseOptions::DTDATTR if options[:dtdattr] == true
624
+ flags |= ::Leptris::XML::ParseOptions::KEEP_ENTITY_REFS if NATIVE_ENTITY_REFS &&
625
+ (context&.config&.entity_mode == :keep ||
626
+ options[:keep_entity_refs] == true)
607
627
  flags |= ::Leptris::XML::ParseOptions::NOBLANKS if options[:noblanks] == true && ENGINE_NOBLANKS_SAFE
608
628
  flags.zero? ? nil : ::Leptris::XML::ParseOptions.new(flags)
609
629
  end
@@ -698,11 +718,18 @@ module Moxml
698
718
  CustomizedLeptris::Declaration.new(version, encoding, standalone)
699
719
  end
700
720
 
701
- def create_native_entity_reference(name)
721
+ def create_native_entity_reference(name, owner_doc = nil)
722
+ if NATIVE_ENTITY_REFS && owner_doc
723
+ return owner_doc.create_entity_reference(name.to_s)
724
+ end
725
+
702
726
  CustomizedLeptris::EntityReference.new(name)
703
727
  end
704
728
 
705
729
  def entity_reference_name(node)
730
+ return node.name if NATIVE_ENTITY_REFS &&
731
+ node.is_a?(::Leptris::XML::EntityReference)
732
+
706
733
  node.name if node.is_a?(CustomizedLeptris::EntityReference)
707
734
  end
708
735
 
@@ -884,6 +911,7 @@ module Moxml
884
911
  when ::Leptris::XML::DocType, CustomizedLeptris::Doctype then :doctype
885
912
  when CustomizedLeptris::Declaration then :declaration
886
913
  when CustomizedLeptris::EntityReference then :entity_reference
914
+ when ::Leptris::XML::EntityReference then :entity_reference if NATIVE_ENTITY_REFS
887
915
  else :unknown
888
916
  end
889
917
  end
@@ -945,6 +973,11 @@ module Moxml
945
973
  end
946
974
 
947
975
  def children(node, entity_bearing: false)
976
+ if NATIVE_ENTITY_REFS &&
977
+ native_keep_entity_refs?(node)
978
+ return to_binding(node).children.to_a
979
+ end
980
+
948
981
  if NATIVE_READ_LAYER && node.is_a?(::Leptris::XML::NativeNode)
949
982
  # Bulk C children through moxml's own scratch (see
950
983
  # NATIVE_READ_LAYER note above). Entity-marker documents
@@ -1007,6 +1040,13 @@ module Moxml
1007
1040
  end
1008
1041
  end
1009
1042
 
1043
+ def native_keep_entity_refs?(node)
1044
+ return false unless node.is_a?(::Leptris::XML::NativeNode)
1045
+
1046
+ doc = NN_DOCUMENT.bind_call(node)
1047
+ attachments.get(doc, :keep_entity_refs) == true
1048
+ end
1049
+
1010
1050
  def parent(node)
1011
1051
  if NATIVE_READ_LAYER && node.is_a?(::Leptris::XML::NativeNode)
1012
1052
  parent = NN_PARENT.bind_call(node)
@@ -198,7 +198,7 @@ module Moxml
198
198
  CustomizedLibxml::Text.new(native)
199
199
  end
200
200
 
201
- def create_native_entity_reference(name)
201
+ def create_native_entity_reference(name, _owner_doc = nil)
202
202
  CustomizedLibxml::EntityReference.new(name)
203
203
  end
204
204
 
@@ -172,7 +172,7 @@ module Moxml
172
172
  )
173
173
  end
174
174
 
175
- def create_native_entity_reference(name)
175
+ def create_native_entity_reference(name, _owner_doc = nil)
176
176
  ::Nokogiri::XML::EntityReference.new(create_document, name)
177
177
  end
178
178
 
@@ -103,7 +103,7 @@ module Moxml
103
103
  text
104
104
  end
105
105
 
106
- def create_native_entity_reference(name)
106
+ def create_native_entity_reference(name, _owner_doc = nil)
107
107
  text = ::Oga::XML::Text.new
108
108
  text.text = "#{Entity::MARKER}#{name};"
109
109
  attachments.set(text, :entity_name, name)
@@ -561,6 +561,20 @@ module Moxml
561
561
  )
562
562
  end
563
563
 
564
+ # Whether this oga build honors the indent option (newer
565
+ # releases do; the capability probe keeps the indentation
566
+ # spec pending-or-live across both).
567
+ def indents_output?
568
+ return @indents_output unless @indents_output.nil?
569
+
570
+ probe = ::Oga::XML::Document.new
571
+ el = ::Oga::XML::Element.new(name: "r")
572
+ el.children << ::Oga::XML::Text.new(text: "t")
573
+ probe.children << el
574
+ @indents_output = serialize(probe, indent: 2).include?("
575
+ ")
576
+ end
577
+
564
578
  def serialize(node, options = {})
565
579
  serialize_without_entity_processing(node, options)
566
580
  end
@@ -109,7 +109,7 @@ module Moxml
109
109
  content.dup
110
110
  end
111
111
 
112
- def create_native_entity_reference(name)
112
+ def create_native_entity_reference(name, _owner_doc = nil)
113
113
  ::Moxml::Adapter::CustomizedOx::EntityReference.new(name)
114
114
  end
115
115
 
@@ -103,7 +103,7 @@ module Moxml
103
103
  ::REXML::Text.new(content.to_s, true, nil)
104
104
  end
105
105
 
106
- def create_native_entity_reference(name)
106
+ def create_native_entity_reference(name, _owner_doc = nil)
107
107
  ::Moxml::Adapter::CustomizedRexml::EntityReference.new(name)
108
108
  end
109
109
 
data/lib/moxml/config.rb CHANGED
@@ -24,6 +24,13 @@ module Moxml
24
24
  # - :optional - Try to load, continue silently if unavailable
25
25
  # - :disabled - Don't load entities, use empty registry
26
26
  # - :custom - Use custom entity provider via entity_provider callback
27
+ # Entity-reference preservation mode (leptris-ruby#212 / upstream
28
+ # #1094): :expand (default) keeps the shipped behavior (entities
29
+ # expanded at parse, marker-driven round-trip); :keep enables
30
+ # first-class EntityReference nodes and skips the marker machinery
31
+ # when the binding supports it (leptris >= 1.9.177).
32
+ ENTITY_MODES = %i[expand keep].freeze
33
+
27
34
  ENTITY_LOAD_MODES = %i[required optional disabled custom].freeze
28
35
 
29
36
  class << self
@@ -115,6 +122,7 @@ module Moxml
115
122
  :entity_provider,
116
123
  :namespace_validation_mode,
117
124
  :entity_restoration_mode,
125
+ :entity_mode,
118
126
  :ox_skip,
119
127
  :ox_mode
120
128
 
@@ -165,6 +173,7 @@ module Moxml
165
173
  @entity_provider = nil
166
174
  @namespace_validation_mode = :strict
167
175
  @entity_restoration_mode = :lenient
176
+ @entity_mode = :expand
168
177
  end
169
178
 
170
179
  def adapter=(name)
@@ -212,6 +221,16 @@ module Moxml
212
221
  @namespace_validation_mode = mode
213
222
  end
214
223
 
224
+ def entity_mode=(mode)
225
+ mode = mode.to_sym
226
+ unless ENTITY_MODES.include?(mode)
227
+ raise ArgumentError,
228
+ "Invalid entity_mode: #{mode.inspect}. Must be one of: #{ENTITY_MODES.join(', ')}"
229
+ end
230
+
231
+ @entity_mode = mode
232
+ end
233
+
215
234
  def entity_restoration_mode=(mode)
216
235
  mode = mode.to_sym
217
236
  unless ENTITY_RESTORATION_MODES.include?(mode)
@@ -99,7 +99,8 @@ module Moxml
99
99
  end
100
100
 
101
101
  def create_entity_reference(name)
102
- Wrappers::EntityReference.new(adapter.create_entity_reference(name), context)
102
+ native = adapter.create_entity_reference(name, @native)
103
+ Wrappers::EntityReference.new(native, context)
103
104
  end
104
105
 
105
106
  def add_child(node)
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.5.51"
4
+ VERSION = "0.5.52"
5
5
  end
@@ -629,14 +629,46 @@ RSpec.describe Moxml::Adapter::Leptris do
629
629
  expect(doc.at_xpath("//a").to_xml).to eq("<a>text</a>")
630
630
  end
631
631
 
632
- it "flips the flag when the builder mints an entity reference" do
633
- doc = ctx.parse("<root><a/></root>")
634
- expect(described_class.entity_bearing?(doc.root.native)).to be(false)
632
+ it "validates entity_mode" do
633
+ config = Moxml::Config.new(:leptris)
634
+ config.entity_mode = :keep
635
635
 
636
- er = doc.create_entity_reference("nbsp")
637
- doc.at_xpath("//a").add_child(er)
638
- expect(described_class.entity_bearing?(doc.root.native)).to be(true)
639
- expect(doc.at_xpath("//a").children.to_a.map { |k| k.is_a?(Moxml::EntityReference) }).to include(true)
636
+ expect(config.entity_mode).to eq(:keep)
637
+ expect { config.entity_mode = :invalid }.to raise_error(ArgumentError)
638
+ end
639
+
640
+ it "uses first-class entity references when requested on supported bindings" do
641
+ skip "requires leptris 1.9.177" unless described_class::NATIVE_ENTITY_REFS
642
+
643
+ keep_ctx = Moxml.new(:leptris) { |config| config.entity_mode = :keep }
644
+ doc = keep_ctx.parse("<root><a>pre &amp; middle &lt; post</a></root>")
645
+ children = doc.at_xpath("//a").children.to_a
646
+
647
+ expect(children.map(&:content)).to eq(["pre ", "", " middle ", "", " post"])
648
+ expect(children[1]).to be_a(Moxml::EntityReference)
649
+ expect(children[1].name).to eq("amp")
650
+ expect(children[3].name).to eq("lt")
651
+ expect(doc.to_xml).to include("pre &amp; middle &lt; post")
652
+ end
653
+
654
+ it "creates a first-class entity reference on supported bindings" do
655
+ skip "requires leptris 1.9.177" unless described_class::NATIVE_ENTITY_REFS
656
+
657
+ keep_ctx = Moxml.new(:leptris) { |config| config.entity_mode = :keep }
658
+ doc = keep_ctx.parse("<root><a/></root>")
659
+ reference = doc.create_entity_reference("copy")
660
+ doc.at_xpath("//a").add_child(reference)
661
+
662
+ expect(reference).to be_a(Moxml::EntityReference)
663
+ expect(reference.name).to eq("copy")
664
+ expect(doc.to_xml).to include("&copy;")
665
+ end
666
+
667
+ it "keeps expansion as the default entity mode" do
668
+ doc = Moxml.new(:leptris).parse("<root>a &amp; b</root>")
669
+
670
+ expect(doc.root.children.first).to be_a(Moxml::Text)
671
+ expect(doc.root.children.first.content).to eq("a & b")
640
672
  end
641
673
  end
642
674
 
@@ -334,7 +334,8 @@ RSpec.shared_examples "xml adapter" do
334
334
  end
335
335
 
336
336
  it "respects indentation settings" do
337
- if described_class.name.include?("Oga")
337
+ if described_class.name.include?("Oga") &&
338
+ !described_class.indents_output?
338
339
  pending("Oga does not support indentation settings")
339
340
  end
340
341
  if described_class.name.include?("Libxml")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.51
4
+ version: 0.5.52
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.