Skip to content

Commit e1204c0

Browse files
authored
chore: various wardening (#18)
* chore: various wardening * test: add more tests Also: pass credo
1 parent 96bc9a7 commit e1204c0

8 files changed

Lines changed: 63 additions & 52 deletions

File tree

.github/workflows/ci.yml

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
runs-on: ubuntu-latest
1313
steps:
1414
- name: Checkout
15-
uses: actions/checkout@v3
15+
uses: actions/checkout@v4
1616
- name: Use Node.js 18.x
1717
uses: actions/setup-node@v3
1818
with:
@@ -22,15 +22,15 @@ jobs:
2222
- name: Run Prettier
2323
run: prettier --check --no-error-on-unmatched-pattern "**/*.{json,md,yml,yaml}"
2424
check:
25-
name: Format
25+
name: Format/Credo
2626
runs-on: ubuntu-latest
2727
steps:
28-
- uses: actions/checkout@v3
28+
- uses: actions/checkout@v4
2929
- name: Set up Elixir
3030
uses: erlef/setup-beam@v1
3131
with:
32-
elixir-version: "1.19.4"
33-
otp-version: "28.2"
32+
elixir-version: "1.19"
33+
otp-version: "28"
3434
- name: Restore dependencies cache
3535
uses: actions/cache@v3
3636
with:
@@ -41,22 +41,30 @@ jobs:
4141
run: mix deps.get
4242
- name: Run formatter
4343
run: mix format --check-formatted
44+
- name: Run Credo
45+
run: mix credo
4446
dialyzer:
4547
name: Dialyzer
4648
runs-on: ubuntu-latest
4749
steps:
48-
- uses: actions/checkout@v3
50+
- uses: actions/checkout@v4
4951
- name: Set up Elixir
5052
uses: erlef/setup-beam@v1
5153
with:
52-
elixir-version: "1.19.4"
53-
otp-version: "28.2"
54+
elixir-version: "1.19"
55+
otp-version: "28"
5456
- name: Restore dependencies cache
5557
uses: actions/cache@v3
5658
with:
5759
path: deps
5860
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
5961
restore-keys: ${{ runner.os }}-mix-
62+
- name: Restore dialyzer cache
63+
uses: actions/cache@v3
64+
with:
65+
path: priv/plts
66+
key: ${{ runner.os }}-mix-plts-${{ hashFiles('./priv/plts/') }}
67+
restore-keys: ${{ runner.os }}-mix-plts-
6068
- name: Install dependencies
6169
run: mix deps.get
6270
- name: Run dialyzer
@@ -65,12 +73,12 @@ jobs:
6573
name: Test
6674
runs-on: ubuntu-latest
6775
steps:
68-
- uses: actions/checkout@v3
76+
- uses: actions/checkout@v4
6977
- name: Set up Elixir
7078
uses: erlef/setup-beam@v1
7179
with:
72-
elixir-version: "1.19.4"
73-
otp-version: "28.2"
80+
elixir-version: "1.19"
81+
otp-version: "28"
7482
- name: Restore dependencies cache
7583
uses: actions/cache@v3
7684
with:

.travis.yml

Lines changed: 0 additions & 9 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
All notable changes to this project will be documented in this file.
44

5-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [0.5.2] - 2025-12-21

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-2025 Codedge LLC (https://www.codedge.io/)
3+
Copyright (c) 2020-2026 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-2025 Codedge LLC (https://www.codedge.io/)
147+
Copyright (c) 2020-2026 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: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ defmodule Commandex do
7171
7272
`&run/1` takes a command struct and runs it through the pipeline functions defined
7373
in the command. **Functions are executed in the order in which they are defined**.
74-
If a command passes through all pipelines without calling `halt/1`, `:success`
75-
will be set to `true`. Otherwise, subsequent pipelines after the `halt/1` will
74+
If a command passes through all pipelines without calling `halt/1`, `:success`
75+
will be set to `true`. Otherwise, subsequent pipelines after the `halt/1` will
7676
be ignored and `:success` will be set to `false`.
7777
7878
%{email: "example@example.com", password: "asdf1234"}
@@ -85,7 +85,7 @@ defmodule Commandex do
8585
%{success: false, errors: %{password: :not_given}} ->
8686
# Respond with a 400 or something
8787
88-
%{success: false, errors: _error} ->
88+
%{success: false, errors: _errors} ->
8989
# I'm a lazy programmer that writes catch-all error handling
9090
end
9191
@@ -112,17 +112,17 @@ defmodule Commandex do
112112
113113
- `pipeline :do_work` - Name of a function inside the command's module, arity three.
114114
- `pipeline {YourModule, :do_work}` - Arity three.
115-
- `pipeline {YourModule, :do_work, [:additonal, "args"]}` - Arity three plus the
115+
- `pipeline {YourModule, :do_work, [:additional, "args"]}` - Arity three plus the
116116
number of additional args given.
117117
- `pipeline &YourModule.do_work/1` - Or any anonymous function of arity one.
118118
- `pipeline &YourModule.do_work/3` - Or any anonymous function of arity three.
119119
"""
120120
@type pipeline ::
121-
atom
122-
| {module, atom}
123-
| {module, atom, [any]}
124-
| (command :: struct -> command :: struct)
125-
| (command :: struct, params :: map, data :: map -> command :: struct)
121+
atom()
122+
| {module(), atom()}
123+
| {module(), atom(), [any()]}
124+
| (command :: struct() -> command :: struct())
125+
| (command :: struct(), params :: map(), data :: map() -> command :: struct())
126126

127127
@typedoc """
128128
Command struct.
@@ -138,19 +138,20 @@ defmodule Commandex do
138138
`true` if the command was not halted after running all of the pipelines.
139139
"""
140140
@type command :: %{
141-
__struct__: atom,
142-
data: map,
143-
errors: map,
144-
halted: boolean,
145-
params: map,
141+
__struct__: atom(),
142+
data: map(),
143+
errors: map(),
144+
halted: boolean(),
145+
params: map(),
146146
pipelines: [pipeline()],
147-
success: boolean
147+
success: boolean()
148148
}
149149

150150
@doc """
151151
Defines a command struct with params, data, and pipelines.
152152
"""
153-
@spec command(do: any) :: no_return
153+
@spec command(do: any()) :: no_return()
154+
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
154155
defmacro command(do: block) do
155156
prelude =
156157
quote do
@@ -206,7 +207,7 @@ defmodule Commandex do
206207
@doc """
207208
Creates a new struct from given parameters.
208209
"""
209-
@spec new(map | Keyword.t()) :: t
210+
@spec new(map() | Keyword.t()) :: t()
210211
def new(opts \\ []) do
211212
Commandex.parse_params(%__MODULE__{}, opts)
212213
end
@@ -215,7 +216,7 @@ defmodule Commandex do
215216
@doc """
216217
Runs given pipelines in order and returns command struct.
217218
"""
218-
@spec run :: t
219+
@spec run() :: t()
219220
def run do
220221
new() |> run()
221222
end
@@ -227,7 +228,7 @@ defmodule Commandex do
227228
`run/1` can either take parameters that would be passed to `new/1`
228229
or the command struct itself.
229230
"""
230-
@spec run(map | Keyword.t() | t) :: t
231+
@spec run(map() | Keyword.t() | t()) :: t()
231232
def run(%unquote(__MODULE__){pipelines: pipelines} = command) do
232233
pipelines
233234
|> Enum.reduce_while(command, fn fun, acc ->
@@ -263,7 +264,7 @@ defmodule Commandex do
263264
# ...pipelines
264265
end
265266
"""
266-
@spec param(atom, Keyword.t()) :: no_return
267+
@spec param(atom(), Keyword.t()) :: no_return()
267268
defmacro param(name, opts \\ []) do
268269
quote do
269270
Commandex.__param__(__MODULE__, unquote(name), unquote(opts))
@@ -284,7 +285,7 @@ defmodule Commandex do
284285
# ...pipelines
285286
end
286287
"""
287-
@spec data(atom) :: no_return
288+
@spec data(atom()) :: no_return()
288289
defmacro data(name) do
289290
quote do
290291
Commandex.__data__(__MODULE__, unquote(name))
@@ -297,7 +298,7 @@ defmodule Commandex do
297298
Pipelines are functions executed against the command, *in the order in which they are defined*.
298299
299300
For example, two pipelines could be defined:
300-
301+
301302
pipeline :check_valid_email
302303
pipeline :create_user
303304
@@ -311,12 +312,12 @@ defmodule Commandex do
311312
312313
- `pipeline :do_work` - Name of a function inside the command's module, arity three.
313314
- `pipeline {YourModule, :do_work}` - Arity three.
314-
- `pipeline {YourModule, :do_work, [:additonal, "args"]}` - Arity three plus the
315+
- `pipeline {YourModule, :do_work, [:additional, "args"]}` - Arity three plus the
315316
number of additional args given.
316317
- `pipeline &YourModule.do_work/1` - Or any anonymous function of arity one.
317318
- `pipeline &YourModule.do_work/3` - Or any anonymous function of arity three.
318319
"""
319-
@spec pipeline(atom) :: no_return
320+
@spec pipeline(atom()) :: no_return()
320321
defmacro pipeline(name) do
321322
quote do
322323
Commandex.__pipeline__(__MODULE__, unquote(name))
@@ -330,14 +331,14 @@ defmodule Commandex do
330331
331332
data :password_hash
332333
333-
Set the password pash in one of your pipeline functions:
334+
Set the password hash in one of your pipeline functions:
334335
335336
def hash_password(command, %{password: password} = _params, _data) do
336337
# Better than plaintext, I guess
337338
put_data(command, :password_hash, Base.encode64(password))
338339
end
339340
"""
340-
@spec put_data(command, atom, any) :: command
341+
@spec put_data(command(), atom(), any()) :: command()
341342
def put_data(%{data: data} = command, key, val) do
342343
%{command | data: Map.put(data, key, val)}
343344
end
@@ -353,15 +354,15 @@ defmodule Commandex do
353354
|> halt()
354355
end
355356
"""
356-
@spec put_error(command, any, any) :: command
357+
@spec put_error(command(), any(), any()) :: command()
357358
def put_error(%{errors: error} = command, key, val) do
358359
%{command | errors: Map.put(error, key, val)}
359360
end
360361

361362
@doc """
362363
Halts a command pipeline.
363364
364-
Any pipelines defined after the halt will be ignored. By default, if a command finishes
365+
Any pipelines defined after the halt will be ignored. By default, if a command finishes
365366
running through all pipelines, `:success` will be set to `true`.
366367
367368
def hash_password(command, %{password: nil} = _params, _data) do
@@ -389,10 +390,12 @@ defmodule Commandex do
389390
end
390391

391392
@doc false
393+
@spec maybe_mark_successful(command()) :: command()
392394
def maybe_mark_successful(%{halted: false} = command), do: %{command | success: true}
393395
def maybe_mark_successful(command), do: command
394396

395397
@doc false
398+
@spec parse_params(command(), map() | Keyword.t()) :: command()
396399
def parse_params(%{params: p} = struct, params) when is_list(params) do
397400
params = for {key, _} <- p, into: %{}, do: {key, Keyword.get(params, key, p[key])}
398401
%{struct | params: params}
@@ -404,6 +407,7 @@ defmodule Commandex do
404407
end
405408

406409
@doc false
410+
@spec apply_fun(command(), pipeline()) :: command()
407411
def apply_fun(%mod{params: params, data: data} = command, name) when is_atom(name) do
408412
:erlang.apply(mod, name, [command, params, data])
409413
end
@@ -424,6 +428,8 @@ defmodule Commandex do
424428
:erlang.apply(m, f, [command, params, data] ++ a)
425429
end
426430

431+
@doc false
432+
@spec __param__(module(), atom(), Keyword.t()) :: :ok
427433
def __param__(mod, name, opts) do
428434
params = Module.get_attribute(mod, :params)
429435

@@ -435,6 +441,8 @@ defmodule Commandex do
435441
Module.put_attribute(mod, :params, {name, default})
436442
end
437443

444+
@doc false
445+
@spec __data__(module(), atom()) :: :ok
438446
def __data__(mod, name) do
439447
data = Module.get_attribute(mod, :data)
440448

@@ -445,6 +453,8 @@ defmodule Commandex do
445453
Module.put_attribute(mod, :data, {name, nil})
446454
end
447455

456+
@doc false
457+
@spec __pipeline__(module(), pipeline()) :: :ok
448458
def __pipeline__(mod, name) when is_atom(name) do
449459
Module.put_attribute(mod, :pipelines, name)
450460
end
@@ -469,6 +479,7 @@ defmodule Commandex do
469479
raise ArgumentError, "pipeline #{inspect(name)} is not valid"
470480
end
471481

482+
@spec get_param(map(), atom(), term()) :: term()
472483
defp get_param(params, key, default) do
473484
case Map.get(params, key) do
474485
nil ->

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ defmodule Commandex.MixProject do
1414
elixirc_paths: elixirc_paths(Mix.env()),
1515
name: "Commandex",
1616
package: package(),
17-
source_url: "https://github.com/codedge-llc/commandex",
17+
source_url: @source_url,
1818
start_permanent: Mix.env() == :prod,
1919
test_coverage: test_coverage(),
2020
version: @version

test/support/register_user.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ defmodule RegisterUser do
1717
pipeline :verify_tos
1818
pipeline :create_user
1919
pipeline :record_auth_attempt
20+
# credo:disable-for-next-line Credo.Check.Warning.IoInspect
2021
pipeline &IO.inspect/1
2122
end
2223

0 commit comments

Comments
 (0)