The change Ramal.Resource installs on every create, update and destroy action.
Inside Ramal.run/2 it replaces "write this row to the data layer" with "put this row in the branch's overlay".
Everything else about the action still happens: argument casting, validations, policies, defaults, timestamps and
the action's own before_action and after_action hooks. Outside a branch it does nothing.
What the action returns is what the action would have returned: a created or updated record, or the destroyed one.
The write is also appended to the branch log so that Ramal.merge/2 can replay the action later.
How it works, confirmed against Ash 3.37 (lib/ash/changeset/changeset.ex and lib/ash/actions/*):
Ash.Changeset.around_action/3wraps the before-action hooks, the data-layer call and the after-action hooks. The around hook adds one morebefore_action, so that it is the last of them and sees the changeset as every other hook left it.- That hook builds the record with
Ash.Changeset.apply_attributes/2, which also applies defaults, and callsAsh.Changeset.set_result/2. The create, update and destroy actions skip the data layer when a result is set and carry on with relationship management and the after-action hooks. - An update runs
fully_atomic_changesetfirst and, when it succeeds, updates the row in the database without calling any hook.atomic/3therefore reports{:not_atomic, reason}while a branch is current, and the resource transformer setsrequire_atomic? falseso Ash falls back to the path above. - Bulk actions call the data layer directly and never look at the result, so they are refused. Upserts too: the branch cannot know whether the row exists in the base.
A failed action leaves the branch as it found it: the writes of the action and of the actions its hooks ran are
taken back. A write made by a hook of another captured action is logged with nested?: true, because replaying the
outer action runs the hook again.
Example
{:ok, branch} = Ramal.branch(MyApp.Farm)
Ramal.run!(branch, fn ->
herd = Ash.create!(MyApp.Farm.Herd, %{name: "R7", head: 40})
Ash.destroy!(herd)
end)
# The database was not written; the branch log holds the create and the destroy.