PhoenixKit. Templates. Overrides
(phoenix_kit_templates v0.1.0)
View Source
Locates a host application's override file for one part of one template.
A host customizes a message by dropping a file into its own repo, where it is version-controlled and reviewable, instead of editing a database row through an admin UI. Nothing here writes; an override is only ever read.
Layout
<root>/<name>/<part>.<locale>.<ext>
<root>/<name>/<part>.<ext>subject and text are .txt; html is .html. A root is typically
Application.app_dir(:my_app, "priv/phoenix_kit_templates"), but this module
takes roots as an argument and reads no configuration of its own — it must not
know which application is using it.
Lookup runs most- to least-specific, and stops at the first file that exists:
text.en-GB.txt → text.en.txt → text.txtso a host that only cares about one language writes text.txt and is done,
while one that translates its overrides gets dialect precision. Roots are
tried in order, so an earlier root shadows a later one.
Runtime, not compile time
Overrides live in the host application, which is compiled separately from
this package — there is no point in this package's compilation at which they
could be read. So they are read at runtime and cached in :persistent_term,
including the absence of a file, since a missing override is the common case
and would otherwise cost a File.stat on every send. Files cannot change
without a deploy; reset_cache/0 exists for tests and dev reloads.
Path safety
name and locale are matched against strict patterns before they are ever
joined onto a root. They are literals at every current call site, but this
module turns a name into a filesystem read, and that is not a boundary to
leave to the caller's good behaviour — ../../../etc/passwd resolves to no
override rather than to a file.
Summary
Functions
The parts an override file can supply.
The contents of the best-matching override file, or nil when there is none.
Drops cached override lookups.
Types
Functions
@spec parts() :: [part()]
The parts an override file can supply.
The contents of the best-matching override file, or nil when there is none.
locale may be nil, which skips straight to the locale-less candidate.
@spec reset_cache([Path.t()] | :all) :: :ok
Drops cached override lookups.
Only tests and dev reloads need this: a deploy starts a fresh VM.
Pass a list of roots to clear only the entries that consulted them. That
scoping is what lets an async test clear its own tmp_dir without erasing a
concurrently-running one's cache — and it is the honest shape anyway, since a
dev reload usually means one application's files changed, not all of them.