Skip to content

Commit 64dd27b

Browse files
committed
support passing a function to Writer.transaction/3
which applies the transaction and returns the txid
1 parent 3aa820d commit 64dd27b

File tree

2 files changed

+81
-3
lines changed

2 files changed

+81
-3
lines changed

lib/phoenix/sync/writer.ex

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,17 +1160,28 @@ defmodule Phoenix.Sync.Writer do
11601160

11611161
@txn_name {:__phoenix_sync__, :txn}
11621162
@txid_name {:__phoenix_sync__, :txid}
1163+
@txid_query "SELECT txid_current() as txid"
11631164

11641165
defp start_multi(txn) do
11651166
Ecto.Multi.new()
1167+
|> txid_step()
11661168
|> Ecto.Multi.put(@txn_name, txn)
1167-
|> Ecto.Multi.run(@txid_name, fn repo, _ ->
1168-
with {:ok, %{rows: [[txid]]}} <- repo.query("SELECT txid_current() as txid") do
1169+
end
1170+
1171+
defp txid_step(multi \\ Ecto.Multi.new()) do
1172+
Ecto.Multi.run(multi, @txid_name, fn repo, _ ->
1173+
with {:ok, %{rows: [[txid]]}} <- repo.query(@txid_query) do
11691174
{:ok, txid}
11701175
end
11711176
end)
11721177
end
11731178

1179+
defp has_txid_step?(multi) do
1180+
multi
1181+
|> Ecto.Multi.to_list()
1182+
|> Enum.any?(fn {name, _} -> name == @txid_name end)
1183+
end
1184+
11741185
defp apply_change(multi, %Operation{} = op, %__MODULE__{} = writer) do
11751186
with {:ok, actions} <- mutation_actions(op, writer),
11761187
{:ok, action} <- Map.fetch(actions, op.operation) do
@@ -1455,18 +1466,53 @@ defmodule Phoenix.Sync.Writer do
14551466
end)
14561467
Plug.Conn.send_resp(conn, 400, Jason.encode!(error))
14571468
end
1469+
1470+
Also supports normal fun/0 or fun/1 style transactions much like
1471+
`Ecto.Repo.transaction/2`, returning the txid of the operation:
1472+
1473+
{:ok, txid, todo} =
1474+
Phoenix.Sync.Writer.transaction(fn ->
1475+
Repo.insert!(changeset)
1476+
end, Repo)
14581477
"""
14591478
@spec transaction(Ecto.Multi.t(), Ecto.Repo.t(), keyword()) ::
14601479
{:ok, txid(), Ecto.Multi.changes()} | Ecto.Multi.failure()
14611480
def transaction(multi, repo, opts \\ [])
14621481

14631482
def transaction(%Ecto.Multi{} = multi, repo, opts) when is_atom(repo) do
1464-
with {:ok, changes} <- repo.transaction(multi, opts) do
1483+
wrapped_multi =
1484+
if has_txid_step?(multi) do
1485+
multi
1486+
else
1487+
Ecto.Multi.prepend(multi, txid_step())
1488+
end
1489+
1490+
with {:ok, changes} <- repo.transaction(wrapped_multi, opts) do
14651491
{txid, changes} = Map.pop!(changes, @txid_name)
14661492
{:ok, txid, changes}
14671493
end
14681494
end
14691495

1496+
def transaction(fun, repo, opts) when is_function(fun) and is_atom(repo) do
1497+
with {:ok, {txid, result}} <-
1498+
repo.transaction(
1499+
fn ->
1500+
%{rows: [[txid]]} = repo.query!(@txid_query)
1501+
1502+
result =
1503+
case fun do
1504+
fun0 when is_function(fun0, 0) -> fun.()
1505+
fun1 when is_function(fun1, 1) -> fun1.(repo)
1506+
end
1507+
1508+
{txid, result}
1509+
end,
1510+
opts
1511+
) do
1512+
{:ok, txid, result}
1513+
end
1514+
end
1515+
14701516
@doc """
14711517
Extract the transaction id from changes returned from `Repo.transaction`.
14721518

test/phoenix/sync/writer_test.exs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,38 @@ defmodule Phoenix.Sync.WriterTest do
929929
end
930930
end
931931

932+
describe "transaction/3" do
933+
setup [:with_repo, :with_todos]
934+
935+
test "supports a function and returns the {:ok, txid, result} tuple" do
936+
changeset =
937+
Ecto.Changeset.change(%Support.Todo{}, %{id: 1111, title: "my todo", completed: false})
938+
939+
assert {:ok, txid, %Support.Todo{title: "my todo"}} =
940+
Writer.transaction(
941+
fn repo ->
942+
assert repo == Repo
943+
repo.insert!(changeset)
944+
end,
945+
Repo
946+
)
947+
948+
assert is_integer(txid)
949+
end
950+
951+
test "supports any Ecto.Multi argument and returns the txid" do
952+
changeset =
953+
Ecto.Changeset.change(%Support.Todo{}, %{id: 1111, title: "my todo", completed: false})
954+
955+
multi = Ecto.Multi.insert(Ecto.Multi.new(), :todo, changeset)
956+
957+
assert {:ok, txid, %{todo: %Support.Todo{title: "my todo"}}} =
958+
Writer.transaction(multi, Repo)
959+
960+
assert is_integer(txid)
961+
end
962+
end
963+
932964
def parse_transaction(m) when is_list(m) do
933965
with {:ok, operations} <-
934966
Writer.Transaction.parse_operations(m, fn op ->

0 commit comments

Comments
 (0)