1. Purpose
Canon’s override system allows environment variables to take precedence over programmatic configuration, enabling flexible deployment without code changes.
This page explains how the priority chain works, when overrides apply, and how to use them effectively.
2. Priority chain
Configuration values are resolved using a strict four-level priority:
┌─────────────────────────────────┐
│ 1. Environment Variables │ ← Highest Priority
│ (CANON_XML_DIFF_ALGORITHM) │
└──────────────┬──────────────────┘
↓ overrides
┌─────────────────────────────────┐
│ 2. Programmatic Configuration │
│ (config.xml.diff.algorithm) │
└──────────────┬──────────────────┘
↓ overrides
┌─────────────────────────────────┐
│ 3. Profile Values │
│ (from YAML config profile) │
└──────────────┬──────────────────┘
↓ overrides
┌─────────────────────────────────┐
│ 4. Default Values │ ← Lowest Priority
│ (defined in Canon::Config) │
└─────────────────────────────────┘
Rule: Higher priority always wins, regardless of when values are set.
See Configuration Profiles for details on the profile layer.
3. How overrides work
3.1. Environment variables override programmatic settings
When an environment variable is set, it always takes precedence:
# Set ENV before requiring Canon
ENV['CANON_XML_DIFF_ALGORITHM'] = 'semantic'
# Programmatic setting is IGNORED
config = Canon::Config.instance
config.xml.diff.algorithm = :dom
# ENV wins
puts config.xml.diff.algorithm # => :semantic (not :dom)
3.2. Format-specific variables override global variables
Format-specific ENV vars override global ENV vars:
# Global setting
export CANON_ALGORITHM=dom
# Format-specific override
export CANON_XML_DIFF_ALGORITHM=semantic
# Result:
# - XML uses semantic (format-specific wins)
# - HTML, JSON, YAML use dom (global applies)
3.3. Setting order doesn’t matter
Unlike programmatic configuration, ENV variable priority is positional, not temporal:
# Set ENV first
ENV['CANON_ALGORITHM'] = 'semantic'
# Configure programmatically later
config = Canon::Config.instance
config.xml.diff.algorithm = :dom # This is IGNORED
# ENV still wins, even though set "earlier"
puts config.xml.diff.algorithm # => :semantic
4. When to use environment variables
4.1. Use ENV variables for
- CI/CD configuration
-
Different test runs need different settings without code changes.
# In .github/workflows/test.yml env: CANON_ALGORITHM: semantic CANON_USE_COLOR: false - Container deployment
-
Docker containers with different comparison behavior.
# Dockerfile ENV CANON_XML_DIFF_ALGORITHM=semantic ENV CANON_CONTEXT_LINES=5 - Environment-specific behavior
-
Different settings for development, staging, production.
# Development export CANON_VERBOSE_DIFF=true # Production export CANON_VERBOSE_DIFF=false - Testing algorithm behavior
-
Quick switching between algorithms for comparison.
# Test with DOM CANON_ALGORITHM=dom bundle exec rspec # Test with semantic CANON_ALGORITHM=semantic bundle exec rspec
4.2. Use programmatic config for
- Application defaults
-
Set sensible defaults in your application code.
# config/initializers/canon.rb Canon::Config.configure do |config| config.xml.diff.algorithm = :dom config.xml.diff.use_color = true end - Test-specific overrides
-
Per-test configuration in RSpec.
RSpec.describe "My feature" do it "compares with specific options" do result = Canon::Comparison.equivalent?(doc1, doc2, diff_algorithm: :semantic # Test-specific ) end end - Dynamic configuration
-
Runtime decisions based on document size or other factors.
algorithm = file_size > 100_000 ? :dom : :semantic Canon::Comparison.equivalent?(doc1, doc2, diff_algorithm: algorithm )
5. Verification and debugging
5.1. Check which source provides a value
Use the resolver to inspect configuration sources:
config = Canon::Config.instance
resolver = config.xml.diff.instance_variable_get(:@resolver)
# Check sources
puts "Algorithm from: #{resolver.source_for(:algorithm)}"
# => "env" | "programmatic" | "default"
# See all sources
puts "ENV values: #{resolver.env.inspect}"
puts "Programmatic values: #{resolver.programmatic.inspect}"
puts "Defaults: #{resolver.defaults.inspect}"
5.2. List all active ENV variables
# Get all Canon ENV variables
canon_env = ENV.select { |k, v| k.start_with?('CANON_') }
puts "Active Canon ENV variables:"
canon_env.each do |key, value|
puts " #{key} = #{value}"
end
5.3. Test ENV override behavior
# Verify ENV takes precedence
ENV['CANON_ALGORITHM'] = 'semantic'
config = Canon::Config.instance
config.xml.diff.algorithm = :dom # Should be ignored
if config.xml.diff.algorithm == :semantic
puts "✓ ENV override working correctly"
else
puts "✗ ENV override NOT working!"
end
6. Common patterns
6.1. Pattern 1: Sensible defaults with ENV override
# config/initializers/canon.rb
# Set defaults that work for most cases
Canon::RSpecMatchers.configure do |config|
config.xml.diff.algorithm = :dom
config.xml.diff.use_color = !ENV['CI']
end
# Users can override per test run:
# CANON_ALGORITHM=semantic bundle exec rspec
6.2. Pattern 2: Environment-based configuration
# config/environments/development.rb
ENV['CANON_VERBOSE_DIFF'] = 'true'
ENV['CANON_USE_COLOR'] = 'true'
# config/environments/production.rb
ENV['CANON_VERBOSE_DIFF'] = 'false'
ENV['CANON_USE_COLOR'] = 'false'
7. Troubleshooting
7.1. ENV variable not working
Problem: ENV variable seems to be ignored.
Checklist:
-
Variable name correct?
# Correct export CANON_XML_DIFF_ALGORITHM=semantic # Wrong (underscore placement) export CANON_XML_DIFFALGORITHM=semantic -
ENV set before Canon loads?
# Wrong order require 'canon' ENV['CANON_ALGORITHM'] = 'semantic' # Too late! # Correct order ENV['CANON_ALGORITHM'] = 'semantic' require 'canon' -
Value valid for attribute type?
# Wrong (should be symbol name) export CANON_ALGORITHM=:semantic # Correct export CANON_ALGORITHM=semantic
7.2. Programmatic setting not working
Problem: Setting config.xml.diff.algorithm = :semantic doesn’t work.
Solution: Check if ENV variable is set:
# Check current ENV
echo $CANON_XML_DIFF_ALGORITHM
echo $CANON_ALGORITHM
# If set, unset it
unset CANON_XML_DIFF_ALGORITHM
unset CANON_ALGORITHM
7.3. Inconsistent behavior across runs
Problem: Tests behave differently on different machines.
Cause: One machine has Canon ENV variables set in shell profile.
Solution: Document required ENV variables or unset them in test setup:
# spec/spec_helper.rb
RSpec.configure do |config|
config.before(:suite) do
# Clear any Canon ENV vars to ensure consistent tests
ENV.keys.select { |k| k.start_with?('CANON_') }.each do |key|
ENV.delete(key)
end
end
end
8. Best practices
8.1. Document expected ENV variables
Create a .env.example or document ENV variables:
# .env.example
# Canon Configuration
# Uncomment and modify as needed
# CANON_ALGORITHM=dom
# CANON_USE_COLOR=true
# CANON_MAX_FILE_SIZE=5242880
8.2. Use ENV for deployment, code for defaults
# Good: Code provides defaults
Canon::RSpecMatchers.configure do |config|
config.xml.diff.algorithm = :dom # Default
end
# Good: ENV overrides for deployment
# In production: CANON_ALGORITHM=semantic
9. See also
-
Environment Configuration - Overview and usage
-
Size Limits - Limit-specific ENV variables
-
Environment Variables Reference - Complete listing
-
Options Across Interfaces - How options work in CLI, Ruby, RSpec