Ash.Temporal (ash v3.34.5)

Copy Markdown View Source

Resolves as_of into the point of time or period that the data layer stores.

On a temporal resource every version of a record is valid for a period of datetimes. You can provide a DateTime, or :now.

# a read resolves as_of a point in time
Ash.Temporal.resolve_read_as_of(query.as_of)

# a write resolves a period beginning at the point,
# and extending forever unless a later write closes it
{:ok, instant} = Ash.Temporal.write_instant(resource, :now)
{:ok, period} = Ash.Temporal.write_period(resource, :now)

The write functions return a DateTime cast to the precision the resource's period declares.

A write that provides no as_of takes effect now. Ash resolves it once for the whole write, before the data layer is called, so a data layer receives a temporal write's as_of as an instant or a range, never nil or :now.

Summary

Types

An as_of as a caller may give it, before it is resolved.

Functions

Casts the as_of of a write to a temporal resource into the type its periods are built from, applying the precision that type declares.

Checks the as_of of a write to a temporal resource, casting it as cast_write_as_of/2 does.

Resolves the as_of a read answers at.

Resolves the as_of a write takes effect at.

Whether a change, validation or preparation module declares itself safe to run on a temporal resource, for the given options.

Resolves the point where a write first takes effect.

Resolves the period a write is valid for.

Types

as_of()

@type as_of() :: :now | term() | nil

An as_of as a caller may give it, before it is resolved.

Functions

cast_write_as_of(resource, as_of)

@spec cast_write_as_of(Ash.Resource.t(), as_of()) :: term()

Casts the as_of of a write to a temporal resource into the type its periods are built from, applying the precision that type declares.

An instant or :now becomes an instant (see write_instant/2) and a range becomes a period (see write_period/2). Anything that can't be cast, and any as_of of a resource that isn't temporal, is returned unchanged.

check_write_as_of(resource, as_of)

@spec check_write_as_of(Ash.Resource.t(), as_of()) ::
  {:ok, term()} | {:error, Exception.t()}

Checks the as_of of a write to a temporal resource, casting it as cast_write_as_of/2 does.

An instant must cast to the type the resource's periods are built from. A range must cast to the resource's period, and satisfy its constraints. Returns the cast as_of, or an Ash.Error.Changes.InvalidAsOf saying why it is refused. Any as_of of a resource that isn't temporal is returned unchanged.

resolve_read_as_of(as_of)

@spec resolve_read_as_of(as_of()) :: DateTime.t() | nil

Resolves the as_of a read answers at.

:now resolves to the current time, and a DateTime is that instant. nil means no particular time was provided. Anything else, such as a range or a Date, raises Ash.Error.Query.AsOfNotAnInstant.

resolve_write_as_of(other)

@spec resolve_write_as_of(as_of()) :: term() | nil

Resolves the as_of a write takes effect at.

:now resolves to the current time. A range resolves to its lower bound. Anything else is returned unchanged. nil means no particular time was provided.

temporal_safe?(module, opts)

@spec temporal_safe?(module(), Keyword.t()) :: boolean()

Whether a change, validation or preparation module declares itself safe to run on a temporal resource, for the given options.

Every action on a temporal resource runs "as of" a point in time, so anything that runs as part of one must not assume it is happening now. A module declares that it meets that bar with the temporal_safe?/1 callback of its behaviour (Ash.Resource.Change.temporal_safe?/1, Ash.Resource.Validation.temporal_safe?/1, Ash.Resource.Preparation.temporal_safe?/1). A module that does not define it is not temporal safe.

Modules from packages that don't declare it yet can be listed as temporal safe in config:

config :ash, :temporal_safe_modules, [SomePackage.Changes.DoesThing]

write_instant(resource, as_of)

@spec write_instant(Ash.Resource.t(), as_of()) :: {:ok, DateTime.t()} | :error

Resolves the point where a write first takes effect.

The value comes back as a DateTime, cast to whatever precision the resource's period declares.

write_period(resource, as_of)

@spec write_period(Ash.Resource.t(), as_of()) :: {:ok, Ash.Range.t()} | :error

Resolves the period a write is valid for.

The value comes back cast to the resource's period. It begins where the write takes effect and extends forever unless a later write closes it.