AshDispatch.Helpers.RecordReader (AshDispatch v0.5.0)
View SourceDefensive field-reading helpers for post-action notifier hooks.
Why this exists
Ash actions can use narrowed select clauses to return only a subset
of attributes. The excluded attributes are populated as
%Ash.NotLoaded{} sentinels rather than raising or returning nil.
Notifier hooks (counter broadcasts, locale extraction, dispatch
context building) read fields off the action's resulting record to
decide what to do next. A bare Map.get(record, field) returns the
sentinel for narrowed fields, which then cascades into:
Ecto.Query.CastErrorwhen used in a query filterArgumentError "Actor field is not loaded"fromAsh.Query.relates_to_actor_viachecksProtocol.UndefinedError{protocol: String.Chars}when interpolated into a topic string or log message- Truthy-check bugs:
if Map.get(record, :foo)istruefor a sentinel because it's a struct, not nil/false
The contract
Notifier side effects are best-effort — they must degrade silently
when the record can't yield the field, not crash the request or
spam the log. These helpers treat %Ash.NotLoaded{} as nil so
callers' existing nil-checks make the right decision automatically.
Single debug log per call so production diagnostics are still available without polluting test output.
Summary
Functions
Returns true if the field is present on the record AND has been
loaded (i.e. is not the %Ash.NotLoaded{} sentinel).
Like Map.get/2 but treats %Ash.NotLoaded{} as nil.
safe_get/2 with a default returned when the field is nil or
%Ash.NotLoaded{}.
Functions
Returns true if the field is present on the record AND has been
loaded (i.e. is not the %Ash.NotLoaded{} sentinel).
Like Map.get/2 but treats %Ash.NotLoaded{} as nil.
Emits a single debug-level log line when the sentinel is observed, to aid diagnosing select-narrowed actions in production.
safe_get/2 with a default returned when the field is nil or
%Ash.NotLoaded{}.