Skip to content

refactor: architecture cleanup — autoload, dimensions, pipeline, type safety#153

Merged
ronaldtse merged 4 commits into
mainfrom
refactor/todo-improve
Jun 15, 2026
Merged

refactor: architecture cleanup — autoload, dimensions, pipeline, type safety#153
ronaldtse merged 4 commits into
mainfrom
refactor/todo-improve

Conversation

@ronaldtse

Copy link
Copy Markdown
Contributor

Consolidates four major architectural improvements (TODO.dims items D04, D07, D09–D14) into one cohesive refactor.

Changes

1. Autoload migration (D11)

Replace all internal require_relative with Ruby autoload declared in immediate parent namespace files.

  • Added namespace files for Canon::{Xml, Html, Diff, Formatters, Validators, PrettyPrinter, Options, Commands} and Canon::TreeDiff::* sub-namespaces — each declares autoloads for its direct children.
  • lib/canon.rb now uses require "canon/..." (load-path style) for bootstrap.
  • class Canon::Config declares autoloads for ConfigDSL, EnvProvider, EnvSchema, OverrideResolver, ProfileLoader, TypeConverter.
  • Zero require_relative calls remain in lib/canon/ (only documentation mentions in comments).

2. Dimension architecture (D04)

Replace 8 dimension classes (BaseDimension + 7 subclasses) with a single Dimension value object and DimensionSet.

  • Comparison logic now lives in comparators where it has full node context.
  • Registry provides pre-built sets per format: XML/HTML share 7 dimensions, JSON has 3, YAML has 4. Format aliases (html, html4, html5) all resolve to the XML set.
  • Removes ~300 lines of duplicated boilerplate.

3. Shared pipeline + DiffNodeBuilder (D07, D09)

  • New Pipeline module consolidates shared steps between DOM and Semantic diff (format detection, validation, config merge, parsing) — keeps the two algorithm cores independent while preventing drift in pipeline mechanics.
  • DiffNodeBuilder is the single DiffNode factory for the DOM comparison path.

4. ConfigDSL + type safety (D10, D12, D13, D14)

  • ConfigDSL eliminates the 5-line getter/setter boilerplate per config attribute; provides single source of truth for attribute types/enums/defaults.
  • Eliminate respond_to? throughout — replaced with is_a? type checks or proper polymorphism.
  • Eliminate instance_variable_set in ConfigDSL (used class_variable_defined? + class-variable access).
  • Fix NodeInspector namespace typo.

5. New specs

  • spec/canon/comparison/pipeline_spec.rb — Pipeline module coverage.
  • spec/canon/config/config_dsl_spec.rb — ConfigDSL coverage.
  • spec/canon/comparison/dimensions/Dimension, DimensionSet, Registry.

6. Housekeeping

  • Auto-correct rubocop style offenses; update .rubocop_todo.yml.
  • Minor Rakefile formatting (rubocop auto-correct).

Test results

  • 2411 examples, 0 failures, 1 pending
  • bundle exec rubocop clean

Architectural compliance

  • ✅ No require_relative in lib/canon/ (autoload only)
  • ✅ No respond_to? for type checking
  • ✅ No instance_variable_set/get
  • ✅ No send to bypass visibility
  • ✅ DOM and Semantic algorithms remain architecturally separate
  • ✅ Real model instances in specs (no doubles)

… safety

Consolidates four major architectural improvements:

1. **Autoload migration (D11)**: Replace all internal `require_relative`
   with Ruby `autoload` declared in immediate parent namespace files.
   Added namespace files for Canon::{Xml,Html,Diff,Formatters,Validators,
   PrettyPrinter,Options,Commands,TreeDiff::*} with all children autoloaded.
   Zero `require_relative` calls remain in lib/canon/.

2. **Dimension architecture (D04)**: Replace 8 dimension classes
   (BaseDimension + 7 subclasses) with a single Dimension value object
   and DimensionSet. Comparison logic lives in comparators where it has
   full node context. Registry provides pre-built sets per format
   (XML/HTML share 7, JSON has 3, YAML has 4).

3. **Shared pipeline + DiffNodeBuilder (D07, D09)**: Extract Pipeline
   module consolidating shared steps between DOM and Semantic diff
   (format detection, validation, config merge, parsing). DiffNodeBuilder
   is the single DiffNode factory for the DOM path.

4. **ConfigDSL + type safety (D10, D12, D13)**: ConfigDSL eliminates
   5-line getter/setter boilerplate per config attribute. Eliminate
   `respond_to?` and `instance_variable_set` throughout. Fix
   NodeInspector namespace typo (D14).

Also:
- Add comprehensive specs for Pipeline, ConfigDSL, and Dimensions
- Auto-correct rubocop style offenses
- Update .rubocop_todo.yml

All 2411 specs pass; rubocop clean.
Auto-correct rubocop 1.87 Performance/StringInclude offenses in spec
files: replace .match(/literal/) with .include?('literal'). Regenerate
.rubocop_todo.yml against rubocop 1.87.
moxml 0.1.24+ autoloads Moxml::Adapter::Base from lib/moxml.rb
instead of eager require_relative in adapter/rexml.rb. Loading
adapter/rexml directly without first loading the moxml namespace
leaves Base undefined under Opal, breaking the smoke test suite.

Prepending 'moxml' to runner.requires sets up the autoload chain
before the Rexml adapter references Base.
The previous performance comparator loaded both branches' Canon code
into a single Ruby process via module_eval, relying on $LOAD_PATH
tricks to switch between them. Canon's move to autoload exposed the
flaw: autoload resolution is global, so constants resolved to whatever
lib path sat at the front of $LOAD_PATH, mixing files from both
branches and producing NameError when base-main's html_comparator
referenced Canon::Comparison::MarkupComparator before it was defined.

The new design runs the benchmark suite in two separate Ruby
subprocesses — one per branch — each loading its own Canon from disk
with full isolation. Each subprocess emits JSON to stdout via the new
lib/tasks/performance_report.rb entry point; the comparator diffs the
two payloads and reports regressions.

Side fixes:
- Lower-bound IPS clamp prevents nonsensical negative values when
  variance exceeds the mean for very fast benchmarks.
- Drops broken performance:json and performance:yaml rake tasks that
  used $send to call a non-existent private method.
@ronaldtse ronaldtse merged commit 03bca73 into main Jun 15, 2026
15 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant