SvEx. Plugin. Apply
(SvEx v0.4.2)
Installs a derived plugin into a target project (SDD 7.3).
Apply never matches a project name — all matching happened at capture. It
renders <%= @app %> and resolves the bare APP path token, both from the
target's own triple, then writes each entry according to its ownership mode.
Applying is also RECORDING, when the target declares itself: see
SvEx.Plugin.Record, which reads back every file this module wrote and
writes the manifest a later update diffs.
Summary
Functions
Inserts dependencies into the target's mix.exs.
Appends a supervision child to the target's application.ex.
Places a :contributes block where the entry's at: option says.
Marks a :manual hunk that also REMOVES lines, always as a conflict region.
The opening marker line for key, which mix sv_ex.check looks for.
The SvEx.Template name triple for target_dir, read from its mix.exs.
Places a :manual hunk against its recorded anchor.
Sets each {name, commands} pair in the target's aliases/0.
Replaces one key of an existing config statement, per the entry's put:.
Sets each {key, value} pair in the target's project/0.
Applies the plugin at component_dir to target.
Writes a :sole_owner entry: the whole rendered file at the resolved path.
Functions
Inserts dependencies into the target's mix.exs.
Delegated to SvEx.Source.MixExs, which locates the list structurally. The
previous implementation matched defp deps do with a regex, wrote nothing for
the other three shapes mix accepts, and returned :ok — a plugin that
installed no dependencies and reported success. Dependencies are APPENDED
where the regex prepended; the old position was an artifact of matching the
opening bracket, not a decision.
Raises SvEx.Source.MixExs.Error when the list cannot be located. A plugin
that cannot install its dependencies must fail at install time, not hand back
a project that does not compile.
@spec add_supervision_child(Path.t(), Path.t(), binary(), SvEx.Template.names()) :: :ok
Appends a supervision child to the target's application.ex.
Placed rather than marked. Adding a list element rewrites the previous last
entry to gain a comma, which reads as a deletion and forced the whole file to
a conflict region on every install — see
SvEx.Source.ApplicationEx, which removes that cause.
Raises SvEx.Source.ApplicationEx.Error when the children list cannot be
located: a plugin whose process is never started did nothing, and saying
:ok for it is the silent-success shape this subsystem exists to remove.
@spec contribute(Path.t(), Path.t(), binary(), SvEx.Template.names(), keyword()) :: :ok
Places a :contributes block where the entry's at: option says.
at: is RECORDED by derive from where the hunk was observed, never inferred
here: apply cannot tell from a block of text whether it belongs above
import_config, inside a config_env() guard, or at the end — and guessing
is how a production secret becomes a test default.
Defaults to :append, so every plugin written before at: existed keeps
its exact behaviour.
Raises SvEx.Source.ConfigExs.Error when the named site is absent.
Marks a :manual hunk that also REMOVES lines, always as a conflict region.
Never anchored. place/6's insert path would write the added lines in and
leave the removed ones behind, which is the silent success this exists to
close — a file holding both the old content and the new, and :ok returned.
Idempotent on the REMOVED half rather than the added one. place/6 re-applies
as a no-op because its block survives verbatim; a human resolving a removal
necessarily deletes text, so that probe stops holding here, and a hunk that
only deletes has no added block to probe at all — a substring test against an
empty needle is always true, which would make the FIRST apply a no-op and
write nothing. Removed text already absent means the edit has been made.
The opening marker line for key, which mix sv_ex.check looks for.
@spec names(Path.t()) :: SvEx.Template.names()
The SvEx.Template name triple for target_dir, read from its mix.exs.
Read structurally rather than guessed from the directory name: a project's
directory and its app: need not agree, and 8.1 states name: is
independent of app:.
Places a :manual hunk against its recorded anchor.
Inserts immediately after the anchor when it occurs EXACTLY once. Anything else — absent, empty, or more than one match — falls back to a keyed conflict region at the end of the file, which will not compile until a human moves it.
Exactly-once is the whole safety argument. An anchor matching twice means
there is no way to tell which site the author meant, and inserting at the
first is a silent wrong answer; a marker is a loud one. mix sv_ex.check
fails while any marker remains, and D12 forbids resolving it with a prompt.
Sets each {name, commands} pair in the target's aliases/0.
Creates aliases/0 and wires it into project/0 when the project has
neither, which is every mix new project.
@spec put_config_key(Path.t(), Path.t(), binary(), SvEx.Template.names(), keyword()) :: :ok
Replaces one key of an existing config statement, per the entry's put:.
The key must already be there. Raises SvEx.Source.ConfigExs.Error otherwise:
an override that silently becomes an insert is a typo turned into a config
key nobody reads.
Sets each {key, value} pair in the target's project/0.
Applies the plugin at component_dir to target.
The target's own mix.exs supplies the name triple, so a plugin installs
into any project rather than only one recorded in priv/baselines.exs.
A target holding a target.exs also gets a plugin.exs recording what
was written; one without is installed and records nothing. See
SvEx.Plugin.Record.
@spec write_owned(Path.t(), Path.t(), binary(), SvEx.Template.names()) :: :ok
Writes a :sole_owner entry: the whole rendered file at the resolved path.
The plugin is the only author, so this overwrites without asking. Doing so for any other mode would be a first-order R6 violation.