Skip to content

Commit b8c1488

Browse files
authored
feat: :success option for halt/2 (#16)
1 parent ae9502b commit b8c1488

File tree

8 files changed

+71
-35
lines changed

8 files changed

+71
-35
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ jobs:
2929
- name: Set up Elixir
3030
uses: erlef/setup-beam@v1
3131
with:
32-
elixir-version: "1.17.2"
33-
otp-version: "27.0.1"
32+
elixir-version: "1.19.4"
33+
otp-version: "28.2"
3434
- name: Restore dependencies cache
3535
uses: actions/cache@v3
3636
with:
@@ -49,8 +49,8 @@ jobs:
4949
- name: Set up Elixir
5050
uses: erlef/setup-beam@v1
5151
with:
52-
elixir-version: "1.17.2"
53-
otp-version: "27.0.1"
52+
elixir-version: "1.19.4"
53+
otp-version: "28.2"
5454
- name: Restore dependencies cache
5555
uses: actions/cache@v3
5656
with:
@@ -69,8 +69,8 @@ jobs:
6969
- name: Set up Elixir
7070
uses: erlef/setup-beam@v1
7171
with:
72-
elixir-version: "1.17.2"
73-
otp-version: "27.0.1"
72+
elixir-version: "1.19.4"
73+
otp-version: "28.2"
7474
- name: Restore dependencies cache
7575
uses: actions/cache@v3
7676
with:

.tool-versions

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
elixir 1.17.2-otp-27
2-
erlang 27.0.1
1+
elixir 1.19.4-otp-28
2+
erlang 28.2

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2020-2024 Codedge LLC (https://www.codedge.io/)
3+
Copyright (c) 2020-2025 Codedge LLC (https://www.codedge.io/)
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,6 @@ Git commit subjects use the [Karma style](http://karma-runner.github.io/5.0/dev/
144144

145145
## License
146146

147-
Copyright (c) 2020-2024 Codedge LLC (https://www.codedge.io/)
147+
Copyright (c) 2020-2025 Codedge LLC (https://www.codedge.io/)
148148

149149
This library is MIT licensed. See the [LICENSE](https://github.com/codedge-llc/commandex/blob/main/LICENSE) for details.

lib/commandex.ex

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,12 @@ defmodule Commandex do
195195
`true` if the command was not halted after running all of the pipelines.
196196
"""
197197
@type t :: %__MODULE__{
198-
data: map,
199-
errors: map,
200-
halted: boolean,
201-
params: map,
198+
data: map(),
199+
errors: map(),
200+
halted: boolean(),
201+
params: map(),
202202
pipelines: [Commandex.pipeline()],
203-
success: boolean
203+
success: boolean()
204204
}
205205

206206
@doc """
@@ -217,8 +217,7 @@ defmodule Commandex do
217217
"""
218218
@spec run :: t
219219
def run do
220-
new()
221-
|> run()
220+
new() |> run()
222221
end
223222
end
224223

@@ -241,9 +240,7 @@ defmodule Commandex do
241240
end
242241

243242
def run(params) do
244-
params
245-
|> new()
246-
|> run()
243+
params |> new() |> run()
247244
end
248245
end
249246

@@ -364,17 +361,32 @@ defmodule Commandex do
364361
@doc """
365362
Halts a command pipeline.
366363
367-
Any pipelines defined after the halt will be ignored. If a command finishes running through
368-
all pipelines, `:success` will be set to `true`.
364+
Any pipelines defined after the halt will be ignored. By default, if a command finishes
365+
running through all pipelines, `:success` will be set to `true`.
369366
370367
def hash_password(command, %{password: nil} = _params, _data) do
371368
command
372369
|> put_error(:password, :not_supplied)
373370
|> halt()
374371
end
372+
373+
### Halting Early Successfully
374+
375+
Pass `success: true` as an optional argument if you would like to mark the command
376+
as successful, even if there are still pipelines left to be run. Useful for returning
377+
early from no-op commands.
378+
379+
def needs_update?(command, _params, _data) do
380+
command
381+
|> put_error(:update, :no_op)
382+
|> halt(success: true)
383+
end
375384
"""
376-
@spec halt(command) :: command
377-
def halt(command), do: %{command | halted: true}
385+
@spec halt(command(), Keyword.t()) :: command()
386+
def halt(command, opts \\ []) do
387+
success = Keyword.get(opts, :success, false)
388+
%{command | halted: true, success: success}
389+
end
378390

379391
@doc false
380392
def maybe_mark_successful(%{halted: false} = command), do: %{command | success: true}

mix.lock

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
%{
22
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
33
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
4-
"credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"},
5-
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
4+
"credo": {:hex, :credo, "1.7.14", "c7e75216cea8d978ba8c60ed9dede4cc79a1c99a266c34b3600dd2c33b96bc92", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "12a97d6bb98c277e4fb1dff45aaf5c137287416009d214fb46e68147bd9e0203"},
5+
"dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"},
66
"earmark": {:hex, :earmark, "1.4.4", "4821b8d05cda507189d51f2caeef370cf1e18ca5d7dfb7d31e9cafe6688106a4", [:mix], [], "hexpm", "1f93aba7340574847c0f609da787f0d79efcab51b044bb6e242cae5aca9d264d"},
7-
"earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"},
8-
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
9-
"ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"},
7+
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
8+
"erlex": {:hex, :erlex, "0.2.8", "cd8116f20f3c0afe376d1e8d1f0ae2452337729f68be016ea544a72f767d9c12", [:mix], [], "hexpm", "9d66ff9fedf69e49dc3fd12831e12a8a37b76f8651dd21cd45fcf5561a8a7590"},
9+
"ex_doc": {:hex, :ex_doc, "0.39.3", "519c6bc7e84a2918b737aec7ef48b96aa4698342927d080437f61395d361dcee", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "0590955cf7ad3b625780ee1c1ea627c28a78948c6c0a9b0322bd976a079996e1"},
1010
"excoveralls": {:hex, :excoveralls, "0.12.1", "a553c59f6850d0aff3770e4729515762ba7c8e41eedde03208182a8dc9d0ce07", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
11-
"file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},
11+
"file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"},
1212
"hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
1313
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
1414
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
15-
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
16-
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
17-
"makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"},
15+
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
16+
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
17+
"makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"},
1818
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
1919
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
20-
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
20+
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
2121
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
2222
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"},
2323
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},

test/commandex_test.exs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,20 @@ defmodule CommandexTest do
135135
end
136136
end
137137

138-
describe "halt/1" do
138+
describe "halt/2" do
139139
test "ignores remaining pipelines" do
140140
command = RegisterUser.run(%{agree_tos: false})
141141

142142
refute command.success
143143
assert command.errors === %{tos: :not_accepted}
144144
end
145+
146+
test "handles :success option" do
147+
command = RegisterUser.run(%{email: "[email protected]"})
148+
149+
assert command.success
150+
assert command.errors === %{user: :already_exists}
151+
end
145152
end
146153

147154
describe "run/0" do

test/support/register_user.ex

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,27 @@ defmodule RegisterUser do
1313
data :user
1414
data :auth
1515

16+
pipeline :check_already_registered
1617
pipeline :verify_tos
1718
pipeline :create_user
1819
pipeline :record_auth_attempt
1920
pipeline &IO.inspect/1
2021
end
2122

23+
@spec check_already_registered(t(), map(), map()) :: t()
24+
def check_already_registered(command, %{email: email}, _data) do
25+
case email do
26+
27+
command
28+
|> put_error(:user, :already_exists)
29+
|> halt(success: true)
30+
31+
_other ->
32+
command
33+
end
34+
end
35+
36+
@spec verify_tos(t(), map(), map()) :: t()
2237
def verify_tos(command, %{agree_tos: true} = _params, _data) do
2338
command
2439
end
@@ -29,6 +44,7 @@ defmodule RegisterUser do
2944
|> halt()
3045
end
3146

47+
@spec create_user(t(), map(), map()) :: t()
3248
def create_user(command, %{password: nil} = _params, _data) do
3349
command
3450
|> put_error(:password, :not_given)
@@ -39,6 +55,7 @@ defmodule RegisterUser do
3955
put_data(command, :user, %{email: email})
4056
end
4157

58+
@spec record_auth_attempt(t(), map(), map()) :: t()
4259
def record_auth_attempt(command, _params, _data) do
4360
put_data(command, :auth, true)
4461
end

0 commit comments

Comments
 (0)