moxml 0.5.16 → 0.5.17

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: 39cfbe08ea2a0e92f2ba89e83beacf564401765e964807dc4d73ca23d867af38
4
- data.tar.gz: cdd5d499fd4b3233a6c4d2774b188aef2339f341f7c7a7112a82ac392ec4e53b
3
+ metadata.gz: 6de47d8ce88e56853b7523dfbcb2049e61d586878dca573d13bacd85138b4a4e
4
+ data.tar.gz: 520a050794c1275e5bc519454319a34fd059b9bbcf3f0efd0f5d8e0876bb7e97
5
5
  SHA512:
6
- metadata.gz: 894d6bacdd4f26dbb2a59b0684b29e8ca68b39b66f841a5cd11005351eff38404e745b47d58bb860c898d3da92e1e2a769210938f26cac2f88cac34cc887de76
7
- data.tar.gz: c5b7b7c9de4f2362e910b740ce4cf6185a72773f39f31a3dfc17fbd7436f65f542e66ff099900bf9073e87bd5975568cb3833ac658115ea5ee21a4218719ce90
6
+ metadata.gz: 6b197e6da24e0b40ead4fa25ee944b242ee3484475b2927a16e126faa208f8ff4bb7709795b1d2830b1394fbb0fad8d28f99ed32ec9dbf902710752286965e7c
7
+ data.tar.gz: 89b1ae523cf06687e67e4d07a088cdef2f942a1b4dcff8ba56f468fb887b1ac3bba936b10145c057eb0f6cf767c9e7ba35e86fb753854ee391f29951f2e17485
@@ -55,7 +55,9 @@ the tracked native; wrappers re-key through `refresh_native!`.
55
55
 
56
56
  Serialization::
57
57
  `serialize(node, options)`. Options: `indent`, `encoding`,
