Skip to content

Fluent API: immutable .because(...) rule rationale #47

Description

@TristanKruse

Context

Architecture rules explain what must hold, but a future maintainer also needs to know why the
constraint exists. ArchUnitPython already supports a terminal .because(...) rationale; Ruby
should provide the same product concept without copying Python's mutable implementation.

Goal

Allow every executable rule to carry an optional human-readable rationale that appears in failed
test output across the framework-neutral helper, RSpec, and Minitest.

Starting approach

  • Add one common rationale seam to Checkable rather than implementing presentation independently
    in each rule family.
  • Preserve Ruby's immutable-builder convention: .because(reason) returns a new frozen rule and
    leaves the original unchanged.
  • Keep rationale out of violation values; pass it into the central ResultFactory presentation
    path so message construction remains centralized.
  • Expose the normalized rationale for future generated architecture documentation.

Example:

rule = ArchUnit.project_files
               .in_folder('app/controllers/**')
               .should_not
               .depend_on_files
               .in_folder('app/persistence/**')
               .because('controllers should stay thin and delegate persistence')

Acceptance criteria

  • .because(...) is available on every final value that implements Checkable.
  • It trims surrounding whitespace and rejects non-strings or an empty result with ArgumentError.
  • Calling it returns a distinct frozen rule; the original rule remains unchanged and reusable.
  • The reason appears exactly once in a failed framework-neutral, RSpec, and Minitest assertion.
  • Positive and negated RSpec expectations retain their current behavior.
  • The reason does not alter violations, extraction, graph-cache keys, or direct rule.check output.
  • Existing rule families have shared contract tests plus at least one end-to-end fluent example.
  • README and generated API documentation explain the feature.

ArchUnitTS currently lists this as a TODO, so Ruby should follow the shared product vocabulary while
remaining idiomatic and immutable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions