canon 0.3.27 → 0.3.29
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/.rubocop.yml +2 -0
- data/README.adoc +3 -0
- data/docs/INDEX.adoc +1 -1
- data/docs/advanced/diff-classification.adoc +1 -1
- data/docs/advanced/extending-canon.adoc +1 -1
- data/docs/advanced/index.adoc +6 -6
- data/docs/advanced/verbose-mode-architecture.adoc +1 -1
- data/docs/features/configuration-profiles.adoc +4 -4
- data/docs/features/diff-formatting/algorithm-specific-output.adoc +5 -5
- data/docs/features/diff-formatting/colors-and-symbols.adoc +4 -4
- data/docs/features/diff-formatting/comment-asymmetry.adoc +3 -3
- data/docs/features/diff-formatting/display-filtering.adoc +4 -4
- data/docs/features/diff-formatting/display-preprocessing.adoc +7 -7
- data/docs/features/diff-formatting/index.adoc +16 -16
- data/docs/features/diff-formatting/pretty-diff-mode.adoc +4 -4
- data/docs/features/diff-formatting/themes.adoc +2 -2
- data/docs/features/diff-formatting/whitespace-adjacency.adoc +3 -3
- data/docs/features/environment-configuration/index.adoc +8 -8
- data/docs/features/environment-configuration/override-system.adoc +5 -5
- data/docs/features/environment-configuration/size-limits.adoc +4 -4
- data/docs/features/index.adoc +1 -1
- data/docs/features/match-options/algorithm-specific-behavior.adoc +5 -5
- data/docs/features/match-options/html-policies.adoc +1 -1
- data/docs/features/match-options/index.adoc +7 -7
- data/docs/features/match-options/pretty-printed-fixtures.adoc +4 -4
- data/docs/features/regenerate-expected.adoc +157 -0
- data/docs/guides/choosing-configuration.adoc +6 -6
- data/docs/interfaces/rspec/index.adoc +1 -1
- data/docs/interfaces/ruby-api/index.adoc +1 -1
- data/docs/internals/diffnode-enrichment.adoc +1 -1
- data/docs/internals/index.adoc +4 -4
- data/docs/reference/cli-options.adoc +4 -4
- data/docs/reference/environment-variables.adoc +1 -1
- data/docs/understanding/algorithms/dom-diff.adoc +9 -9
- data/docs/understanding/algorithms/index.adoc +11 -11
- data/docs/understanding/algorithms/semantic-tree-diff.adoc +7 -7
- data/docs/understanding/architecture.adoc +9 -9
- data/docs/understanding/comparison-pipeline.adoc +7 -7
- data/docs/understanding/formats/html.adoc +5 -5
- data/docs/understanding/formats/index.adoc +5 -5
- data/docs/understanding/formats/json.adoc +5 -5
- data/docs/understanding/formats/xml.adoc +4 -4
- data/docs/understanding/formats/yaml.adoc +5 -5
- data/lib/canon/comparison/diff_node_builder.rb +9 -0
- data/lib/canon/config/env_schema.rb +6 -0
- data/lib/canon/rebaseliner/atomic_writer.rb +32 -0
- data/lib/canon/rebaseliner/call_site_resolver.rb +126 -0
- data/lib/canon/rebaseliner/heredoc_locator.rb +140 -0
- data/lib/canon/rebaseliner/heredoc_rewriter.rb +56 -0
- data/lib/canon/rebaseliner/heredoc_target.rb +19 -0
- data/lib/canon/rebaseliner/logger.rb +24 -0
- data/lib/canon/rebaseliner.rb +162 -0
- data/lib/canon/rspec_matchers.rb +101 -23
- data/lib/canon/version.rb +1 -1
- metadata +10 -2
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Regenerating expected heredocs
|
|
3
|
+
parent: Features
|
|
4
|
+
nav_order: 50
|
|
5
|
+
---
|
|
6
|
+
= Regenerating expected heredocs
|
|
7
|
+
:toc:
|
|
8
|
+
:toclevels: 3
|
|
9
|
+
|
|
10
|
+
== Purpose
|
|
11
|
+
|
|
12
|
+
When a Canon matcher (`be_xml_equivalent_to`, `be_html_equivalent_to`, etc.)
|
|
13
|
+
asserts equivalence against a heredoc-literal `expected`, and the
|
|
14
|
+
upstream code legitimately changes its output, the fixture heredoc
|
|
15
|
+
needs to be updated to the new value. The default workflow is:
|
|
16
|
+
|
|
17
|
+
1. Run `bundle exec rspec` — see N failing assertions.
|
|
18
|
+
2. Copy the prettyprinted received from canon's diff display.
|
|
19
|
+
3. Paste into the `<<~XML ... XML` heredoc in the spec file.
|
|
20
|
+
4. Re-run, iterate.
|
|
21
|
+
|
|
22
|
+
For large fixture suites (e.g. the metanorma stack — 200+ heredocs per
|
|
23
|
+
rebaseline event) this is slow and error-prone (indent drift, partial
|
|
24
|
+
copies, missed trailing whitespace).
|
|
25
|
+
|
|
26
|
+
The `CANON_REGENERATE_EXPECTED=true` env var collapses steps 2-3 into
|
|
27
|
+
an automated rewrite.
|
|
28
|
+
|
|
29
|
+
== Workflow
|
|
30
|
+
|
|
31
|
+
[source,sh]
|
|
32
|
+
----
|
|
33
|
+
CANON_REGENERATE_EXPECTED=true bundle exec rspec
|
|
34
|
+
git diff # review what changed
|
|
35
|
+
git commit -am "rebaseline fixtures after upstream X change"
|
|
36
|
+
----
|
|
37
|
+
|
|
38
|
+
When the env var is set:
|
|
39
|
+
|
|
40
|
+
* Every failing `be_*_equivalent_to` assertion attempts to rewrite the
|
|
41
|
+
source heredoc that backs its `expected` argument.
|
|
42
|
+
* The rewrite uses the same `Canon::PrettyPrinter` that powers
|
|
43
|
+
`CANON_<FORMAT>_DIFF_SHOW_PRETTYPRINT_RECEIVED` — output is identical
|
|
44
|
+
to what would have appeared in the diff display.
|
|
45
|
+
* Successful rewrites mark the assertion as passing for the run, so CI
|
|
46
|
+
does not fail mid-rebaseline.
|
|
47
|
+
* Passing assertions are never touched.
|
|
48
|
+
* Negated matchers (`.not_to`) are never rewritten.
|
|
49
|
+
|
|
50
|
+
Each rewrite (or skip) emits a single-line log to stderr:
|
|
51
|
+
|
|
52
|
+
[source]
|
|
53
|
+
----
|
|
54
|
+
[canon:rebaseline] rewritten /path/to/spec.rb:123
|
|
55
|
+
[canon:rebaseline] skipped_interpolation /path/to/spec.rb:456
|
|
56
|
+
----
|
|
57
|
+
|
|
58
|
+
== Supported expected forms (v1)
|
|
59
|
+
|
|
60
|
+
The rebaseliner uses Prism to parse the caller spec file and locate the
|
|
61
|
+
heredoc backing the `expected` argument. It supports:
|
|
62
|
+
|
|
63
|
+
* `<<~XML` / `<<-XML` / `<<XML` heredoc assigned to a local variable in
|
|
64
|
+
the same `it` block.
|
|
65
|
+
* `<<~XML` / `<<-XML` / `<<XML` heredoc passed inline directly to the
|
|
66
|
+
matcher.
|
|
67
|
+
* *Multiple sequential assignments* to the same local variable within
|
|
68
|
+
one `it` block (the common metanorma pattern of reassigning `output`
|
|
69
|
+
before each format-specific expect). The rebaseliner walks backward
|
|
70
|
+
from the failing `expect` line to the *most-recent* assignment and
|
|
71
|
+
rewrites that one.
|
|
72
|
+
* Substitution chains on the actual side (e.g.
|
|
73
|
+
`expect(strip_guid(actual).gsub(...))`) — since the matcher receives
|
|
74
|
+
the post-substitution value, idempotency holds on the next run.
|
|
75
|
+
|
|
76
|
+
== Skipped with a warning (v1)
|
|
77
|
+
|
|
78
|
+
* Heredoc with `#{}` interpolation. Mechanical rewriting would lose
|
|
79
|
+
the interpolation; v2 (token preservation) is planned.
|
|
80
|
+
* Expected value from a method call (`load_fixture(...)`). The
|
|
81
|
+
expected is computed at runtime; the rebaseliner cannot identify
|
|
82
|
+
which on-disk fixture to rewrite.
|
|
83
|
+
* Expected value from a `let` or `shared_context` in a different
|
|
84
|
+
spec/support file. v1 does not cross file boundaries.
|
|
85
|
+
* Inline string literal expected (no heredoc). There is nothing to
|
|
86
|
+
re-indent into.
|
|
87
|
+
|
|
88
|
+
In each skipped case the file is left unchanged and the assertion
|
|
89
|
+
fails normally with the standard Canon diff display.
|
|
90
|
+
|
|
91
|
+
== Tracking line shifts across multiple rewrites
|
|
92
|
+
|
|
93
|
+
After a rewrite, the file's on-disk line numbering can shift if the new
|
|
94
|
+
heredoc body has a different line count from the old. Ruby's
|
|
95
|
+
`caller_locations` continues to report the *original* line numbers
|
|
96
|
+
during the same process run (the in-memory source is unchanged), so
|
|
97
|
+
the rebaseliner maintains a per-file cumulative line-shift tracker and
|
|
98
|
+
translates subsequent caller-reported lines accordingly. Multiple
|
|
99
|
+
rewrites within a single `it` block, or across multiple `it` blocks
|
|
100
|
+
in the same spec file, work transparently.
|
|
101
|
+
|
|
102
|
+
== Limitations and v2 roadmap
|
|
103
|
+
|
|
104
|
+
The following are intentionally out of v1 scope:
|
|
105
|
+
|
|
106
|
+
* `#{}` interpolation in heredoc bodies — v2 adds token-preserving
|
|
107
|
+
rewrites that re-anchor `#{var}` fragments in the prettyprinted
|
|
108
|
+
actual.
|
|
109
|
+
* JSON and YAML format prettyprinter wiring (v1 covers XML + HTML).
|
|
110
|
+
* A `canon regenerate SPEC_GLOB` Thor subcommand with a summary
|
|
111
|
+
report.
|
|
112
|
+
* File-locking for `parallel_rspec` safety. v1 documents "do not run
|
|
113
|
+
rebaseline mode in parallel."
|
|
114
|
+
* Optional `rubocop -A` post-rewrite formatter.
|
|
115
|
+
|
|
116
|
+
== Architecture
|
|
117
|
+
|
|
118
|
+
The rebaseliner is a self-contained module under
|
|
119
|
+
`lib/canon/rebaseliner/`:
|
|
120
|
+
|
|
121
|
+
`Canon::Rebaseliner`::
|
|
122
|
+
Top-level orchestrator. `enabled?` reads the env var.
|
|
123
|
+
`rewrite!` is the entry called by the matcher hook on failure.
|
|
124
|
+
|
|
125
|
+
`CallSiteResolver`::
|
|
126
|
+
Parses the spec file with Prism and locates the matcher invocation
|
|
127
|
+
on the failing line. Returns the AST node passed as `expected` and
|
|
128
|
+
the enclosing `it`/`example` block.
|
|
129
|
+
|
|
130
|
+
`HeredocLocator`::
|
|
131
|
+
Classifies the `expected` AST node. For local-variable references,
|
|
132
|
+
walks the enclosing block backward to find the most-recent
|
|
133
|
+
assignment. Returns a `HeredocSpec` describing the byte range to
|
|
134
|
+
rewrite, or a skip reason.
|
|
135
|
+
|
|
136
|
+
`HeredocRewriter`::
|
|
137
|
+
Re-indents the new body for `<<~` (squiggly) heredocs and writes the
|
|
138
|
+
new source via `AtomicWriter`.
|
|
139
|
+
|
|
140
|
+
`AtomicWriter`::
|
|
141
|
+
Tempfile-then-rename to avoid half-written files on Ctrl-C.
|
|
142
|
+
|
|
143
|
+
`Logger`::
|
|
144
|
+
Single-line stderr writes with the `[canon:rebaseline]` prefix.
|
|
145
|
+
|
|
146
|
+
The matcher hook lives in `lib/canon/rspec_matchers.rb`. It captures
|
|
147
|
+
`caller_locations` only when the env var is set, so passing
|
|
148
|
+
assertions pay no overhead.
|
|
149
|
+
|
|
150
|
+
== Testing the rebaseliner
|
|
151
|
+
|
|
152
|
+
`spec/canon/rebaseliner_spec.rb` runs each fixture under
|
|
153
|
+
`spec/fixtures/rebaseliner/` in a subprocess with the env var set,
|
|
154
|
+
then asserts the file content was (or was not) modified appropriately
|
|
155
|
+
and that the stderr log line is present. Add new fixture cases by
|
|
156
|
+
dropping `*_input.rb` into `spec/fixtures/rebaseliner/` and a new
|
|
157
|
+
example into `rebaseliner_spec.rb`.
|
|
@@ -216,7 +216,7 @@ Canon::Comparison.equivalent?(doc1, doc2,
|
|
|
216
216
|
)
|
|
217
217
|
----
|
|
218
218
|
|
|
219
|
-
**Remember**: Match options behave differently with each algorithm! See link
|
|
219
|
+
**Remember**: Match options behave differently with each algorithm! See link:../../features/match-options/algorithm-specific-behavior/[Algorithm-Specific Behavior].
|
|
220
220
|
|
|
221
221
|
==== Whitespace Type Sensitivity
|
|
222
222
|
|
|
@@ -709,14 +709,14 @@ Profiles bundle all layers (preprocessing, match profile, diff settings,
|
|
|
709
709
|
whitespace element lists) into a named preset defined in YAML.
|
|
710
710
|
Custom file profiles can inherit from built-in profiles.
|
|
711
711
|
|
|
712
|
-
See link
|
|
712
|
+
See link:../../features/configuration-profiles/[Configuration Profiles] for full documentation.
|
|
713
713
|
|
|
714
714
|
== See Also
|
|
715
715
|
|
|
716
|
-
* link
|
|
717
|
-
* link
|
|
716
|
+
* link:../../features/configuration-profiles/[Configuration Profiles] - Named config presets
|
|
717
|
+
* link:../../understanding/comparison-pipeline/[Comparison Pipeline] - Understanding the 4 layers
|
|
718
718
|
* link:../understanding/algorithms/[Algorithms] - Detailed algorithm documentation
|
|
719
|
-
* link
|
|
720
|
-
* link
|
|
719
|
+
* link:../../features/match-options/algorithm-specific-behavior/[Algorithm-Specific Behavior] - How algorithms differ
|
|
720
|
+
* link:../../features/diff-formatting/algorithm-specific-output/[Algorithm-Specific Output] - Output format differences
|
|
721
721
|
* link:../features/match-options/[Match Options] - All matching options
|
|
722
722
|
* link:../features/diff-formatting/[Diff Formatting] - Formatting options
|
|
@@ -428,7 +428,7 @@ IMPORTANT: Display filtering does NOT affect equivalence determination. It
|
|
|
428
428
|
only controls which differences appear in the failure output. Equivalence is
|
|
429
429
|
always based on normative differences only.
|
|
430
430
|
|
|
431
|
-
See link:../../features/diff-formatting/display-filtering
|
|
431
|
+
See link:../../features/diff-formatting/display-filtering/[Display
|
|
432
432
|
filtering] for complete details.
|
|
433
433
|
|
|
434
434
|
== Diff algorithms
|
|
@@ -545,7 +545,7 @@ result = Canon::Comparison.equivalent?(xml1, xml2,
|
|
|
545
545
|
----
|
|
546
546
|
====
|
|
547
547
|
|
|
548
|
-
See link:../../features/diff-formatting/display-filtering
|
|
548
|
+
See link:../../features/diff-formatting/display-filtering/[Display
|
|
549
549
|
filtering] for complete details.
|
|
550
550
|
|
|
551
551
|
=== Format-specific comparators
|
|
@@ -606,7 +606,7 @@ The old API still works for backwards compatibility, but enriched properties pro
|
|
|
606
606
|
|
|
607
607
|
== See also
|
|
608
608
|
|
|
609
|
-
* link
|
|
609
|
+
* link:../../understanding/architecture/[Architecture] - 4-layer architecture overview
|
|
610
610
|
* link:../understanding/algorithms/[Algorithms] - DOM and Semantic algorithm details
|
|
611
611
|
* link:../features/diff-formatting/[Diff Formatting] - Layer 4 rendering options
|
|
612
612
|
* link:diff-char-range-pipeline[DiffCharRange Pipeline] - How enriched DiffNodes are processed into character-level display positions
|
data/docs/internals/index.adoc
CHANGED
|
@@ -71,7 +71,7 @@ Canon separates comparison concerns into four layers:
|
|
|
71
71
|
|
|
72
72
|
Only Layer 2 differs between algorithms, and the enriched DiffNode structure ensures clean communication between layers.
|
|
73
73
|
|
|
74
|
-
See link:../understanding/architecture
|
|
74
|
+
See link:../understanding/architecture/[Architecture] for the complete overview.
|
|
75
75
|
|
|
76
76
|
=== Enriched Metadata Flow
|
|
77
77
|
|
|
@@ -265,7 +265,7 @@ def add_difference(node1, node2, diff1, diff2, dimension, opts, differences)
|
|
|
265
265
|
end
|
|
266
266
|
----
|
|
267
267
|
|
|
268
|
-
See link:../understanding/algorithms/dom-diff
|
|
268
|
+
See link:../understanding/algorithms/dom-diff/[DOM Diff Algorithm] for details.
|
|
269
269
|
|
|
270
270
|
=== Semantic Algorithm
|
|
271
271
|
|
|
@@ -289,11 +289,11 @@ def convert_insert(operation)
|
|
|
289
289
|
end
|
|
290
290
|
----
|
|
291
291
|
|
|
292
|
-
See link:../understanding/algorithms/semantic-tree-diff
|
|
292
|
+
See link:../understanding/algorithms/semantic-tree-diff/[Semantic Tree Diff Algorithm] for details.
|
|
293
293
|
|
|
294
294
|
== See Also
|
|
295
295
|
|
|
296
|
-
* link:../understanding/architecture
|
|
296
|
+
* link:../understanding/architecture/[Architecture] - 4-layer architecture overview
|
|
297
297
|
* link:../understanding/algorithms/[Algorithms] - DOM and Semantic algorithm details
|
|
298
298
|
* link:../features/diff-formatting/[Diff Formatting] - Layer 4 rendering options
|
|
299
299
|
* link:../advanced/[Advanced Topics] - Deep technical documentation
|
|
@@ -11,9 +11,9 @@ nav_order: 1
|
|
|
11
11
|
|
|
12
12
|
This page provides a complete reference for all Canon command-line interface options organized by command and function.
|
|
13
13
|
|
|
14
|
-
For conceptual understanding, see link
|
|
14
|
+
For conceptual understanding, see link:../../understanding/comparison-pipeline/[Comparison Pipeline].
|
|
15
15
|
|
|
16
|
-
For choosing the right configuration, see link
|
|
16
|
+
For choosing the right configuration, see link:../../guides/choosing-configuration/[Choosing Configuration].
|
|
17
17
|
|
|
18
18
|
== Command syntax
|
|
19
19
|
|
|
@@ -413,8 +413,8 @@ canon diff page1.html page2.html \
|
|
|
413
413
|
|
|
414
414
|
== See also
|
|
415
415
|
|
|
416
|
-
* link
|
|
417
|
-
* link
|
|
416
|
+
* link:../../understanding/comparison-pipeline/[Comparison Pipeline] - 4-layer architecture
|
|
417
|
+
* link:../../guides/choosing-configuration/[Choosing Configuration] - Decision guide
|
|
418
418
|
* link:../features/match-options/[Match Options] - Dimension reference
|
|
419
419
|
* link:../features/diff-formatting/[Diff Formatting] - Output customization
|
|
420
420
|
* link:../interfaces/cli/[CLI Interface] - Command-line usage guide
|
|
@@ -128,7 +128,7 @@ export CANON_JSON_FORMAT_PREPROCESSING=normalize
|
|
|
128
128
|
|`CANON_XML_DIFF_PRETTY_PRINTED_EXPECTED`
|
|
129
129
|
|boolean
|
|
130
130
|
|`false`
|
|
131
|
-
|When `true`, whitespace-only text nodes that start with `"\n"` inside `:collapse`-classified elements are dropped from the **expected (fixture)** document before it reaches the line diff. Solves the asymmetric case where the expected side is a hand-indented heredoc fixture but the received side is compact programmatic XML. Only applies when `display_preprocessing: :normalize_pretty_print`. Format-specific; no global form. See also link
|
|
131
|
+
|When `true`, whitespace-only text nodes that start with `"\n"` inside `:collapse`-classified elements are dropped from the **expected (fixture)** document before it reaches the line diff. Solves the asymmetric case where the expected side is a hand-indented heredoc fixture but the received side is compact programmatic XML. Only applies when `display_preprocessing: :normalize_pretty_print`. Format-specific; no global form. See also link:../../features/match-options/pretty-printed-fixtures/[Pretty-printed fixture support].
|
|
132
132
|
|XML (display only)
|
|
133
133
|
|
|
134
134
|
|`CANON_XML_DIFF_PRETTY_PRINTED_RECEIVED`
|
|
@@ -174,7 +174,7 @@ Canon::Comparison.equivalent?(doc1, doc2,
|
|
|
174
174
|
)
|
|
175
175
|
----
|
|
176
176
|
|
|
177
|
-
See link
|
|
177
|
+
See link:../../../features/match-options/algorithm-specific-behavior/[Algorithm-Specific Behavior] for how DOM interprets match options.
|
|
178
178
|
|
|
179
179
|
=== With Diff Formatting
|
|
180
180
|
|
|
@@ -289,7 +289,7 @@ The DOM algorithm cannot detect when content moves to a different position:
|
|
|
289
289
|
|
|
290
290
|
**Limitation**: Reordered content shows as DELETE + INSERT pairs
|
|
291
291
|
|
|
292
|
-
**Workaround**: Use link
|
|
292
|
+
**Workaround**: Use link:../semantic-tree-diff/[Semantic Algorithm] for move detection
|
|
293
293
|
|
|
294
294
|
=== Position-Dependent
|
|
295
295
|
|
|
@@ -297,7 +297,7 @@ The algorithm assumes similar structure between documents:
|
|
|
297
297
|
|
|
298
298
|
**Limitation**: Heavily restructured documents produce noisy diffs
|
|
299
299
|
|
|
300
|
-
**Workaround**: Use link
|
|
300
|
+
**Workaround**: Use link:../semantic-tree-diff/[Semantic Algorithm] for restructured documents
|
|
301
301
|
|
|
302
302
|
== Common Use Cases
|
|
303
303
|
|
|
@@ -381,9 +381,9 @@ Canon::Comparison.equivalent?(html1, html2,
|
|
|
381
381
|
|
|
382
382
|
== See Also
|
|
383
383
|
|
|
384
|
-
* link
|
|
385
|
-
* link
|
|
386
|
-
* link
|
|
387
|
-
* link
|
|
388
|
-
* link
|
|
389
|
-
* link
|
|
384
|
+
* link:../[Algorithms Overview] - Comparison of DOM vs Semantic
|
|
385
|
+
* link:../semantic-tree-diff/[Semantic Algorithm] - Alternative algorithm
|
|
386
|
+
* link:../../../features/match-options/algorithm-specific-behavior/[Algorithm-Specific Behavior] - How DOM interprets options
|
|
387
|
+
* link:../../../features/diff-formatting/algorithm-specific-output/[Algorithm-Specific Output] - Output format details
|
|
388
|
+
* link:../../../guides/choosing-configuration/[Choosing Configuration] - Complete decision guide
|
|
389
|
+
* link:../dom-diff/[DOM Diff] - Implementation details
|
|
@@ -12,7 +12,7 @@ has_children: true
|
|
|
12
12
|
|
|
13
13
|
Canon provides two comparison algorithms, each with different strengths and use cases. This section explains how to choose between them and what to expect from each.
|
|
14
14
|
|
|
15
|
-
This corresponds to **Layer 2 (Algorithm Selection)** in Canon's 4-layer architecture. See link:../comparison-pipeline
|
|
15
|
+
This corresponds to **Layer 2 (Algorithm Selection)** in Canon's 4-layer architecture. See link:../comparison-pipeline/[Comparison Pipeline] for the complete flow.
|
|
16
16
|
|
|
17
17
|
== Overview
|
|
18
18
|
|
|
@@ -21,12 +21,12 @@ Canon supports two algorithms for document comparison:
|
|
|
21
21
|
* **DOM Algorithm** - Fast, stable, positional comparison (default)
|
|
22
22
|
* **Semantic Algorithm** - Slower, intelligent, detects moves and restructuring (experimental)
|
|
23
23
|
|
|
24
|
-
**Critical**: The algorithm choice affects how Layers 3 and 4 behave. See link:../../features/match-options/algorithm-specific-behavior
|
|
24
|
+
**Critical**: The algorithm choice affects how Layers 3 and 4 behave. See link:../../features/match-options/algorithm-specific-behavior/[Algorithm-Specific Behavior] and link:../../features/diff-formatting/algorithm-specific-output/[Algorithm-Specific Output].
|
|
25
25
|
|
|
26
26
|
== Child Pages
|
|
27
27
|
|
|
28
|
-
* link:dom-diff
|
|
29
|
-
* link:semantic-tree-diff
|
|
28
|
+
* link:dom-diff/[DOM Algorithm] - Positional comparison details
|
|
29
|
+
* link:semantic-tree-diff/[Semantic Algorithm] - Tree-based comparison details
|
|
30
30
|
|
|
31
31
|
== Algorithm Comparison
|
|
32
32
|
|
|
@@ -249,7 +249,7 @@ NOTE: These are approximate times. Actual performance depends on document struct
|
|
|
249
249
|
3. Performance slower but more intelligent
|
|
250
250
|
4. Output format changes to operation-based
|
|
251
251
|
|
|
252
|
-
See link:../../features/match-options/algorithm-specific-behavior
|
|
252
|
+
See link:../../features/match-options/algorithm-specific-behavior/[Algorithm-Specific Behavior] for migration details.
|
|
253
253
|
|
|
254
254
|
=== Switching from Semantic to DOM
|
|
255
255
|
|
|
@@ -306,9 +306,9 @@ end
|
|
|
306
306
|
|
|
307
307
|
== See also
|
|
308
308
|
|
|
309
|
-
* link:dom-diff
|
|
310
|
-
* link:semantic-tree-diff
|
|
311
|
-
* link:../comparison-pipeline
|
|
312
|
-
* link:../../features/match-options/algorithm-specific-behavior
|
|
313
|
-
* link:../../features/diff-formatting/algorithm-specific-output
|
|
314
|
-
* link:../../guides/choosing-configuration
|
|
309
|
+
* link:dom-diff/[DOM Algorithm] - Detailed DOM algorithm documentation
|
|
310
|
+
* link:semantic-tree-diff/[Semantic Algorithm] - Detailed semantic algorithm documentation
|
|
311
|
+
* link:../comparison-pipeline/[Comparison Pipeline] - 4-layer architecture
|
|
312
|
+
* link:../../features/match-options/algorithm-specific-behavior/[Algorithm-Specific Behavior] - How algorithms interpret options
|
|
313
|
+
* link:../../features/diff-formatting/algorithm-specific-output/[Algorithm-Specific Output] - Output format differences
|
|
314
|
+
* link:../../guides/choosing-configuration/[Choosing Configuration] - Complete decision guide
|
|
@@ -230,7 +230,7 @@ Canon::Comparison.equivalent?(doc1, doc2,
|
|
|
230
230
|
)
|
|
231
231
|
----
|
|
232
232
|
|
|
233
|
-
See link
|
|
233
|
+
See link:../../../features/match-options/algorithm-specific-behavior/[Algorithm-Specific Behavior] for details.
|
|
234
234
|
|
|
235
235
|
=== With Diff Formatting
|
|
236
236
|
|
|
@@ -525,9 +525,9 @@ When switching from DOM to Semantic:
|
|
|
525
525
|
|
|
526
526
|
== See Also
|
|
527
527
|
|
|
528
|
-
* link
|
|
529
|
-
* link
|
|
530
|
-
* link
|
|
531
|
-
* link
|
|
532
|
-
* link
|
|
533
|
-
* link
|
|
528
|
+
* link:../[Algorithms Overview] - Comparison of DOM vs Semantic
|
|
529
|
+
* link:../dom-diff/[DOM Algorithm] - Standard algorithm
|
|
530
|
+
* link:../../../features/match-options/algorithm-specific-behavior/[Algorithm-Specific Behavior] - How Semantic interprets options
|
|
531
|
+
* link:../../../features/diff-formatting/algorithm-specific-output/[Algorithm-Specific Output] - Output format details
|
|
532
|
+
* link:../../../guides/choosing-configuration/[Choosing Configuration] - Complete decision guide
|
|
533
|
+
* link:../semantic-tree-diff/[Semantic Tree Diff] - Advanced details
|
|
@@ -11,9 +11,9 @@ nav_order: 1
|
|
|
11
11
|
|
|
12
12
|
This document explains Canon's 4-layer comparison architecture and how documents flow through preprocessing, algorithm selection, semantic matching, and diff rendering.
|
|
13
13
|
|
|
14
|
-
For a guided walkthrough of choosing configurations, see link
|
|
14
|
+
For a guided walkthrough of choosing configurations, see link:../../guides/choosing-configuration/[Choosing Configuration].
|
|
15
15
|
|
|
16
|
-
For detailed 4-layer pipeline documentation, see link
|
|
16
|
+
For detailed 4-layer pipeline documentation, see link:../comparison-pipeline/[Comparison Pipeline].
|
|
17
17
|
|
|
18
18
|
== Overview
|
|
19
19
|
|
|
@@ -799,7 +799,7 @@ Control how differences are displayed. **This layer is algorithm-specific** - ea
|
|
|
799
799
|
* **DOM algorithm**: Generates line-based differences
|
|
800
800
|
* **Semantic algorithm**: Generates operation-based differences (INSERT, DELETE, UPDATE, MOVE)
|
|
801
801
|
|
|
802
|
-
See link
|
|
802
|
+
See link:../../features/diff-formatting/algorithm-specific-output/[Algorithm-Specific Output] for detailed comparison.
|
|
803
803
|
|
|
804
804
|
=== Diff options
|
|
805
805
|
|
|
@@ -865,7 +865,7 @@ result = Canon::Comparison.equivalent?(doc1, doc2,
|
|
|
865
865
|
)
|
|
866
866
|
----
|
|
867
867
|
|
|
868
|
-
See link
|
|
868
|
+
See link:../comparison-pipeline/[Comparison Pipeline] for layer-by-layer examples.
|
|
869
869
|
|
|
870
870
|
=== DiffNode: Representation of differences
|
|
871
871
|
|
|
@@ -1153,16 +1153,16 @@ This refactoring improves:
|
|
|
1153
1153
|
|
|
1154
1154
|
== See also
|
|
1155
1155
|
|
|
1156
|
-
* link
|
|
1156
|
+
* link:../comparison-pipeline/[Comparison Pipeline] - Complete 4-layer walkthrough
|
|
1157
1157
|
* link:algorithms/[Algorithms] - DOM and Semantic algorithm details
|
|
1158
1158
|
* link:../internals/[Internals] - Implementation details and data structures
|
|
1159
|
-
* link
|
|
1159
|
+
* link:../../internals/diffnode-enrichment/[DiffNode Enrichment] - How metadata flows from Layer 2 to Layer 4
|
|
1160
1160
|
* link:../features/preprocessing/[Preprocessing options]
|
|
1161
1161
|
* link:../features/match-options/[Match dimensions and profiles]
|
|
1162
|
-
* link
|
|
1162
|
+
* link:../../features/match-options/algorithm-specific-behavior/[Algorithm-Specific Behavior]
|
|
1163
1163
|
* link:../features/diff-formatting/[Diff formatting]
|
|
1164
|
-
* link
|
|
1165
|
-
* link
|
|
1164
|
+
* link:../../features/diff-formatting/algorithm-specific-output/[Algorithm-Specific Output]
|
|
1165
|
+
* link:../../guides/choosing-configuration/[Choosing Configuration]
|
|
1166
1166
|
* link:../interfaces/ruby-api/[Ruby API documentation]
|
|
1167
1167
|
* link:../interfaces/cli/[Command-line interface]
|
|
1168
1168
|
* link:../interfaces/rspec/[RSpec matchers]
|
|
@@ -67,7 +67,7 @@ graph TD
|
|
|
67
67
|
* Match profiles (preset combinations)
|
|
68
68
|
* Algorithm-specific behaviors
|
|
69
69
|
|
|
70
|
-
**Documentation**: See link:../features/match-options/[Match Options] and link
|
|
70
|
+
**Documentation**: See link:../features/match-options/[Match Options] and link:../../features/match-options/algorithm-specific-behavior/[Algorithm-Specific Behavior]
|
|
71
71
|
|
|
72
72
|
=== Layer 4: Diff Formatting
|
|
73
73
|
|
|
@@ -81,7 +81,7 @@ graph TD
|
|
|
81
81
|
* Context and grouping
|
|
82
82
|
* Character visualization
|
|
83
83
|
|
|
84
|
-
**Documentation**: See link:../features/diff-formatting/[Diff Formatting] and link
|
|
84
|
+
**Documentation**: See link:../features/diff-formatting/[Diff Formatting] and link:../../features/diff-formatting/algorithm-specific-output/[Algorithm-Specific Output]
|
|
85
85
|
|
|
86
86
|
== DiffNode Data Flow
|
|
87
87
|
|
|
@@ -154,7 +154,7 @@ After creation, DiffNodes are enriched with metadata for Layer 4 rendering:
|
|
|
154
154
|
* **NodeSerializer**: Library-agnostic serialization of node content
|
|
155
155
|
* **Attribute extraction**: Normalized attribute hashes
|
|
156
156
|
|
|
157
|
-
See link
|
|
157
|
+
See link:../../internals/diffnode-enrichment/[DiffNode Enrichment] for implementation details.
|
|
158
158
|
|
|
159
159
|
=== Layer 3: Classification
|
|
160
160
|
|
|
@@ -432,8 +432,8 @@ Canon::Comparison.equivalent?(doc1, doc2,
|
|
|
432
432
|
|
|
433
433
|
== See Also
|
|
434
434
|
|
|
435
|
-
* link
|
|
435
|
+
* link:../architecture/[Architecture] - Overall system design
|
|
436
436
|
* link:algorithms/[Algorithms] - Detailed algorithm documentation
|
|
437
|
-
* link
|
|
438
|
-
* link
|
|
439
|
-
* link
|
|
437
|
+
* link:../../features/match-options/algorithm-specific-behavior/[Algorithm-Specific Behavior] - How algorithms interpret match options
|
|
438
|
+
* link:../../features/diff-formatting/algorithm-specific-output/[Algorithm-Specific Output] - Different output formats
|
|
439
|
+
* link:../../guides/choosing-configuration/[Choosing Configuration] - Decision guide for all layers
|
|
@@ -396,9 +396,9 @@ XHTML is treated as XML and follows stricter rules.
|
|
|
396
396
|
|
|
397
397
|
== See also
|
|
398
398
|
|
|
399
|
-
* link
|
|
399
|
+
* link:../../comparison-pipeline/[Comparison Pipeline] - Understanding the 4 layers
|
|
400
400
|
* link:../../features/match-options/[Match Options] - All matching options
|
|
401
|
-
* link
|
|
402
|
-
* link
|
|
403
|
-
* link
|
|
404
|
-
* link
|
|
401
|
+
* link:../../../guides/choosing-configuration/[Choosing Configuration] - Decision guide
|
|
402
|
+
* link:../[Format Support] - Overview of all formats
|
|
403
|
+
* link:../xml/[XML Format] - XML-specific features
|
|
404
|
+
* link:../json/[JSON Format] - JSON-specific features
|
|
@@ -20,10 +20,10 @@ Canon provides unified canonicalization and comparison for four serialization fo
|
|
|
20
20
|
|
|
21
21
|
This page provides an overview of format support. See the child pages for format-specific details:
|
|
22
22
|
|
|
23
|
-
* link:xml
|
|
24
|
-
* link:html
|
|
25
|
-
* link:json
|
|
26
|
-
* link:yaml
|
|
23
|
+
* link:xml/[XML Format] - W3C C14N, namespace handling
|
|
24
|
+
* link:html/[HTML Format] - HTML4/5 detection, rendering behavior
|
|
25
|
+
* link:json/[JSON Format] - Sorted keys, type preservation
|
|
26
|
+
* link:yaml/[YAML Format] - YAML specifics, anchors and aliases
|
|
27
27
|
|
|
28
28
|
== Format detection
|
|
29
29
|
|
|
@@ -257,5 +257,5 @@ Canon::Comparison::YamlComparator.equivalent?(yaml1, yaml2,
|
|
|
257
257
|
* link:../../interfaces/ruby-api/[Ruby API documentation]
|
|
258
258
|
* link:../../interfaces/cli/[Command-line interface]
|
|
259
259
|
* link:../../features/match-options/[Match options reference]
|
|
260
|
-
* link:../comparison-pipeline
|
|
260
|
+
* link:../comparison-pipeline/[Comparison Pipeline]
|
|
261
261
|
* link:../../features/preprocessing/[Preprocessing options]
|
|
@@ -382,9 +382,9 @@ Canon normalizes Unicode escapes during canonicalization.
|
|
|
382
382
|
|
|
383
383
|
== See also
|
|
384
384
|
|
|
385
|
-
* link
|
|
385
|
+
* link:../../comparison-pipeline/[Comparison Pipeline] - Understanding the 4 layers
|
|
386
386
|
* link:../../features/match-options/[Match Options] - All matching options
|
|
387
|
-
* link
|
|
388
|
-
* link
|
|
389
|
-
* link
|
|
390
|
-
* link
|
|
387
|
+
* link:../../../guides/choosing-configuration/[Choosing Configuration] - Decision guide
|
|
388
|
+
* link:../[Format Support] - Overview of all formats
|
|
389
|
+
* link:../yaml/[YAML Format] - YAML-specific features (similar to JSON)
|
|
390
|
+
* link:../xml/[XML Format] - XML-specific features
|
|
@@ -439,8 +439,8 @@ canon diff expected.xml actual.xml \
|
|
|
439
439
|
|
|
440
440
|
== See also
|
|
441
441
|
|
|
442
|
-
* link
|
|
442
|
+
* link:../../comparison-pipeline/[Comparison Pipeline] - Understanding the 4 layers
|
|
443
443
|
* link:../../features/match-options/[Match Options] - All matching options
|
|
444
|
-
* link
|
|
445
|
-
* link
|
|
446
|
-
* link
|
|
444
|
+
* link:../../../guides/choosing-configuration/[Choosing Configuration] - Decision guide
|
|
445
|
+
* link:../[Format Support] - Overview of all formats
|
|
446
|
+
* link:../html/[HTML Format] - HTML-specific features
|
|
@@ -496,9 +496,9 @@ YAML is sensitive to indentation. Use `structural_whitespace: :ignore` to handle
|
|
|
496
496
|
|
|
497
497
|
== See also
|
|
498
498
|
|
|
499
|
-
* link
|
|
499
|
+
* link:../../comparison-pipeline/[Comparison Pipeline] - Understanding the 4 layers
|
|
500
500
|
* link:../../features/match-options/[Match Options] - All matching options
|
|
501
|
-
* link
|
|
502
|
-
* link
|
|
503
|
-
* link
|
|
504
|
-
* link
|
|
501
|
+
* link:../../../guides/choosing-configuration/[Choosing Configuration] - Decision guide
|
|
502
|
+
* link:../[Format Support] - Overview of all formats
|
|
503
|
+
* link:../json/[JSON Format] - JSON-specific features (similar to YAML)
|
|
504
|
+
* link:../xml/[XML Format] - XML-specific features
|
|
@@ -179,6 +179,15 @@ module Canon
|
|
|
179
179
|
return "whitespace: #{describe_whitespace(text1)} vs #{describe_whitespace(text2)}"
|
|
180
180
|
end
|
|
181
181
|
|
|
182
|
+
# Identical content modulo whitespace: two full visualized
|
|
183
|
+
# copies read as materially different text when the only
|
|
184
|
+
# delta is trailing/invisible whitespace (#94) — the compact
|
|
185
|
+
# character-count description says what actually differs.
|
|
186
|
+
if text1.strip == text2.strip
|
|
187
|
+
return "whitespace-only: #{describe_whitespace(text1)} vs " \
|
|
188
|
+
"#{describe_whitespace(text2)}"
|
|
189
|
+
end
|
|
190
|
+
|
|
182
191
|
"Text: \"#{visualize_whitespace(text1)}\" vs \"#{visualize_whitespace(text2)}\""
|
|
183
192
|
end
|
|
184
193
|
|
|
@@ -21,6 +21,12 @@ module Canon
|
|
|
21
21
|
|
|
22
22
|
FORMAT_ATTRIBUTE_TYPES = {
|
|
23
23
|
preprocessing: :string,
|
|
24
|
+
|
|
25
|
+
# Rebaseliner: opt-in mode that rewrites failing heredoc
|
|
26
|
+
# expectations in spec files with the prettyprinted received
|
|
27
|
+
# value. Read directly by Canon::Rebaseliner.enabled?; schema
|
|
28
|
+
# entry exists for --env-help discoverability.
|
|
29
|
+
regenerate_expected: :boolean,
|
|
24
30
|
}.freeze
|
|
25
31
|
|
|
26
32
|
class << self
|