canon 0.1.6 → 0.1.7
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_todo.yml +163 -67
- data/README.adoc +400 -7
- data/docs/Gemfile +9 -0
- data/docs/INDEX.adoc +99 -182
- data/docs/_config.yml +100 -0
- data/docs/advanced/diff-classification.adoc +547 -0
- data/docs/advanced/diff-pipeline.adoc +358 -0
- data/docs/advanced/index.adoc +214 -0
- data/docs/advanced/semantic-diff-report.adoc +390 -0
- data/docs/{VERBOSE.adoc → advanced/verbose-mode-architecture.adoc} +51 -53
- data/docs/features/diff-formatting/algorithm-specific-output.adoc +533 -0
- data/docs/{CHARACTER_VISUALIZATION.adoc → features/diff-formatting/character-visualization.adoc} +23 -62
- data/docs/features/diff-formatting/colors-and-symbols.adoc +606 -0
- data/docs/features/diff-formatting/context-and-grouping.adoc +490 -0
- data/docs/features/diff-formatting/display-filtering.adoc +472 -0
- data/docs/features/diff-formatting/index.adoc +140 -0
- data/docs/features/environment-configuration/index.adoc +327 -0
- data/docs/features/environment-configuration/override-system.adoc +436 -0
- data/docs/features/environment-configuration/size-limits.adoc +273 -0
- data/docs/features/index.adoc +173 -0
- data/docs/features/input-validation/index.adoc +521 -0
- data/docs/features/match-options/algorithm-specific-behavior.adoc +365 -0
- data/docs/features/match-options/html-policies.adoc +312 -0
- data/docs/features/match-options/index.adoc +621 -0
- data/docs/getting-started/index.adoc +83 -0
- data/docs/getting-started/quick-start.adoc +76 -0
- data/docs/guides/choosing-configuration.adoc +689 -0
- data/docs/guides/index.adoc +181 -0
- data/docs/{CLI.adoc → interfaces/cli/index.adoc} +18 -13
- data/docs/interfaces/index.adoc +101 -0
- data/docs/{RSPEC.adoc → interfaces/rspec/index.adoc} +242 -31
- data/docs/{RUBY_API.adoc → interfaces/ruby-api/index.adoc} +118 -16
- data/docs/lychee.toml +65 -0
- data/docs/reference/cli-options.adoc +418 -0
- data/docs/reference/environment-variables.adoc +375 -0
- data/docs/reference/index.adoc +204 -0
- data/docs/reference/options-across-interfaces.adoc +417 -0
- data/docs/understanding/algorithms/dom-diff.adoc +389 -0
- data/docs/understanding/algorithms/index.adoc +314 -0
- data/docs/understanding/algorithms/semantic-tree-diff.adoc +533 -0
- data/docs/understanding/architecture.adoc +447 -0
- data/docs/understanding/comparison-pipeline.adoc +317 -0
- data/docs/understanding/formats/html.adoc +380 -0
- data/docs/understanding/formats/index.adoc +261 -0
- data/docs/understanding/formats/json.adoc +390 -0
- data/docs/understanding/formats/xml.adoc +366 -0
- data/docs/understanding/formats/yaml.adoc +504 -0
- data/docs/understanding/index.adoc +130 -0
- data/lib/canon/cli.rb +42 -1
- data/lib/canon/commands/diff_command.rb +108 -23
- data/lib/canon/comparison/compare_profile.rb +101 -0
- data/lib/canon/comparison/comparison_result.rb +41 -2
- data/lib/canon/comparison/html_comparator.rb +292 -71
- data/lib/canon/comparison/html_compare_profile.rb +117 -0
- data/lib/canon/comparison/match_options.rb +42 -4
- data/lib/canon/comparison/strategies/base_match_strategy.rb +99 -0
- data/lib/canon/comparison/strategies/match_strategy_factory.rb +74 -0
- data/lib/canon/comparison/strategies/semantic_tree_match_strategy.rb +220 -0
- data/lib/canon/comparison/xml_comparator.rb +695 -91
- data/lib/canon/comparison.rb +207 -2
- data/lib/canon/config/env_provider.rb +71 -0
- data/lib/canon/config/env_schema.rb +58 -0
- data/lib/canon/config/override_resolver.rb +55 -0
- data/lib/canon/config/type_converter.rb +59 -0
- data/lib/canon/config.rb +158 -29
- data/lib/canon/data_model.rb +29 -0
- data/lib/canon/diff/diff_classifier.rb +74 -14
- data/lib/canon/diff/diff_context_builder.rb +41 -0
- data/lib/canon/diff/diff_line.rb +18 -2
- data/lib/canon/diff/diff_node.rb +18 -3
- data/lib/canon/diff/diff_node_mapper.rb +71 -12
- data/lib/canon/diff/formatting_detector.rb +53 -0
- data/lib/canon/diff_formatter/by_line/base_formatter.rb +60 -5
- data/lib/canon/diff_formatter/by_line/html_formatter.rb +68 -16
- data/lib/canon/diff_formatter/by_line/json_formatter.rb +0 -37
- data/lib/canon/diff_formatter/by_line/simple_formatter.rb +0 -42
- data/lib/canon/diff_formatter/by_line/xml_formatter.rb +116 -31
- data/lib/canon/diff_formatter/by_line/yaml_formatter.rb +0 -37
- data/lib/canon/diff_formatter/by_object/base_formatter.rb +126 -19
- data/lib/canon/diff_formatter/by_object/xml_formatter.rb +30 -1
- data/lib/canon/diff_formatter/debug_output.rb +7 -1
- data/lib/canon/diff_formatter/diff_detail_formatter.rb +674 -57
- data/lib/canon/diff_formatter/legend.rb +42 -0
- data/lib/canon/diff_formatter.rb +78 -9
- data/lib/canon/errors.rb +56 -0
- data/lib/canon/formatters/html_formatter_base.rb +35 -1
- data/lib/canon/formatters/json_formatter.rb +3 -0
- data/lib/canon/formatters/yaml_formatter.rb +3 -0
- data/lib/canon/html/data_model.rb +229 -0
- data/lib/canon/html.rb +9 -0
- data/lib/canon/options/cli_generator.rb +70 -0
- data/lib/canon/options/registry.rb +234 -0
- data/lib/canon/rspec_matchers.rb +34 -13
- data/lib/canon/tree_diff/adapters/html_adapter.rb +316 -0
- data/lib/canon/tree_diff/adapters/json_adapter.rb +204 -0
- data/lib/canon/tree_diff/adapters/xml_adapter.rb +285 -0
- data/lib/canon/tree_diff/adapters/yaml_adapter.rb +213 -0
- data/lib/canon/tree_diff/core/attribute_comparator.rb +84 -0
- data/lib/canon/tree_diff/core/matching.rb +241 -0
- data/lib/canon/tree_diff/core/node_signature.rb +164 -0
- data/lib/canon/tree_diff/core/node_weight.rb +135 -0
- data/lib/canon/tree_diff/core/tree_node.rb +450 -0
- data/lib/canon/tree_diff/matchers/hash_matcher.rb +258 -0
- data/lib/canon/tree_diff/matchers/similarity_matcher.rb +168 -0
- data/lib/canon/tree_diff/matchers/structural_propagator.rb +242 -0
- data/lib/canon/tree_diff/matchers/universal_matcher.rb +220 -0
- data/lib/canon/tree_diff/operation_converter.rb +631 -0
- data/lib/canon/tree_diff/operations/operation.rb +92 -0
- data/lib/canon/tree_diff/operations/operation_detector.rb +626 -0
- data/lib/canon/tree_diff/tree_diff_integrator.rb +140 -0
- data/lib/canon/tree_diff.rb +33 -0
- data/lib/canon/validators/json_validator.rb +3 -1
- data/lib/canon/validators/yaml_validator.rb +3 -1
- data/lib/canon/version.rb +1 -1
- data/lib/canon/xml/data_model.rb +22 -23
- data/lib/canon/xml/element_matcher.rb +128 -20
- data/lib/canon/xml/namespace_helper.rb +110 -0
- data/lib/canon.rb +3 -0
- metadata +81 -23
- data/_config.yml +0 -116
- data/docs/ADVANCED_TOPICS.adoc +0 -20
- data/docs/BASIC_USAGE.adoc +0 -16
- data/docs/CUSTOMIZING_BEHAVIOR.adoc +0 -19
- data/docs/DIFF_ARCHITECTURE.adoc +0 -435
- data/docs/DIFF_FORMATTING.adoc +0 -540
- data/docs/FORMATS.adoc +0 -447
- data/docs/INPUT_VALIDATION.adoc +0 -477
- data/docs/MATCH_ARCHITECTURE.adoc +0 -463
- data/docs/MATCH_OPTIONS.adoc +0 -719
- data/docs/MODES.adoc +0 -432
- data/docs/NORMATIVE_INFORMATIVE_DIFFS.adoc +0 -219
- data/docs/OPTIONS.adoc +0 -1387
- data/docs/PREPROCESSING.adoc +0 -491
- data/docs/SEMANTIC_DIFF_REPORT.adoc +0 -528
- data/docs/UNDERSTANDING_CANON.adoc +0 -17
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Architecture
|
|
3
|
+
parent: Understanding
|
|
4
|
+
nav_order: 1
|
|
5
|
+
---
|
|
6
|
+
= Architecture
|
|
7
|
+
:toc:
|
|
8
|
+
:toclevels: 3
|
|
9
|
+
|
|
10
|
+
== Purpose
|
|
11
|
+
|
|
12
|
+
This document explains Canon's 4-layer comparison architecture and how documents flow through preprocessing, algorithm selection, semantic matching, and diff rendering.
|
|
13
|
+
|
|
14
|
+
For a guided walkthrough of choosing configurations, see link:../guides/choosing-configuration.adoc[Choosing Configuration].
|
|
15
|
+
|
|
16
|
+
For detailed 4-layer pipeline documentation, see link:comparison-pipeline.adoc[Comparison Pipeline].
|
|
17
|
+
|
|
18
|
+
== Overview
|
|
19
|
+
|
|
20
|
+
Canon uses a 4-layer architecture that separates concerns for clean, maintainable comparison logic:
|
|
21
|
+
|
|
22
|
+
. **Layer 1 - Preprocessing**: Optional document normalization
|
|
23
|
+
. **Layer 2 - Algorithm Selection**: Choose comparison strategy (DOM vs Semantic)
|
|
24
|
+
. **Layer 3 - Match Options**: Content comparison with configurable dimensions (algorithm-specific)
|
|
25
|
+
. **Layer 4 - Diff Formatting**: Formatted output with visualization (algorithm-specific)
|
|
26
|
+
|
|
27
|
+
Each layer is independent and configurable, allowing fine-grained control over comparison behavior.
|
|
28
|
+
|
|
29
|
+
**Key Insight**: Layers 3 and 4 are **algorithm-specific** - they behave differently depending on which algorithm (DOM or Semantic) is chosen in Layer 2.
|
|
30
|
+
|
|
31
|
+
== System architecture diagram
|
|
32
|
+
|
|
33
|
+
[mermaid]
|
|
34
|
+
----
|
|
35
|
+
graph TD
|
|
36
|
+
A[Input Documents] --> B[Layer 1: Preprocessing]
|
|
37
|
+
B --> C[Layer 2: Algorithm Selection]
|
|
38
|
+
C --> D[Layer 3: Match Options]
|
|
39
|
+
D --> E[Layer 4: Diff Formatting]
|
|
40
|
+
E --> F[Output]
|
|
41
|
+
|
|
42
|
+
C -->|DOM Algorithm| D1[Positional Matching]
|
|
43
|
+
C -->|Semantic Algorithm| D2[Signature Matching]
|
|
44
|
+
|
|
45
|
+
D1 --> E1[Line-based Diff]
|
|
46
|
+
D2 --> E2[Operation-based Diff]
|
|
47
|
+
|
|
48
|
+
style B fill:#e1f5ff
|
|
49
|
+
style C fill:#fff4e1
|
|
50
|
+
style D fill:#ffe1f5
|
|
51
|
+
style E fill:#e1ffe1
|
|
52
|
+
----
|
|
53
|
+
|
|
54
|
+
== Layer 1: Preprocessing
|
|
55
|
+
|
|
56
|
+
=== Purpose
|
|
57
|
+
|
|
58
|
+
Transform documents into a normalized form before comparison. This eliminates format-specific variations that should not affect semantic equivalence.
|
|
59
|
+
|
|
60
|
+
=== Options
|
|
61
|
+
|
|
62
|
+
`none` (default):: No preprocessing - compare documents as-is
|
|
63
|
+
|
|
64
|
+
`c14n`:: Canonical form:
|
|
65
|
+
* XML: W3C Canonical XML 1.1
|
|
66
|
+
* HTML: Normalized HTML structure
|
|
67
|
+
* JSON: Sorted keys, normalized whitespace
|
|
68
|
+
* YAML: Sorted keys, standard format
|
|
69
|
+
|
|
70
|
+
`normalize`:: Normalize whitespace:
|
|
71
|
+
* Collapse multiple whitespace to single space
|
|
72
|
+
* Trim leading/trailing whitespace
|
|
73
|
+
* Normalize line endings
|
|
74
|
+
|
|
75
|
+
`format`:: Pretty-print with standard formatting:
|
|
76
|
+
* 2-space indentation
|
|
77
|
+
* One element/property per line
|
|
78
|
+
* Consistent structure
|
|
79
|
+
|
|
80
|
+
=== Usage
|
|
81
|
+
|
|
82
|
+
.Ruby API
|
|
83
|
+
[example]
|
|
84
|
+
====
|
|
85
|
+
[source,ruby]
|
|
86
|
+
----
|
|
87
|
+
Canon::Comparison.equivalent?(doc1, doc2,
|
|
88
|
+
preprocessing: :normalize
|
|
89
|
+
)
|
|
90
|
+
----
|
|
91
|
+
====
|
|
92
|
+
|
|
93
|
+
.CLI
|
|
94
|
+
[example]
|
|
95
|
+
====
|
|
96
|
+
[source,bash]
|
|
97
|
+
----
|
|
98
|
+
$ canon diff file1.xml file2.xml --preprocessing normalize
|
|
99
|
+
----
|
|
100
|
+
====
|
|
101
|
+
|
|
102
|
+
See link:../features/preprocessing/[Preprocessing documentation] for details.
|
|
103
|
+
|
|
104
|
+
== Layer 2: Algorithm selection
|
|
105
|
+
|
|
106
|
+
=== Purpose
|
|
107
|
+
|
|
108
|
+
Choose the comparison strategy. This is a **critical decision** because it determines how Layers 3 and 4 behave.
|
|
109
|
+
|
|
110
|
+
=== Options
|
|
111
|
+
|
|
112
|
+
`dom` (default):: DOM-based positional comparison
|
|
113
|
+
* Fast, stable, well-tested
|
|
114
|
+
* Position-based element matching
|
|
115
|
+
* No move detection
|
|
116
|
+
* Best for similar documents
|
|
117
|
+
|
|
118
|
+
`semantic` (experimental):: Tree-based semantic diff
|
|
119
|
+
* Slower but more intelligent
|
|
120
|
+
* Signature-based matching
|
|
121
|
+
* Detects moves, merges, splits
|
|
122
|
+
* Best for restructured documents
|
|
123
|
+
|
|
124
|
+
=== Algorithm characteristics
|
|
125
|
+
|
|
126
|
+
[cols="2,3,3"]
|
|
127
|
+
|===
|
|
128
|
+
|Feature |DOM |Semantic
|
|
129
|
+
|
|
130
|
+
|**Stability**
|
|
131
|
+
|Stable (production-ready)
|
|
132
|
+
|Experimental
|
|
133
|
+
|
|
134
|
+
|**Performance**
|
|
135
|
+
|Fast (linear)
|
|
136
|
+
|Slower (quadratic worst case)
|
|
137
|
+
|
|
138
|
+
|**Move Detection**
|
|
139
|
+
|No
|
|
140
|
+
|Yes
|
|
141
|
+
|
|
142
|
+
|**Match Strategy**
|
|
143
|
+
|Positional
|
|
144
|
+
|Signature-based
|
|
145
|
+
|
|
146
|
+
|**Layer 3 Behavior**
|
|
147
|
+
|Element-by-element comparison
|
|
148
|
+
|Signature calculation
|
|
149
|
+
|
|
150
|
+
|**Layer 4 Behavior**
|
|
151
|
+
|Line-based differences
|
|
152
|
+
|Operation-based (INSERT, DELETE, UPDATE, MOVE)
|
|
153
|
+
|
|
154
|
+
|**Best For**
|
|
155
|
+
|Similar documents
|
|
156
|
+
|Restructured documents
|
|
157
|
+
|===
|
|
158
|
+
|
|
159
|
+
=== Usage
|
|
160
|
+
|
|
161
|
+
.Ruby API
|
|
162
|
+
[example]
|
|
163
|
+
====
|
|
164
|
+
[source,ruby]
|
|
165
|
+
----
|
|
166
|
+
# DOM algorithm (default)
|
|
167
|
+
Canon::Comparison.equivalent?(doc1, doc2,
|
|
168
|
+
diff_algorithm: :dom
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# Semantic algorithm
|
|
172
|
+
Canon::Comparison.equivalent?(doc1, doc2,
|
|
173
|
+
diff_algorithm: :semantic
|
|
174
|
+
)
|
|
175
|
+
----
|
|
176
|
+
====
|
|
177
|
+
|
|
178
|
+
.CLI
|
|
179
|
+
[example]
|
|
180
|
+
====
|
|
181
|
+
[source,bash]
|
|
182
|
+
----
|
|
183
|
+
# DOM algorithm
|
|
184
|
+
$ canon diff file1.xml file2.xml --diff-algorithm dom
|
|
185
|
+
|
|
186
|
+
# Semantic algorithm
|
|
187
|
+
$ canon diff file1.xml file2.xml --diff-algorithm semantic
|
|
188
|
+
----
|
|
189
|
+
====
|
|
190
|
+
|
|
191
|
+
See link:algorithms/[Algorithm documentation] for details.
|
|
192
|
+
|
|
193
|
+
== Layer 3: Match options
|
|
194
|
+
|
|
195
|
+
=== Purpose
|
|
196
|
+
|
|
197
|
+
Configure what to compare and how strictly. **This layer is algorithm-specific** - each algorithm interprets match options differently.
|
|
198
|
+
|
|
199
|
+
=== Match dimensions
|
|
200
|
+
|
|
201
|
+
Match dimensions are orthogonal aspects of documents that can be compared independently:
|
|
202
|
+
|
|
203
|
+
`text_content`:: Text within elements/values
|
|
204
|
+
`structural_whitespace`:: Whitespace between elements
|
|
205
|
+
`attribute_whitespace`:: Whitespace in attribute values (XML/HTML)
|
|
206
|
+
`attribute_order`:: Order of attributes (XML/HTML)
|
|
207
|
+
`attribute_values`:: Attribute value content (XML/HTML)
|
|
208
|
+
`key_order`:: Order of object keys (JSON/YAML)
|
|
209
|
+
`comments`:: Comment content and placement
|
|
210
|
+
|
|
211
|
+
Each dimension supports behaviors:
|
|
212
|
+
|
|
213
|
+
* `:strict` - Must match exactly
|
|
214
|
+
* `:normalize` - Match after normalization
|
|
215
|
+
* `:ignore` - Don't compare
|
|
216
|
+
|
|
217
|
+
=== Match profiles
|
|
218
|
+
|
|
219
|
+
Profiles are predefined combinations of dimension settings for common scenarios:
|
|
220
|
+
|
|
221
|
+
`:strict`:: Exact matching - all dimensions use `:strict` behavior
|
|
222
|
+
`:rendered`:: Browser rendering - ignores formatting that doesn't affect display
|
|
223
|
+
`:spec_friendly`:: Test-friendly - ignores formatting, focuses on content
|
|
224
|
+
`:content_only`:: Maximum tolerance - only semantic content matters
|
|
225
|
+
|
|
226
|
+
=== Algorithm-specific behavior
|
|
227
|
+
|
|
228
|
+
**Critical**: The same match options behave differently with each algorithm!
|
|
229
|
+
|
|
230
|
+
* **DOM algorithm**: Uses options for positional element comparison
|
|
231
|
+
* **Semantic algorithm**: Uses options during signature calculation
|
|
232
|
+
|
|
233
|
+
See link:../features/match-options/algorithm-specific-behavior.adoc[Algorithm-Specific Behavior] for detailed comparison.
|
|
234
|
+
|
|
235
|
+
=== Usage
|
|
236
|
+
|
|
237
|
+
.With dimensions
|
|
238
|
+
[example]
|
|
239
|
+
====
|
|
240
|
+
[source,ruby]
|
|
241
|
+
----
|
|
242
|
+
Canon::Comparison.equivalent?(doc1, doc2,
|
|
243
|
+
match: {
|
|
244
|
+
text_content: :normalize,
|
|
245
|
+
structural_whitespace: :ignore,
|
|
246
|
+
comments: :ignore
|
|
247
|
+
}
|
|
248
|
+
)
|
|
249
|
+
----
|
|
250
|
+
====
|
|
251
|
+
|
|
252
|
+
.With profile
|
|
253
|
+
[example]
|
|
254
|
+
====
|
|
255
|
+
[source,ruby]
|
|
256
|
+
----
|
|
257
|
+
Canon::Comparison.equivalent?(doc1, doc2,
|
|
258
|
+
match_profile: :spec_friendly
|
|
259
|
+
)
|
|
260
|
+
----
|
|
261
|
+
====
|
|
262
|
+
|
|
263
|
+
.Profile with dimension overrides
|
|
264
|
+
[example]
|
|
265
|
+
====
|
|
266
|
+
[source,ruby]
|
|
267
|
+
----
|
|
268
|
+
Canon::Comparison.equivalent?(doc1, doc2,
|
|
269
|
+
match_profile: :spec_friendly,
|
|
270
|
+
match: {
|
|
271
|
+
comments: :strict # Override profile setting
|
|
272
|
+
}
|
|
273
|
+
)
|
|
274
|
+
----
|
|
275
|
+
====
|
|
276
|
+
|
|
277
|
+
See link:../features/match-options/[Match Options] for complete reference.
|
|
278
|
+
|
|
279
|
+
== Layer 4: Diff formatting
|
|
280
|
+
|
|
281
|
+
=== Purpose
|
|
282
|
+
|
|
283
|
+
Control how differences are displayed. **This layer is algorithm-specific** - each algorithm generates different output types.
|
|
284
|
+
|
|
285
|
+
=== Diff modes
|
|
286
|
+
|
|
287
|
+
`by_line`:: Traditional line-by-line diff
|
|
288
|
+
* Natural fit for DOM algorithm
|
|
289
|
+
* Shows positional changes
|
|
290
|
+
* Traditional diff format
|
|
291
|
+
|
|
292
|
+
`by_object`:: Tree-based semantic diff
|
|
293
|
+
* Natural fit for Semantic algorithm
|
|
294
|
+
* Shows structural operations
|
|
295
|
+
* Visual tree representation
|
|
296
|
+
|
|
297
|
+
=== Algorithm-specific output
|
|
298
|
+
|
|
299
|
+
**Critical**: Each algorithm produces fundamentally different output!
|
|
300
|
+
|
|
301
|
+
* **DOM algorithm**: Generates line-based differences
|
|
302
|
+
* **Semantic algorithm**: Generates operation-based differences (INSERT, DELETE, UPDATE, MOVE)
|
|
303
|
+
|
|
304
|
+
See link:../features/diff-formatting/algorithm-specific-output.adoc[Algorithm-Specific Output] for detailed comparison.
|
|
305
|
+
|
|
306
|
+
=== Diff options
|
|
307
|
+
|
|
308
|
+
`use_color`:: Enable/disable ANSI color codes (default: `true`)
|
|
309
|
+
|
|
310
|
+
`context_lines`:: Number of unchanged lines around changes (default: `3`)
|
|
311
|
+
|
|
312
|
+
`diff_grouping_lines`:: Group changes within N lines (default: `nil`)
|
|
313
|
+
|
|
314
|
+
See link:../features/diff-formatting/[Diff Formatting] for details.
|
|
315
|
+
|
|
316
|
+
=== Usage
|
|
317
|
+
|
|
318
|
+
.Ruby API
|
|
319
|
+
[example]
|
|
320
|
+
====
|
|
321
|
+
[source,ruby]
|
|
322
|
+
----
|
|
323
|
+
Canon::Comparison.equivalent?(doc1, doc2,
|
|
324
|
+
verbose: true,
|
|
325
|
+
diff_mode: :by_line,
|
|
326
|
+
use_color: true,
|
|
327
|
+
context_lines: 5,
|
|
328
|
+
diff_grouping_lines: 10
|
|
329
|
+
)
|
|
330
|
+
----
|
|
331
|
+
====
|
|
332
|
+
|
|
333
|
+
.CLI
|
|
334
|
+
[example]
|
|
335
|
+
====
|
|
336
|
+
[source,bash]
|
|
337
|
+
----
|
|
338
|
+
$ canon diff file1.xml file2.xml \
|
|
339
|
+
--verbose \
|
|
340
|
+
--diff-mode by-line \
|
|
341
|
+
--context-lines 5 \
|
|
342
|
+
--diff-grouping-lines 10
|
|
343
|
+
----
|
|
344
|
+
====
|
|
345
|
+
|
|
346
|
+
== Complete example: All 4 layers
|
|
347
|
+
|
|
348
|
+
Here's a full configuration showing all 4 layers working together:
|
|
349
|
+
|
|
350
|
+
[source,ruby]
|
|
351
|
+
----
|
|
352
|
+
result = Canon::Comparison.equivalent?(doc1, doc2,
|
|
353
|
+
# Layer 1: Preprocessing
|
|
354
|
+
preprocessing: :normalize,
|
|
355
|
+
|
|
356
|
+
# Layer 2: Algorithm
|
|
357
|
+
diff_algorithm: :semantic,
|
|
358
|
+
|
|
359
|
+
# Layer 3: Match Options
|
|
360
|
+
match_profile: :spec_friendly,
|
|
361
|
+
|
|
362
|
+
# Layer 4: Diff Formatting
|
|
363
|
+
verbose: true,
|
|
364
|
+
diff_mode: :by_object,
|
|
365
|
+
use_color: true,
|
|
366
|
+
context_lines: 3
|
|
367
|
+
)
|
|
368
|
+
----
|
|
369
|
+
|
|
370
|
+
See link:comparison-pipeline.adoc[Comparison Pipeline] for layer-by-layer examples.
|
|
371
|
+
|
|
372
|
+
== Configuration precedence
|
|
373
|
+
|
|
374
|
+
When options are specified in multiple places, Canon resolves them using this hierarchy (highest to lowest priority):
|
|
375
|
+
|
|
376
|
+
[source]
|
|
377
|
+
----
|
|
378
|
+
1. Per-comparison explicit options (highest)
|
|
379
|
+
↓
|
|
380
|
+
2. Per-comparison profile
|
|
381
|
+
↓
|
|
382
|
+
3. Global configuration explicit options
|
|
383
|
+
↓
|
|
384
|
+
4. Global configuration profile
|
|
385
|
+
↓
|
|
386
|
+
5. Format defaults (lowest)
|
|
387
|
+
----
|
|
388
|
+
|
|
389
|
+
.Precedence example
|
|
390
|
+
[example]
|
|
391
|
+
====
|
|
392
|
+
Global configuration:
|
|
393
|
+
|
|
394
|
+
[source,ruby]
|
|
395
|
+
----
|
|
396
|
+
Canon::RSpecMatchers.configure do |config|
|
|
397
|
+
config.xml.match.profile = :spec_friendly
|
|
398
|
+
config.xml.match.options = { comments: :strict }
|
|
399
|
+
end
|
|
400
|
+
----
|
|
401
|
+
|
|
402
|
+
Per-test usage:
|
|
403
|
+
|
|
404
|
+
[source,ruby]
|
|
405
|
+
----
|
|
406
|
+
expect(actual).to be_xml_equivalent_to(expected)
|
|
407
|
+
.with_profile(:rendered)
|
|
408
|
+
.with_options(structural_whitespace: :ignore)
|
|
409
|
+
----
|
|
410
|
+
|
|
411
|
+
**Final resolved options**:
|
|
412
|
+
|
|
413
|
+
* `text_content: :normalize` (from `:rendered` per-test profile)
|
|
414
|
+
* `structural_whitespace: :ignore` (from per-test explicit option)
|
|
415
|
+
* `comments: :strict` (from global explicit option)
|
|
416
|
+
* Other dimensions use `:rendered` profile or format defaults
|
|
417
|
+
====
|
|
418
|
+
|
|
419
|
+
== Benefits of 4-layer architecture
|
|
420
|
+
|
|
421
|
+
**Separation of concerns**:: Each layer has a single responsibility
|
|
422
|
+
|
|
423
|
+
**Composability**:: Mix and match preprocessing, algorithm, matching, and rendering options
|
|
424
|
+
|
|
425
|
+
**Algorithm flexibility**:: Choose between speed (DOM) and intelligence (Semantic)
|
|
426
|
+
|
|
427
|
+
**Testability**:: Each layer can be tested independently
|
|
428
|
+
|
|
429
|
+
**Flexibility**:: Fine-grained control over comparison behavior
|
|
430
|
+
|
|
431
|
+
**Clarity**:: Clear data flow from input to output
|
|
432
|
+
|
|
433
|
+
**Extensibility**:: Easy to add new preprocessing, algorithms, dimensions, or rendering modes
|
|
434
|
+
|
|
435
|
+
== See also
|
|
436
|
+
|
|
437
|
+
* link:comparison-pipeline.adoc[Comparison Pipeline] - Complete 4-layer walkthrough
|
|
438
|
+
* link:algorithms/[Algorithms] - DOM and Semantic algorithm details
|
|
439
|
+
* link:../features/preprocessing/[Preprocessing options]
|
|
440
|
+
* link:../features/match-options/[Match dimensions and profiles]
|
|
441
|
+
* link:../features/match-options/algorithm-specific-behavior.adoc[Algorithm-Specific Behavior]
|
|
442
|
+
* link:../features/diff-formatting/[Diff formatting]
|
|
443
|
+
* link:../features/diff-formatting/algorithm-specific-output.adoc[Algorithm-Specific Output]
|
|
444
|
+
* link:../guides/choosing-configuration.adoc[Choosing Configuration]
|
|
445
|
+
* link:../interfaces/ruby-api/[Ruby API documentation]
|
|
446
|
+
* link:../interfaces/cli/[Command-line interface]
|
|
447
|
+
* link:../interfaces/rspec/[RSpec matchers]
|