58
- `declaration`, `no_declaration`, `expand_empty`, `line_ending`.
58
+ `declaration`, `no_declaration`, `expand_empty`, `line_ending`,
59
+ `indent_text` (the indent-unit string — honored where the engine
60
+ has it, e.g. Nokogiri; leptris waits on leptris-ruby#109).
59
61
  Entity-marker restoration is owned by the wrapper layer
60
62
  (`Node#to_xml`), guarded by `entity_bearing?`.
61
63
 
@@ -62,16 +62,18 @@ implementation over one `leptris_node_traverse` call.
62
62
 
63
63
  == Known limitations (tracked upstream)
64
64
 
65
- * Tab indentation (`indent_text`) needs a serialize-time parameter in
66
- libleptris; post-hoc rewriting would corrupt mixed content.
67
- * Child PIs serialize inline (engine emits `<r>\n<?pi x?> <e/>`
68
- rather than libxml2's one-PI-per-indented-line layout); safe
69
- post-hoc rewriting is impossible in mixed content.
70
- * DOCTYPE internal subsets serialize inline; libxml2 separates the
71
- declarations with newlines.
72
- * Document-level PIs are read-only natives (`target=`/`data=`/
73
- `unlink` rejected); write-through and removal are pending C
74
- surface (leptris/leptris#612).
75
65
  * A PI preceding the DOCTYPE reorders on serialization — libleptris
76
66
  normalizes DOCTYPE-first and the document node does not record its
77
67
  relative position.
68
+ * Tab indentation: the binding's `indent_text` is the display-form
69
+ boolean, not Nokogiri's indent-unit string — the unit ask is
70
+ leptris-ruby#109. moxml forwards `indent_text:` to Nokogiri
71
+ meanwhile.
72
+
73
+ Resolved with leptris-ruby 1.9.42 (leptris/leptris#636/#612): child
74
+ PIs serialize on their own indented lines, DOCTYPE internal subsets
75
+ newline-separate their declarations (moxml re-formats the subset to
76
+ the libxml2 layout), the stray trailing newline after non-ASCII
77
+ ending elements is gone engine-side, and document-level PIs accept
78
+ `target=`/`content=`/removal with full round-trip. The moxml-side
79
+ compat guards remain as no-ops for older floor bindings.
@@ -100,4 +100,9 @@ context.parse(src).to_xml(
100
100
  ) == Nokogiri::XML(src).to_xml(indent: 2, encoding: "UTF-8") # => true
101
101
  ----
102
102
 
103
- Known residual divergences (engine-level, tracked upstream): tab indentation needs a serialize-time parameter in libleptris, and a PI preceding the DOCTYPE reorders (the engine normalizes DOCTYPE-first).
103
+ Known residual divergences (engine-level, tracked upstream): a PI
104
+ preceding the DOCTYPE reorders (the engine normalizes DOCTYPE-first),
105
+ and tab indentation waits on the binding exposing an indent-unit
106
+ parameter (leptris-ruby#109) — child-PI lines, DOCTYPE subset
107
+ layout, and the non-ASCII trailing-newline quirk were resolved with
108
+ leptris-ruby 1.9.42.
@@ -113,9 +113,75 @@ module Moxml
113
113
  return nil unless dt
114
114
 
115
115
  subset = dt.internal_subset if dt.class.method_defined?(:internal_subset)
116
+ subset = format_internal_subset(subset) if LIBXML2_LAYOUT_PARITY
116
117
  XmlEmitter.doctype_xml(dt.root_name, dt.public_id, dt.system_id, subset)
117
118
  end
118
119
 
120
+ # libxml2's DTD dump layout (leptris/leptris#636): newline
121
+ # after "[", one after every markup declaration, none after
122
+ # comments (they glue to both neighbors); an empty subset
123
+ # drops the brackets. Returns the INNER text for
124
+ # XmlEmitter.doctype_xml, nil when there is nothing to emit.
125
+ # The engine reports internal_subset as raw source text, so
126
+ # the declarations are re-tokenized — quote-aware, since an
127
+ # attribute default can contain ">".
128
+ def format_internal_subset(subset)
129
+ return nil if subset.nil? || subset.empty?
130
+
131
+ out = +"\n"
132
+ pos = 0
133
+ length = subset.length
134
+ while pos < length
135
+ start = subset.index("<", pos)
136
+ break if start.nil?
137
+
138
+ terminator, skip = if subset[start, 4] == "<!--"
139
+ ["-->", 4]
140
+ elsif subset[start, 2] == "<?"
141
+ ["?>", 2]
142
+ else
143
+ [nil, 0]
144
+ end
145
+ if terminator
146
+ stop = subset.index(terminator, start + skip)
147
+ break if stop.nil?
148
+
149
+ item_end = stop + terminator.length
150
+ else
151
+ item_end = markup_decl_end(subset, start)
152
+ break if item_end.nil?
153
+ end
154
+ out << subset[start...item_end]
155
+ # Comments carry no trailing newline; declarations do.
156
+ out << "\n" unless subset[start, 4] == "<!--"
157
+ pos = item_end
158
+ end
159
+ out == "\n" ? nil : out
160
+ end
161
+
162
+ # End index of a markup declaration starting at `start`:
163
+ # the first ">" outside quotes.
164
+ QUOTE_CHARS = ['"', "'"].freeze
165
+ private_constant :QUOTE_CHARS
166
+
167
+ def markup_decl_end(subset, start)
168
+ quote = nil
169
+ i = start
170
+ length = subset.length
171
+ while i < length
172
+ ch = subset[i]
173
+ if quote
174
+ quote = nil if ch == quote
175
+ elsif QUOTE_CHARS.include?(ch)
176
+ quote = ch
177
+ elsif ch == ">"
178
+ return i + 1
179
+ end
180
+ i += 1
181
+ end
182
+ nil
183
+ end
184
+
119
185
  def default_declaration_xml(doc, options)
120
186
  encoding = options[:encoding] || doc.encoding
121
187
  encoding = "UTF-8" if encoding.to_s.empty?
@@ -70,7 +70,10 @@ module Moxml
70
70
  )
71
71
  # Element output always ends with the close tag — but the
72
72
  # engine's serializer appends a stray trailing newline when
73
- # the element's last text child is non-ASCII.
73
+ # the element's last text child is non-ASCII (fixed engine
74
+ # side in 1.9.42; kept for older floor bindings). Note: the
75
+ # binding's indent_text kwarg is the display-form boolean,
76
+ # not Nokogiri's indent-unit string — do not forward it.
74
77
  xml.sub(/\n+\z/, "")
75
78
  end
76
79
 
@@ -31,6 +31,15 @@ module Moxml
31
31
  PREFIXED_ATTR_PREDICATES_NATIVE =
32
32
  Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.40")
33
33
 
34
+ # leptris/leptris#636 (leptris-ruby 1.9.42): libxml2-compatible
35
+ # pretty-print — child-PI lines, DOCTYPE internal-subset layout,
36
+ # no stray trailing newline after non-ASCII text, and the indent
37
+ # unit (`to_xml`'s indent_text takes the unit string). Older
38
+ # floor bindings treat indent_text as a display-form boolean, so
39
+ # the passthrough and the parity pins gate on this.
40
+ LIBXML2_LAYOUT_PARITY =
41
+ Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.42")
42
+
34
43
  NO_PARSE_ERRORS = [].freeze
35
44
  private_constant :NO_PARSE_ERRORS
36
45
 
@@ -429,11 +429,13 @@ module Moxml
429
429
  custom_decl = "<?xml #{attrs.join(' ')}?>"
430
430
  end
431
431
 
432
- result = node.to_xml(
432
+ serialize_kwargs = {
433
433
  indent: options[:indent],
434
434
  encoding: options[:encoding],
435
435
  save_with: save_options,
436
- )
436
+ }
437
+ serialize_kwargs[:indent_text] = options[:indent_text] if options[:indent_text]
438
+ result = node.to_xml(**serialize_kwargs)
437
439
 
438
440
  if custom_decl
439
441
  result = "#{custom_decl}\n#{result}"
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.16"
4
+ VERSION = "0.5.17"
5
5
  end
@@ -157,6 +157,15 @@ RSpec.describe Moxml::Adapter::Leptris do
157
157
  selfclosing: %(<r><a/><b/><c>t</c><d/></r>),
158
158
  }
159
159
 
160
+ # leptris/leptris#636 (binding 1.9.42): child-PI lines and
161
+ # DOCTYPE internal-subset layout match libxml2.
162
+ if defined?(described_class::LIBXML2_LAYOUT_PARITY) && described_class::LIBXML2_LAYOUT_PARITY
163
+ cases[:pi_child] = %(<r><?pi data?><e/></r>)
164
+ cases[:pi_child_mixed] = %(<r>t<?pi d?><e>x</e>u</r>)
165
+ cases[:doctype_subset] = %(<?xml version="1.0"?><!DOCTYPE r [<!ELEMENT r (#PCDATA)>]><r>t</r>)
166
+ cases[:doctype_subset_multi] = %(<?xml version="1.0"?><!DOCTYPE r [<!ELEMENT r (#PCDATA)><!ATTLIST e a CDATA "d">]><r><e/></r>)
167
+ end
168
+
160
169
  cases.each do |name, source|
161
170
  target = Nokogiri::XML(source).to_xml(indent: 2, encoding: "UTF-8")
162
171
  output = ctx.parse(source).to_xml(
@@ -166,6 +175,16 @@ RSpec.describe Moxml::Adapter::Leptris do
166
175
  end
167
176
  end
168
177
 
178
+ it "round-trips document-level PI mutations through serialization" do
179
+ # leptris/leptris#612: parse-created document PIs carry doc
180
+ # linkage — the setters work and the tree round-trips.
181
+ doc = ctx.parse("<?pi-prolog before?><root/>")
182
+ pi = doc.children.to_a.first
183
+ pi.target = "renamed"
184
+ pi.content = "changed"
185
+ expect(doc.to_xml).to include("<?renamed changed?>")
186
+ end
187
+
169
188
  it "reports the tracked native for a re-added document PI" do
170
189
  doc = ctx.parse("<?pi-src orig?><root/>")
171
190
  moved = doc.children.to_a[0]
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.16
4
+ version: 0.5.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.