1. Purpose
This page describes Canon’s YAML format support, including canonicalization with sorted keys, comment preservation, anchors and aliases, and YAML-specific features.
2. Canonicalization
Canon provides YAML canonicalization with sorted keys and standard formatting.
Key features:
-
Alphabetically sorted mapping keys
-
Consistent indentation
-
Standard YAML 1.2 format
-
Comment preservation (optional)
-
Anchor and alias handling
yaml = <<~YAML
z: 3
a: 1
nested:
y: 2
x: 1
YAML
Canon.format(yaml, :yaml)
# => Keys sorted at all levels
3. Format defaults
| Dimension | Default Behavior |
|---|---|
|
|
|
|
|
|
|
|
Default diff mode: :by_object (tree-based semantic diff)
4. Match profiles for YAML
Canon provides predefined profiles optimized for YAML documents. Each profile configures preprocessing, match options, diff algorithm, and formatting.
4.1. strict profile
Purpose: Character-perfect YAML matching
Configuration:
{
preprocessing: :none,
diff_algorithm: :dom,
diff_mode: :by_object, # Tree-based diff output (YAML default)
match: {
text_content: :strict,
structural_whitespace: :strict,
key_order: :strict,
comments: :strict
}
}
Use when: Testing exact YAML serializer output, verifying YAML formatting compliance.
4.2. rendered profile
Purpose: Normalized YAML comparison
Configuration:
{
preprocessing: :none,
diff_algorithm: :dom,
diff_mode: :by_object,
match: {
text_content: :normalize,
structural_whitespace: :normalize,
key_order: :ignore, # Allow unordered mapping keys
comments: :ignore
}
}
Use when: Comparing YAML data where key order, whitespace, and comments don’t matter.
4.3. spec_friendly profile
Purpose: Test-friendly comparison for RSpec
Configuration:
{
preprocessing: :normalize,
diff_algorithm: :dom,
diff_mode: :by_object,
match: {
text_content: :normalize,
structural_whitespace: :ignore,
key_order: :ignore,
comments: :ignore
}
}
Use when: Writing RSpec tests for YAML generation, testing semantic YAML correctness. Most common for YAML testing.
4.4. content_only profile
Purpose: Maximum tolerance - only values matter
Configuration:
{
preprocessing: :normalize,
diff_algorithm: :dom,
diff_mode: :by_object,
match: {
text_content: :normalize,
structural_whitespace: :ignore,
key_order: :ignore,
comments: :ignore
}
}
Use when: Only YAML structure and values need to match, maximum flexibility for formatting, key order, and comments.
5. YAML-specific features
5.1. Comment support
YAML comments are preserved and can be compared.
# Configuration file
name: test
# Database settings
database:
host: localhost
port: 5432
Comments can be preserved or ignored using the comments dimension:
# Preserve comments
Canon::Comparison.equivalent?(yaml1, yaml2,
match: { comments: :strict }
)
# Ignore comments
Canon::Comparison.equivalent?(yaml1, yaml2,
match: { comments: :ignore }
)
5.2. Key ordering
Mapping keys are sorted alphabetically for consistent output.
# Unordered input
name: Alice
age: 30
city: NYC
# Canonicalized output (keys sorted)
age: 30
city: NYC
name: Alice
5.3. Type detection
YAML’s rich type system is preserved (strings, numbers, booleans, dates, etc.).
string: "123"
number: 123
boolean: true
null_value: null
date: 2024-01-01
float: 123.45
unquoted: hello
YAML automatically detects types:
* 123 (number) ≠ "123" (string)
* true (boolean) ≠ "true" (string)
* null ≠ "null" (string)
* 2024-01-01 (date object) ≠ "2024-01-01" (string)
5.4. Anchors and aliases
YAML anchors (&) and aliases (*) are properly handled.
defaults: &defaults
timeout: 30
retries: 3
production:
<<: *defaults
host: prod.example.com
development:
<<: *defaults
host: dev.example.com
Canon correctly expands anchors and aliases during comparison:
# Expanded equivalent
production:
timeout: 30
retries: 3
host: prod.example.com
development:
timeout: 30
retries: 3
host: dev.example.com
5.5. Multi-line strings
YAML supports multiple styles for multi-line strings.
# Literal block scalar (preserves newlines)
literal: |
Line 1
Line 2
Line 3
# Folded block scalar (folds newlines to spaces)
folded: >
This is a very long line that will be
folded into a single line with spaces
replacing the newlines.
# Plain string
plain: "This is a plain string"
These are treated as different values unless text_content: :normalize is used.
6. Usage examples
6.1. Basic YAML comparison
yaml1 = File.read("config1.yml")
yaml2 = File.read("config2.yml")
Canon::Comparison.equivalent?(yaml1, yaml2)
6.2. Ignoring comments and key order
Canon::Comparison.equivalent?(yaml1, yaml2,
match: {
key_order: :ignore,
comments: :ignore
}
)
6.3. Test-friendly YAML comparison
expect(actual_yaml).to be_yaml_equivalent_to(expected_yaml)
.with_profile(:spec_friendly)
7. Common YAML comparison scenarios
7.1. Configuration file comparison
# Compare config files ignoring formatting
Canon::Comparison.equivalent?(config1, config2,
match_profile: :spec_friendly,
verbose: true
)
8. YAML quirks and edge cases
8.1. Boolean interpretation
# All these are boolean true
value1: true
value2: True
value3: TRUE
value4: yes
value5: Yes
value6: YES
value7: on
value8: On
value9: ON
YAML 1.1 has many boolean synonyms. YAML 1.2 (which Canon uses) is stricter: only true and false are booleans.
8.2. Numeric strings
# Number
port: 8080
# String (quoted)
port: "8080"
# String (with non-numeric character)
port: 8080a
Quoting forces string interpretation.
8.3. Empty values
# Different meanings
key1: # null (no value)
key2: "" # empty string
key3: null # explicit null
key4: [] # empty array
key5: {} # empty object
These are all different and not equivalent.
8.4. Indentation sensitivity
# Valid YAML
parent:
child1: value1
child2: value2
# Invalid YAML (inconsistent indentation)
parent:
child1: value1
child2: value2 # Wrong indentation!
YAML is sensitive to indentation. Use structural_whitespace: :ignore to handle minor indentation differences.
9. See also
-
Comparison Pipeline - Understanding the 4 layers
-
Match Options - All matching options
-
Choosing Configuration - Decision guide
-
Format Support - Overview of all formats
-
JSON Format - JSON-specific features (similar to YAML)
-
XML Format - XML-specific features