Class: Canon::Config::DiffConfig

Inherits:
Object
  • Object
show all
Defined in:
lib/canon/config.rb

Overview

Diff configuration for output formatting

Constant Summary collapse

VALID_ENUM_VALUES =

Valid values for enum-like configuration options

{
  mode: %i[by_line by_object pretty_diff],
  show_diffs: %i[all normative informative],
  algorithm: %i[dom semantic],
  parser: %i[sax dom],
  display_preprocessing: %i[none pretty_print normalize_pretty_print
                            c14n],
  display_format: %i[raw canonical],
  pretty_printer_indent_type: %i[space tab],
  character_visualization: [true, false, :content_only],
  theme: %i[light dark retro claude cyberpunk],
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(format = nil) ⇒ DiffConfig

Returns a new instance of DiffConfig.



311
312
313
314
315
# File 'lib/canon/config.rb', line 311

def initialize(format = nil)
  @format = format
  @resolver = build_resolver(format)
  @pretty_printer = PrettyPrinterConfig.new(@resolver)
end

Instance Attribute Details

#pretty_printerObject (readonly)

Returns the value of attribute pretty_printer.



295
296
297
# File 'lib/canon/config.rb', line 295

def pretty_printer
  @pretty_printer
end

Class Method Details

.validate_config_value!(key, value) ⇒ Object

Validate a config value against its allowed enum values

Raises:

  • (ArgumentError)


338
339
340
341
342
343
344
345
346
347
# File 'lib/canon/config.rb', line 338

def self.validate_config_value!(key, value)
  valid = VALID_ENUM_VALUES[key]
  return unless valid

  return if valid.include?(value)

  raise ArgumentError,
        "Invalid value #{value.inspect} for #{key}. " \
        "Valid values: #{valid.map(&:inspect).join(', ')}"
end

Instance Method Details

#algorithmObject



674
675
676
# File 'lib/canon/config.rb', line 674

def algorithm
  @resolver.resolve(:algorithm)
end

#algorithm=(value) ⇒ Object



678
679
680
681
# File 'lib/canon/config.rb', line 678

def algorithm=(value)
  self.class.validate_config_value!(:algorithm, value)
  @resolver.set_programmatic(:algorithm, value)
end

#apply_profile_data(data) ⇒ Object



322
323
324
325
326
327
328
329
330
331
# File 'lib/canon/config.rb', line 322

def apply_profile_data(data)
  return unless data

  data.each do |key, value|
    sym_key = key.to_sym
    coerced = coerce_profile_value(sym_key, value)
    self.class.validate_config_value!(sym_key, coerced)
    @resolver.set_profile(sym_key, coerced)
  end
end

#character_visualizationObject

Controls whether invisible characters (spaces, tabs, non-breaking spaces, etc.) are replaced with visible Unicode symbols in diff output.

Values:

true          - apply the full default visualization map (default)
false         - disable visualization; output plain text
:content_only - apply visualization only to text content, not
                to structural indentation whitespace.


658
659
660
661
662
663
664
665
666
667
# File 'lib/canon/config.rb', line 658

def character_visualization
  val = @resolver.resolve(:character_visualization)
  # Coerce symbol booleans that may arrive via ENV (env_schema uses :symbol type
  # so "true"/"false" env strings become :true/:false symbols)
  case val
  when true, :true then true # rubocop:disable Lint/BooleanSymbol
  when false, :false then false # rubocop:disable Lint/BooleanSymbol
  else val # true/false from programmatic, or :content_only
  end
end

#character_visualization=(value) ⇒ Object



669
670
671
672
# File 'lib/canon/config.rb', line 669

def character_visualization=(value)
  self.class.validate_config_value!(:character_visualization, value)
  @resolver.set_programmatic(:character_visualization, value)
end

#clear_profile!Object



333
334
335
# File 'lib/canon/config.rb', line 333

def clear_profile!
  @resolver.clear_profile!
end

#collapse_whitespace_elementsObject

Element names where whitespace is COLLAPSED (HTML-style behavior). Multiple whitespace chars collapse to single space; boundaries preserved. ENV variable: CANON_<FORMAT>_DIFF_COLLAPSE_WHITESPACE_ELEMENTS



565
566
567
# File 'lib/canon/config.rb', line 565

def collapse_whitespace_elements
  @resolver.resolve(:collapse_whitespace_elements) || []
end

#collapse_whitespace_elements=(value) ⇒ Object



569
570
571
572
# File 'lib/canon/config.rb', line 569

def collapse_whitespace_elements=(value)
  @resolver.set_programmatic(:collapse_whitespace_elements,
                             Array(value).map(&:to_s))
end

#compact_semantic_reportObject

Render element nodes in the Semantic Diff Report as compact inline XML (e.g. <strong>Annex</strong>) instead of the verbose node_info description string (e.g. “name: strong namespace_uri: …”).

Default: false (keep existing verbose format for backwards compatibility) ENV variable: CANON_<FORMAT>_DIFF_COMPACT_SEMANTIC_REPORT



629
630
631
# File 'lib/canon/config.rb', line 629

def compact_semantic_report
  @resolver.resolve(:compact_semantic_report)
end

#compact_semantic_report=(value) ⇒ Object



633
634
635
# File 'lib/canon/config.rb', line 633

def compact_semantic_report=(value)
  @resolver.set_programmatic(:compact_semantic_report, value)
end

#context_linesObject



367
368
369
# File 'lib/canon/config.rb', line 367

def context_lines
  @resolver.resolve(:context_lines)
end

#context_lines=(value) ⇒ Object



371
372
373
# File 'lib/canon/config.rb', line 371

def context_lines=(value)
  @resolver.set_programmatic(:context_lines, value)
end

#display_formatObject



524
525
526
# File 'lib/canon/config.rb', line 524

def display_format
  @resolver.resolve(:display_format)
end

#display_format=(value) ⇒ Object



528
529
530
531
# File 'lib/canon/config.rb', line 528

def display_format=(value)
  self.class.validate_config_value!(:display_format, value)
  @resolver.set_programmatic(:display_format, value)
end

#display_preprocessingObject

Controls how documents are normalized *for display* before the line diff. This is independent of FormatConfig#preprocessing, which controls normalization for comparison (equivalence detection).

Values:

:none         - use documents as-is (default, existing behaviour)
:pretty_print - run through Canon::PrettyPrinter::Xml before diffing
:c14n         - run through XML C14N normalization before diffing


541
542
543
# File 'lib/canon/config.rb', line 541

def display_preprocessing
  @resolver.resolve(:display_preprocessing)
end

#display_preprocessing=(value) ⇒ Object



545
546
547
548
# File 'lib/canon/config.rb', line 545

def display_preprocessing=(value)
  self.class.validate_config_value!(:display_preprocessing, value)
  @resolver.set_programmatic(:display_preprocessing, value)
end

#expand_differenceObject

Show the full serialized node content (including children) in element_structure diffs instead of just the tag name.

Default: false (show only the tag name, e.g. <biblio-tag>) ENV variable: CANON_<FORMAT>_DIFF_EXPAND_DIFFERENCE



642
643
644
# File 'lib/canon/config.rb', line 642

def expand_difference
  @resolver.resolve(:expand_difference)
end

#expand_difference=(value) ⇒ Object



646
647
648
# File 'lib/canon/config.rb', line 646

def expand_difference=(value)
  @resolver.set_programmatic(:expand_difference, value)
end

#grouping_linesObject



375
376
377
# File 'lib/canon/config.rb', line 375

def grouping_lines
  @resolver.resolve(:grouping_lines)
end

#grouping_lines=(value) ⇒ Object



379
380
381
# File 'lib/canon/config.rb', line 379

def grouping_lines=(value)
  @resolver.set_programmatic(:grouping_lines, value)
end

#max_diff_linesObject

Maximum diff output lines (default 10,000)



722
723
724
# File 'lib/canon/config.rb', line 722

def max_diff_lines
  @resolver.resolve(:max_diff_lines)
end

#max_diff_lines=(value) ⇒ Object



726
727
728
# File 'lib/canon/config.rb', line 726

def max_diff_lines=(value)
  @resolver.set_programmatic(:max_diff_lines, value)
end

#max_file_sizeObject

File size limit in bytes (default 5MB)



704
705
706
# File 'lib/canon/config.rb', line 704

def max_file_size
  @resolver.resolve(:max_file_size)
end

#max_file_size=(value) ⇒ Object



708
709
710
# File 'lib/canon/config.rb', line 708

def max_file_size=(value)
  @resolver.set_programmatic(:max_file_size, value)
end

#max_node_countObject

Maximum node count in tree (default 10,000)



713
714
715
# File 'lib/canon/config.rb', line 713

def max_node_count
  @resolver.resolve(:max_node_count)
end

#max_node_count=(value) ⇒ Object



717
718
719
# File 'lib/canon/config.rb', line 717

def max_node_count=(value)
  @resolver.set_programmatic(:max_node_count, value)
end

#modeObject

Accessors with ENV override support



350
351
352
# File 'lib/canon/config.rb', line 350

def mode
  @resolver.resolve(:mode)
end

#mode=(value) ⇒ Object



354
355
356
357
# File 'lib/canon/config.rb', line 354

def mode=(value)
  self.class.validate_config_value!(:mode, value)
  @resolver.set_programmatic(:mode, value)
end

#parserObject

XML parser backend (:sax or :dom, default :sax)



684
685
686
# File 'lib/canon/config.rb', line 684

def parser
  @resolver.resolve(:parser)
end

#parser=(value) ⇒ Object



688
689
690
691
# File 'lib/canon/config.rb', line 688

def parser=(value)
  self.class.validate_config_value!(:parser, value)
  @resolver.set_programmatic(:parser, value)
end

#preserve_whitespace_elementsObject

Element names where whitespace is PRESERVED exactly (no manipulation). All whitespace characters are significant in these elements. ENV variable: CANON_<FORMAT>_DIFF_PRESERVE_WHITESPACE_ELEMENTS



553
554
555
# File 'lib/canon/config.rb', line 553

def preserve_whitespace_elements
  @resolver.resolve(:preserve_whitespace_elements) || []
end

#preserve_whitespace_elements=(value) ⇒ Object



557
558
559
560
# File 'lib/canon/config.rb', line 557

def preserve_whitespace_elements=(value)
  @resolver.set_programmatic(:preserve_whitespace_elements,
                             Array(value).map(&:to_s))
end

#pretty_printed_expectedObject

When true, whitespace-only text nodes starting with “n” in :collapse elements of the expected (fixture) document are treated as structural indentation and dropped from both comparison and display. Use this when fixture files are indented but received XML is compact. ENV variable: CANON_<FORMAT>_DIFF_PRETTY_PRINTED_EXPECTED



590
591
592
# File 'lib/canon/config.rb', line 590

def pretty_printed_expected
  @resolver.resolve(:pretty_printed_expected)
end

#pretty_printed_expected=(value) ⇒ Object



594
595
596
# File 'lib/canon/config.rb', line 594

def pretty_printed_expected=(value)
  @resolver.set_programmatic(:pretty_printed_expected, value)
end

#pretty_printed_receivedObject

When true, whitespace-only text nodes starting with “n” in :normalize elements of the received document are treated as structural indentation and dropped from both comparison and display. Use this when received XML may be pretty-printed but the fixture is compact. ENV variable: CANON_<FORMAT>_DIFF_PRETTY_PRINTED_RECEIVED



603
604
605
# File 'lib/canon/config.rb', line 603

def pretty_printed_received
  @resolver.resolve(:pretty_printed_received)
end

#pretty_printed_received=(value) ⇒ Object



607
608
609
# File 'lib/canon/config.rb', line 607

def pretty_printed_received=(value)
  @resolver.set_programmatic(:pretty_printed_received, value)
end

#pretty_printer_sort_attributesObject

When true, attributes on each element are sorted by namespace URI then local name in the pretty-printed display, eliminating spurious diff noise from differing attribute order. ENV variable: CANON_<FORMAT>_DIFF_PRETTY_PRINTER_SORT_ATTRIBUTES



615
616
617
# File 'lib/canon/config.rb', line 615

def pretty_printer_sort_attributes
  @resolver.resolve(:pretty_printer_sort_attributes)
end

#pretty_printer_sort_attributes=(value) ⇒ Object



619
620
621
# File 'lib/canon/config.rb', line 619

def pretty_printer_sort_attributes=(value)
  @resolver.set_programmatic(:pretty_printer_sort_attributes, value)
end

#reset!Object



317
318
319
320
# File 'lib/canon/config.rb', line 317

def reset!
  @resolver = build_resolver(@format)
  @pretty_printer = PrettyPrinterConfig.new(@resolver)
end

#show_diffsObject



383
384
385
# File 'lib/canon/config.rb', line 383

def show_diffs
  @resolver.resolve(:show_diffs)
end

#show_diffs=(value) ⇒ Object



387
388
389
390
# File 'lib/canon/config.rb', line 387

def show_diffs=(value)
  self.class.validate_config_value!(:show_diffs, value)
  @resolver.set_programmatic(:show_diffs, value)
end

#show_line_numbered_inputsObject



516
517
518
# File 'lib/canon/config.rb', line 516

def show_line_numbered_inputs
  @resolver.resolve(:show_line_numbered_inputs)
end

#show_line_numbered_inputs=(value) ⇒ Object



520
521
522
# File 'lib/canon/config.rb', line 520

def show_line_numbered_inputs=(value)
  @resolver.set_programmatic(:show_line_numbered_inputs, value)
end

#show_preprocessed_expectedObject

Show only the EXPECTED (fixture) block in the preprocessed-inputs section. Has no effect unless show_preprocessed_inputs or verbose_diff is also set. Use show_preprocessed_expected: true together with show_preprocessed_received: false to display only the preprocessed fixture while suppressing the preprocessed received output.

ENV variable: CANON_<FORMAT>_DIFF_SHOW_PREPROCESSED_EXPECTED



452
453
454
# File 'lib/canon/config.rb', line 452

def show_preprocessed_expected
  @resolver.resolve(:show_preprocessed_expected)
end

#show_preprocessed_expected=(value) ⇒ Object



456
457
458
# File 'lib/canon/config.rb', line 456

def show_preprocessed_expected=(value)
  @resolver.set_programmatic(:show_preprocessed_expected, value)
end

#show_preprocessed_inputsObject



437
438
439
# File 'lib/canon/config.rb', line 437

def show_preprocessed_inputs
  @resolver.resolve(:show_preprocessed_inputs)
end

#show_preprocessed_inputs=(value) ⇒ Object



441
442
443
# File 'lib/canon/config.rb', line 441

def show_preprocessed_inputs=(value)
  @resolver.set_programmatic(:show_preprocessed_inputs, value)
end

#show_preprocessed_receivedObject

Show only the RECEIVED (actual) block in the preprocessed-inputs section. Combined with show_preprocessed_expected: false (or leaving it at the default false) this suppresses the fixture preprocessing display while still showing what the received document looked like after preprocessing.

ENV variable: CANON_<FORMAT>_DIFF_SHOW_PREPROCESSED_RECEIVED



467
468
469
# File 'lib/canon/config.rb', line 467

def show_preprocessed_received
  @resolver.resolve(:show_preprocessed_received)
end

#show_preprocessed_received=(value) ⇒ Object



471
472
473
# File 'lib/canon/config.rb', line 471

def show_preprocessed_received=(value)
  @resolver.set_programmatic(:show_preprocessed_received, value)
end

#show_prettyprint_expectedObject

Show only the EXPECTED (fixture) block in the pretty-print section. Useful when the fixture is what needs updating and the received side is not needed for copy-pasting.

ENV variable: CANON_<FORMAT>_DIFF_SHOW_PRETTYPRINT_EXPECTED



495
496
497
# File 'lib/canon/config.rb', line 495

def show_prettyprint_expected
  @resolver.resolve(:show_prettyprint_expected)
end

#show_prettyprint_expected=(value) ⇒ Object



499
500
501
# File 'lib/canon/config.rb', line 499

def show_prettyprint_expected=(value)
  @resolver.set_programmatic(:show_prettyprint_expected, value)
end

#show_prettyprint_inputsObject

Show both EXPECTED and RECEIVED blocks in a fixture-ready pretty-printed section. The output uses the same pretty-printer as display_preprocessing: :pretty_print (one tag per line, indentation) but with no character visualization — whitespace appears as plain ASCII so the output can be copy-pasted directly into RSpec fixture heredocs.

ENV variable: CANON_<FORMAT>_DIFF_SHOW_PRETTYPRINT_INPUTS



482
483
484
# File 'lib/canon/config.rb', line 482

def show_prettyprint_inputs
  @resolver.resolve(:show_prettyprint_inputs)
end

#show_prettyprint_inputs=(value) ⇒ Object



486
487
488
# File 'lib/canon/config.rb', line 486

def show_prettyprint_inputs=(value)
  @resolver.set_programmatic(:show_prettyprint_inputs, value)
end

#show_prettyprint_receivedObject

Show only the RECEIVED (actual) block in the pretty-print section. Use this to get a copy-pasteable pretty-printed form of the generated output (the most common fixture-update workflow).

ENV variable: CANON_<FORMAT>_DIFF_SHOW_PRETTYPRINT_RECEIVED



508
509
510
# File 'lib/canon/config.rb', line 508

def show_prettyprint_received
  @resolver.resolve(:show_prettyprint_received)
end

#show_prettyprint_received=(value) ⇒ Object



512
513
514
# File 'lib/canon/config.rb', line 512

def show_prettyprint_received=(value)
  @resolver.set_programmatic(:show_prettyprint_received, value)
end

#show_raw_expectedObject

Show only the EXPECTED (fixture) block in the raw-inputs section. Has no effect unless show_raw_inputs or verbose_diff is also set. Use show_raw_expected: false together with show_raw_received: true (or show_raw_inputs: true) to suppress the fixture display while keeping the received output.

ENV variable: CANON_<FORMAT>_DIFF_SHOW_RAW_EXPECTED



415
416
417
# File 'lib/canon/config.rb', line 415

def show_raw_expected
  @resolver.resolve(:show_raw_expected)
end

#show_raw_expected=(value) ⇒ Object



419
420
421
# File 'lib/canon/config.rb', line 419

def show_raw_expected=(value)
  @resolver.set_programmatic(:show_raw_expected, value)
end

#show_raw_inputsObject



400
401
402
# File 'lib/canon/config.rb', line 400

def show_raw_inputs
  @resolver.resolve(:show_raw_inputs)
end

#show_raw_inputs=(value) ⇒ Object



404
405
406
# File 'lib/canon/config.rb', line 404

def show_raw_inputs=(value)
  @resolver.set_programmatic(:show_raw_inputs, value)
end

#show_raw_receivedObject

Show only the RECEIVED (actual) block in the raw-inputs section. Combined with show_raw_expected: false (or leaving it at the default false) this suppresses the fixture while still displaying the output that was generated.

ENV variable: CANON_<FORMAT>_DIFF_SHOW_RAW_RECEIVED



429
430
431
# File 'lib/canon/config.rb', line 429

def show_raw_received
  @resolver.resolve(:show_raw_received)
end

#show_raw_received=(value) ⇒ Object



433
434
435
# File 'lib/canon/config.rb', line 433

def show_raw_received=(value)
  @resolver.set_programmatic(:show_raw_received, value)
end

#strip_whitespace_elementsObject

Element names where whitespace-only text nodes are STRIPPED. ENV variable: CANON_<FORMAT>_DIFF_STRIP_WHITESPACE_ELEMENTS



576
577
578
# File 'lib/canon/config.rb', line 576

def strip_whitespace_elements
  @resolver.resolve(:strip_whitespace_elements) || []
end

#strip_whitespace_elements=(value) ⇒ Object



580
581
582
583
# File 'lib/canon/config.rb', line 580

def strip_whitespace_elements=(value)
  @resolver.set_programmatic(:strip_whitespace_elements,
                             Array(value).map(&:to_s))
end

#themeObject

Theme name (:light, :dark, :retro, :claude)



694
695
696
# File 'lib/canon/config.rb', line 694

def theme
  @resolver.resolve(:theme)
end

#theme=(value) ⇒ Object



698
699
700
701
# File 'lib/canon/config.rb', line 698

def theme=(value)
  self.class.validate_config_value!(:theme, value)
  @resolver.set_programmatic(:theme, value)
end

#to_hObject

Build diff options



731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
# File 'lib/canon/config.rb', line 731

def to_h
  {
    diff: mode,
    use_color: use_color,
    context_lines: context_lines,
    grouping_lines: grouping_lines,
    show_diffs: show_diffs,
    verbose_diff: verbose_diff,
    diff_algorithm: algorithm,
    parser: parser,
    show_raw_inputs: show_raw_inputs,
    show_raw_expected: show_raw_expected,
    show_raw_received: show_raw_received,
    show_preprocessed_inputs: show_preprocessed_inputs,
    show_preprocessed_expected: show_preprocessed_expected,
    show_preprocessed_received: show_preprocessed_received,
    show_prettyprint_inputs: show_prettyprint_inputs,
    show_prettyprint_expected: show_prettyprint_expected,
    show_prettyprint_received: show_prettyprint_received,
    show_line_numbered_inputs: show_line_numbered_inputs,
    character_visualization: character_visualization,
    display_format: display_format,
    display_preprocessing: display_preprocessing,
    pretty_printer_indent: pretty_printer.indent,
    pretty_printer_indent_type: pretty_printer.indent_type,
    preserve_whitespace_elements: preserve_whitespace_elements,
    collapse_whitespace_elements: collapse_whitespace_elements,
    strip_whitespace_elements: strip_whitespace_elements,
    pretty_printed_expected: pretty_printed_expected,
    pretty_printed_received: pretty_printed_received,
    compact_semantic_report: compact_semantic_report,
    expand_difference: expand_difference,
    max_file_size: max_file_size,
    max_node_count: max_node_count,
    max_diff_lines: max_diff_lines,
    theme: theme,
  }
end

#use_colorObject



359
360
361
# File 'lib/canon/config.rb', line 359

def use_color
  @resolver.resolve(:use_color)
end

#use_color=(value) ⇒ Object



363
364
365
# File 'lib/canon/config.rb', line 363

def use_color=(value)
  @resolver.set_programmatic(:use_color, value)
end

#verbose_diffObject



392
393
394
# File 'lib/canon/config.rb', line 392

def verbose_diff
  @resolver.resolve(:verbose_diff)
end

#verbose_diff=(value) ⇒ Object



396
397
398
# File 'lib/canon/config.rb', line 396

def verbose_diff=(value)
  @resolver.set_programmatic(:verbose_diff, value)
end