Diffo.Provider.Calculations.InheritedCharacteristic (Diffo v0.8.0)

Copy Markdown View Source

Backing calculation for inherited_characteristic DSL declarations.

Walks the graph along a normalised via: hop chain (assignment and/or relationship edges, in either direction — see Diffo.Provider.Calculations.Traversal), reads the typed characteristic at the declared read role on each reached instance, optionally renames it (as) and collapses the result to one end (collapse).

Injected automatically by TransformInheritedRefs — do not reference this module directly; use the inherited_characteristic DSL entity inside characteristics do.

Cross-world resolution

Unlike inherited_place / inherited_party (which query universal PlaceRef / PartyRef), the typed characteristic module varies per reached resource. This calc resolves it at runtime via AshNeo4j.worlds/1 on each struct and Diffo.Provider.Extension.Info.provider_characteristics/1 on its outermost resource. Late-binding by design — the reached resource may not exist at the consumer's compile time.

Result shape

Without collapse, a list per input record — one entry per reached instance. Each entry is:

  • A BaseCharacteristic-derived record (or a list of such records when the reached resource's characteristic declaration is {:array, M}).
  • %Diffo.Unknown{} when the instance can't be projected to a loadable resource module, or its module declares no characteristic at the read role.

With collapse: :first | :last, a single such entry or nil (empty result).

When as is set, each BaseCharacteristic record is renamed to that name (both the loaded value and, via surfacing, the encoded TMF entry); %Diffo.Unknown{} sentinels are left untouched.

Reason vocabulary (local to this world)

  • :no_concrete_worldAshNeo4j.worlds/1 returned []; the reached struct has no labels resolvable to a loaded AshNeo4j.DataLayer resource. :context carries %{source_id: id}.
  • :role_not_declared — the outermost world's resource exists but its provider_characteristics/1 has no entry for the read role. :context carries %{source_id: id, resource: module, role: atom}.

Per the Cross-domain lookups AGENTS.md section, reasons are world-local — consumers in other worlds should treat them as opaque and (if composing) wrap via :inner_unknown.

Summary

Functions

describe(opts)

Callback implementation for Ash.Resource.Calculation.describe/1.

has_calculate?()

has_expression?()

Callback implementation for Ash.Resource.Calculation.has_expression?/0.

init(opts)

Callback implementation for Ash.Resource.Calculation.init/1.

strict_loads?()

Callback implementation for Ash.Resource.Calculation.strict_loads?/0.