ores.cpp.history-provider-registrar

Table of Contents

This page is the node for the ores.cpp.history-provider-registrar facet of the codegen template library, part of the ores.cpp technical space. Each archetype below is documented, and holds its mustache source, in its own linked page. This page is the facet-level summary and the routing table to them.

1. Summary

Two templates forming the per-entity history-provider registrar pair. The header declares register_<entity>_history_provider(). The implementation builds the entity's history provider and registers it into the ores::history::service::dispatch_registry the caller passes in. The provider reads the entity's version history through its service and renders each version through its field mapper. The per-component service registrar delegates to each per-entity registrar, so a compile failure in one entity's provider cannot block registration of the rest. Consumed by the default (no --address) generation set for every domain_entity.

2. The ores.cpp.history-provider-registrar facet

Projects the entity model onto its generic-history registration surface: the entity_type key the generic history.v1.get subject dispatches on, the service read that produces the version history, and the field mapper that renders each version for display. It consumes the same domain_entity model and the same presentation::render_<entity>_fields mapper the ores.cpp.presentation facet emits. It derives its entity_type key identically to the entity_name the notify_trigger SQL facet stamps on each row change (product_component_entity_singular), so the two sides of the pipeline can never drift out of sync.

2.1. Junctions

A junction is stored with its history: each link has a version, an author, a reason and a validity range, and a closing ends the range and deletes nothing. Until a junction asked for it, nothing read that history back. A junction model states :history: true in its * Flags to get the surface below. It is opt-in, because a provider nothing wires into its component's registrar is dead code.

  • A link has two keys, so a provider cannot be read by one link's id. A junction's provider is read by the id of its parent side (the side that states :list_by:, the left when both or neither does) and answers every version of every link of that parent, closed links included. The facet adds the repository read read_all_by_<parent>, which returns a <junction>_history_row.
  • The history row holds the domain type beside closed_at, and the actor and the time flat, which is what the history builder reads. The domain type is final, so the row composes it. A link is closed by ending its validity, not by writing a version, so a version that ended because the link was closed carries closed_at, and a version that a later version replaced does not. The store records when a link ended and not who ended it, so a closing has no author.
  • The field mapper names the link by both keys in its first field, ID, so the versions of one link pair up in a stream that holds many. The provider numbers and diffs each link on its own, because a version number is a link's.
  • The provider needs the junction's own read code (<component>::<name>:read), or the code the model names with :history_read_permission:. A history holds every version of the rows its reads serve, so it must not be easier to read than they are.
  • A junction column is rendered by type. A type the table in core.py (_HISTORY_VALUE_EXPRESSIONS) does not name is refused when the model is generated, so a history never leaves a field out without saying so.

The per-component registrar.cpp still lists each provider by hand. Add the include and the call when a junction states :history: true.

Replaces the pattern of hand-wiring one ~10-line register_history_provider() lambda per entity directly into each component's registrar.cpp. That hand-maintained surface has already drifted: refdata's registrar registers only 53 of its 64 mapper-bearing entities. A small per-component aggregator (not generated; it mirrors ores::refdata::core::messaging::registrar) calls each generated per-entity function, the same way it already calls the CRUD register_<entity>_handlers() functions. refdata's registrar keeps its current 53 calls, so the registered key set is unchanged. The 11 mapper-bearing entities with no provider today stay unregistered, with identical runtime behaviour, until their own wiring work adds them.

3. Archetypes

Archetype Description
ores.cpp.history-provider-registrar.history_provider_registrar_header Per-entity history-provider registrar header declaring register_<entity>_history_provider().
ores.cpp.history-provider-registrar.history_provider_registrar_implementation Per-entity history-provider registrar implementation registering the provider body into the dispatch registry.

4. See also

Emacs 29.3 (Org mode 9.6.15)