canon 0.3.108 → 0.3.110
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/lib/canon/comparison/xml_comparator/child_comparison.rb +7 -1
- data/lib/canon/formatters/html_formatter_base.rb +18 -15
- data/lib/canon/formatters/xml_formatter.rb +2 -6
- data/lib/canon/version.rb +1 -1
- data/lib/canon/xml/attribute_sorter.rb +39 -0
- data/lib/canon/xml.rb +1 -0
- data/lib/canon/xml_parsing.rb +6 -3
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 831a2d23d844114132e9a234a4e1452f08aacc36ef0947d80c7100358bbbe209
|
|
4
|
+
data.tar.gz: ff423e64f21b9e5e6c93694c124fc404b95e31ad5b4ed3162262c62023bd5c35
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 35762c78b2ba5bd8090cb799566ecabec4d863bd956c27c0a9119bf8025cda2bad8d49f9a93b4242ece7bfc5de7e8ef5a939bce5e3f955e46af925857379e704
|
|
7
|
+
data.tar.gz: 4b932b9b03940bc536887cbf4a750ce265b2992afc775b446ea9a56b675d23f52724b7b7a6f710ae3258a4c5e73c69bfc01bd67b56087554ca0a3c47a5f35343
|
|
@@ -198,6 +198,12 @@ module Canon
|
|
|
198
198
|
)
|
|
199
199
|
has_mismatch = false
|
|
200
200
|
|
|
201
|
+
# The ORIGINAL direction, captured before
|
|
202
|
+
# determine_mismatch_children returns reduced sets of equal
|
|
203
|
+
# length — deriving the side from the reduced sets reports
|
|
204
|
+
# every mid-content removal as an insertion (canon#285).
|
|
205
|
+
before_longer = children1.length > children2.length
|
|
206
|
+
|
|
201
207
|
# Length check
|
|
202
208
|
unless children1.length == children2.length
|
|
203
209
|
has_mismatch = true
|
|
@@ -228,7 +234,7 @@ module Canon
|
|
|
228
234
|
else
|
|
229
235
|
mismatched_children.each do |child|
|
|
230
236
|
child_dim = comparator.determine_node_dimension(child)
|
|
231
|
-
if
|
|
237
|
+
if before_longer
|
|
232
238
|
comparator.add_difference(child, nil,
|
|
233
239
|
Comparison::MISSING_NODE,
|
|
234
240
|
Comparison::MISSING_NODE,
|
|
@@ -63,6 +63,12 @@ module Canon
|
|
|
63
63
|
# regex per format call cost more than the gsub it drives.
|
|
64
64
|
BLOCK_SPACING_PATTERN =
|
|
65
65
|
Regexp.new("(</(?:#{BLOCK_ELEMENTS.join('|')})>)(?:\n*)(<(?:#{BLOCK_ELEMENTS.join('|')})[\s>])").freeze
|
|
66
|
+
# Engine-inserted newlines after a block close, before ANY next
|
|
67
|
+
# tag (the serializer's indent model breaks lines where canon's
|
|
68
|
+
# normalize step kept no text node) — folded to the canonical
|
|
69
|
+
# single space so serialization is idempotent.
|
|
70
|
+
AFTER_BLOCK_NEWLINE_PATTERN =
|
|
71
|
+
Regexp.new("(</(?:#{BLOCK_ELEMENTS.join('|')})>)(\n+)(<(?!/))").freeze
|
|
66
72
|
# Format HTML using canonical form
|
|
67
73
|
# @param html [String] HTML document to canonicalize
|
|
68
74
|
# @return [String] Canonical form of HTML
|
|
@@ -108,21 +114,10 @@ module Canon
|
|
|
108
114
|
end
|
|
109
115
|
|
|
110
116
|
# Sort element attributes alphabetically throughout document
|
|
117
|
+
# (the shared canonical attribute order — TODO.sota/03)
|
|
111
118
|
# @param doc [Moxml::Document] Parsed document
|
|
112
119
|
def self.sort_attributes(doc)
|
|
113
|
-
|
|
114
|
-
attrs = element.attributes
|
|
115
|
-
next if attrs.empty?
|
|
116
|
-
|
|
117
|
-
names = attrs.map(&:name)
|
|
118
|
-
# Already-sorted is the common case — removing and re-adding
|
|
119
|
-
# every attribute is expensive, so check first.
|
|
120
|
-
next if names.each_cons(2).all? { |a, b| (a <=> b) <= 0 }
|
|
121
|
-
|
|
122
|
-
sorted_attrs = attrs.sort_by(&:name)
|
|
123
|
-
names.each { |name| element.remove_attribute(name) }
|
|
124
|
-
sorted_attrs.each { |attr| element[attr.name] = attr.value }
|
|
125
|
-
end
|
|
120
|
+
Canon::Xml::AttributeSorter.sort!(doc)
|
|
126
121
|
end
|
|
127
122
|
|
|
128
123
|
# Depth-first element walk (pre-order).
|
|
@@ -211,9 +206,17 @@ module Canon
|
|
|
211
206
|
# @param html [String] Serialized HTML string
|
|
212
207
|
# @return [String] HTML with proper spacing between block elements
|
|
213
208
|
def self.ensure_block_element_spacing(html)
|
|
214
|
-
#
|
|
209
|
+
# Space between closing and opening block element tags
|
|
215
210
|
# (pattern compiled once — see BLOCK_SPACING_PATTERN)
|
|
216
|
-
html.gsub(BLOCK_SPACING_PATTERN, '\1 \2')
|
|
211
|
+
html = html.gsub(BLOCK_SPACING_PATTERN, '\1 \2')
|
|
212
|
+
# The engine's serializer inserts its own newlines after a
|
|
213
|
+
# block close when the tree has no whitespace text node there
|
|
214
|
+
# (normalize_whitespace keeps a single space only where one
|
|
215
|
+
# existed). Collapse those self-inserted newlines to the same
|
|
216
|
+
# canonical space — without this, format is not idempotent:
|
|
217
|
+
# pass 1 emits "</p>\n<br>", pass 2 answers "</p> <br>"
|
|
218
|
+
# (the property suite, TODO.sota/03).
|
|
219
|
+
html.gsub(AFTER_BLOCK_NEWLINE_PATTERN, '\1 \3')
|
|
217
220
|
end
|
|
218
221
|
|
|
219
222
|
# Check if a node is a block-level element
|
|
@@ -11,13 +11,9 @@ module Canon
|
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def self.parse(xml)
|
|
14
|
+
def self.parse(xml, options = {})
|
|
15
15
|
Canon::Validators::XmlValidator.validate!(xml)
|
|
16
|
-
|
|
17
|
-
Nokogiri::XML(xml)
|
|
18
|
-
else
|
|
19
|
-
XmlParsing.parse(xml)
|
|
20
|
-
end
|
|
16
|
+
XmlParsing.parse(xml, options)
|
|
21
17
|
end
|
|
22
18
|
end
|
|
23
19
|
end
|
data/lib/canon/version.rb
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Canon
|
|
4
|
+
module Xml
|
|
5
|
+
# Canonical attribute ordering: every element's attributes are
|
|
6
|
+
# re-attached in lexicographic name order. Both canonical lanes
|
|
7
|
+
# consume this — the HTML formatter base and the XML pretty
|
|
8
|
+
# printer — so attribute order is invariant across formats and
|
|
9
|
+
# across input orderings (the equivalence-respect property,
|
|
10
|
+
# TODO.sota/03).
|
|
11
|
+
module AttributeSorter
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def sort!(doc)
|
|
15
|
+
walk_elements(doc.root) do |element|
|
|
16
|
+
attrs = element.attributes
|
|
17
|
+
next if attrs.empty?
|
|
18
|
+
|
|
19
|
+
names = attrs.map(&:name)
|
|
20
|
+
# Already-sorted is the common case — removing and re-adding
|
|
21
|
+
# every attribute is expensive, so check first.
|
|
22
|
+
next if names.each_cons(2).all? { |a, b| (a <=> b) <= 0 }
|
|
23
|
+
|
|
24
|
+
sorted_attrs = attrs.sort_by(&:name)
|
|
25
|
+
names.each { |name| element.remove_attribute(name) }
|
|
26
|
+
sorted_attrs.each { |attr| element[attr.name] = attr.value }
|
|
27
|
+
end
|
|
28
|
+
doc
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def walk_elements(node, &block)
|
|
32
|
+
return unless node.is_a?(Moxml::Element)
|
|
33
|
+
|
|
34
|
+
yield(node)
|
|
35
|
+
node.children.to_a.each { |child| walk_elements(child, &block) }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/lib/canon/xml.rb
CHANGED
|
@@ -15,6 +15,7 @@ module Canon
|
|
|
15
15
|
# Canon::Xml::Nodes.
|
|
16
16
|
module Xml
|
|
17
17
|
autoload :AttributeHandler, "canon/xml/attribute_handler"
|
|
18
|
+
autoload :AttributeSorter, "canon/xml/attribute_sorter"
|
|
18
19
|
autoload :C14n, "canon/xml/c14n"
|
|
19
20
|
autoload :DigestGate, "canon/xml/digest_gate"
|
|
20
21
|
autoload :CharacterEncoder, "canon/xml/character_encoder"
|
data/lib/canon/xml_parsing.rb
CHANGED
|
@@ -303,11 +303,14 @@ module Canon
|
|
|
303
303
|
|
|
304
304
|
# --- Moxml engine ---
|
|
305
305
|
|
|
306
|
-
# Readonly: canon
|
|
306
|
+
# Readonly by default: canon reads engine documents (conversion,
|
|
307
307
|
# serialization) — leptris memoizes reads on readonly documents,
|
|
308
308
|
# and mutation is refused loudly rather than silently corrupting.
|
|
309
|
-
|
|
310
|
-
|
|
309
|
+
# Mutating lanes (the attribute sorter, TODO.sota/03) opt out
|
|
310
|
+
# explicitly with readonly: false.
|
|
311
|
+
def moxml_parse(xml_string, options)
|
|
312
|
+
readonly = options.fetch(:readonly, true)
|
|
313
|
+
moxml_context.parse(xml_string, readonly: readonly)
|
|
311
314
|
end
|
|
312
315
|
|
|
313
316
|
def moxml_node_type(node)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: canon
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.110
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: diff-lcs
|
|
@@ -405,6 +405,7 @@ files:
|
|
|
405
405
|
- lib/canon/version.rb
|
|
406
406
|
- lib/canon/xml.rb
|
|
407
407
|
- lib/canon/xml/attribute_handler.rb
|
|
408
|
+
- lib/canon/xml/attribute_sorter.rb
|
|
408
409
|
- lib/canon/xml/c14n.rb
|
|
409
410
|
- lib/canon/xml/character_encoder.rb
|
|
410
411
|
- lib/canon/xml/data_model.rb
|