AshSandbox.RegistryTemplate (AshSandbox v0.1.2)

Copy Markdown View Source

The sandbox registry resource, as a template the host owns (012 T025, FR-009).

defmodule MyApp.SandboxRegistry do
  use AshSandbox.RegistryTemplate,
    data_layer: AshPostgres.DataLayer,
    domain: MyApp.Sandboxes,
    repo: MyApp.Repo,
    table: "sandboxes"
end

The host declares data layer, repo, table, and domain; this library owns the attributes, actions, and semantics.

Why a template and not a resource module

⚠️ Research R5's spike falsified the obvious alternative — the library declaring the resource and the host attaching a data layer afterward.

Ash.Resource.Info.data_layer/1 delegates to Extension.get_persisted(resource, :data_layer). The data layer is written into the resource module's beam when the library compiles. With no declaration it is Ash.DataLayer.Simple — the no-persistence default — and it stays that way permanently. The host can add the resource to its own domain, and the whole thing compiles cleanly and exits 0, and nothing can ever be stored.

That is the same failure shape as a boundary violation: compiles, exits 0, fails later. The alternative is recorded here because it looks right and nothing in the build will tell a future contributor otherwise.

__using__/1's options are public interface

Because the host writes this call site, its options are public API under FR-014, and changing them is a breaking change under FR-015.

Required options

  • :data_layer — the host's data layer
  • :domain — the host's Ash domain
  • :repo — required by AshPostgres.DataLayer; ignored by data layers that do not use one
  • :table — the host's table name

What this library does not declare

No data layer, no repo, no domain, no table, and no multitenancy strategy. The last is deliberate: FR-003 forbids requiring any particular multi-tenancy model, and a library that declared strategy: :context would force one on every host.

Summary

Functions

The states in which a sandbox is live — it exists as far as its mechanism is concerned, whether or not it is serving traffic yet.

The states in which a sandbox is settled — nothing is running under the rules it was launched with.

Functions

live_states()

@spec live_states() :: [atom()]

The states in which a sandbox is live — it exists as far as its mechanism is concerned, whether or not it is serving traffic yet.

⚠️ Enumerated, never expressed as "not stopped" (029 T018 ruling). state below is a closed set precisely so that a state added later has to be classified deliberately: a negation would silently sort a new state into settled, and the one caller of this list refuses a write while a sandbox is live. Axonn.Sandbox.EnvironmentAllowlistUpdateTest's partition test fails if the two lists here stop covering the constraint exactly. (It lives in the host because AshSandbox.EnvironmentTemplate cannot be exercised on this library's own ETS fixtures — see that module.)

:stopping is live. A sandbox being torn down still has whatever its mechanism installed at launch, and it can still reach the network until the teardown completes.

settled_states()

@spec settled_states() :: [atom()]

The states in which a sandbox is settled — nothing is running under the rules it was launched with.

The complement of live_states/0 over state's closed set, written out rather than derived, so that both halves have to be edited when the set grows.