Skip to content

Commit f8a00bc

Browse files
authored
chore: prepare for v0.5.0 release (#13)
* chore: prepare for v0.5.0 release * docs: update docs Also: - Added dialyzer CI Job * test: full test coverage * test: minor test tweak * feat: raise if invalid pipeline is defined
1 parent 8f7aec5 commit f8a00bc

File tree

9 files changed

+191
-18
lines changed

9 files changed

+191
-18
lines changed

.github/workflows/ci.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,26 @@ jobs:
4141
run: mix deps.get
4242
- name: Run formatter
4343
run: mix format --check-formatted
44+
dialyzer:
45+
name: Dialyzer
46+
runs-on: ubuntu-latest
47+
steps:
48+
- uses: actions/checkout@v3
49+
- name: Set up Elixir
50+
uses: erlef/setup-beam@v1
51+
with:
52+
elixir-version: "1.17.2"
53+
otp-version: "27.0.1"
54+
- name: Restore dependencies cache
55+
uses: actions/cache@v3
56+
with:
57+
path: deps
58+
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
59+
restore-keys: ${{ runner.os }}-mix-
60+
- name: Install dependencies
61+
run: mix deps.get
62+
- name: Run dialyzer
63+
run: mix dialyzer
4464
test:
4565
name: Test
4666
runs-on: ubuntu-latest

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,11 @@ erl_crash.dump
2222
# Ignore package tarball (built via "mix hex.build").
2323
commandex-*.tar
2424

25+
# Ignore dialyzer files.
26+
/priv/plts/*.plt
27+
/priv/plts/*.plt.hash
28+
29+
# Misc
30+
.DS_Store
2531
.iex.exs
2632
*.swp

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## Unreleased
8+
## [0.5.0] - 2024-09-08
99

1010
### Added
1111

12-
- New `run/0` function for commands that don't define any parameters.
12+
- `run/0` function for commands that don't define any parameters.
13+
14+
### Changed
15+
16+
- Raise `ArgumentError` if an invalid `pipeline` is defined.
1317

1418
## [0.4.1] - 2020-06-26
1519

lib/commandex.ex

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ defmodule Commandex do
22
@moduledoc """
33
Defines a command struct.
44
5-
Commandex structs are a loose implementation of the command pattern, making it easy
5+
Commandex is a loose implementation of the command pattern, making it easy
66
to wrap parameters, data, and errors into a well-defined struct.
77
88
## Example
@@ -70,7 +70,7 @@ defmodule Commandex do
7070
or map with atom/string keys.
7171
7272
`&run/1` takes a command struct and runs it through the pipeline functions defined
73-
in the command. Functions are executed *in the order in which they are defined*.
73+
in the command. **Functions are executed in the order in which they are defined**.
7474
If a command passes through all pipelines without calling `halt/1`, `:success`
7575
will be set to `true`. Otherwise, subsequent pipelines after the `halt/1` will
7676
be ignored and `:success` will be set to `false`.
@@ -88,6 +88,21 @@ defmodule Commandex do
8888
%{success: false, errors: _error} ->
8989
# I'm a lazy programmer that writes catch-all error handling
9090
end
91+
92+
## Parameter-less Commands
93+
94+
If a command does not have any parameters defined, a `run/0` will be generated
95+
automatically. Useful for diagnostic jobs and internal tasks.
96+
97+
iex> GenerateReport.run()
98+
%GenerateReport{
99+
pipelines: [:fetch_data, :calculate_results],
100+
data: %{total_valid: 183220, total_invalid: 781215},
101+
params: %{},
102+
halted: false,
103+
errors: %{},
104+
success: true
105+
}
91106
"""
92107

93108
@typedoc """
@@ -418,10 +433,30 @@ defmodule Commandex do
418433
Module.put_attribute(mod, :data, {name, nil})
419434
end
420435

421-
def __pipeline__(mod, name) do
436+
def __pipeline__(mod, name) when is_atom(name) do
422437
Module.put_attribute(mod, :pipelines, name)
423438
end
424439

440+
def __pipeline__(mod, fun) when is_function(fun, 1) do
441+
Module.put_attribute(mod, :pipelines, fun)
442+
end
443+
444+
def __pipeline__(mod, fun) when is_function(fun, 3) do
445+
Module.put_attribute(mod, :pipelines, fun)
446+
end
447+
448+
def __pipeline__(mod, {m, f}) do
449+
Module.put_attribute(mod, :pipelines, {m, f})
450+
end
451+
452+
def __pipeline__(mod, {m, f, a}) do
453+
Module.put_attribute(mod, :pipelines, {m, f, a})
454+
end
455+
456+
def __pipeline__(_mod, name) do
457+
raise ArgumentError, "pipeline #{inspect(name)} is not valid"
458+
end
459+
425460
defp get_param(params, key, default) do
426461
case Map.get(params, key) do
427462
nil ->

mix.exs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,42 @@ defmodule Commandex.MixProject do
22
use Mix.Project
33

44
@source_url "https://github.com/codedge-llc/commandex"
5-
@version "0.4.1"
5+
@version "0.5.0"
66

77
def project do
88
[
99
app: :commandex,
1010
deps: deps(),
11+
dialyzer: dialyzer(),
1112
docs: docs(),
1213
elixir: "~> 1.9",
1314
elixirc_paths: elixirc_paths(Mix.env()),
1415
name: "Commandex",
1516
package: package(),
1617
source_url: "https://github.com/codedge-llc/commandex",
1718
start_permanent: Mix.env() == :prod,
19+
test_coverage: test_coverage(),
1820
version: @version
1921
]
2022
end
2123

2224
defp deps do
2325
[
2426
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
27+
{:dialyxir, "~> 1.0", only: [:dev], runtime: false},
2528
{:ex_doc, "~> 0.31", only: :dev}
2629
]
2730
end
2831

2932
defp elixirc_paths(:test), do: ["lib", "test/support"]
3033
defp elixirc_paths(_), do: ["lib"]
3134

35+
defp dialyzer do
36+
[
37+
plt_file: {:no_warn, "priv/plts/dialyzer.plt"}
38+
]
39+
end
40+
3241
defp docs do
3342
[
3443
extras: [
@@ -57,6 +66,16 @@ defmodule Commandex.MixProject do
5766
]
5867
end
5968

69+
defp test_coverage do
70+
[
71+
ignore_modules: [
72+
GenerateReport,
73+
RegisterUser
74+
],
75+
summary: [threshold: 70]
76+
]
77+
end
78+
6079
def application do
6180
[
6281
extra_applications: [:logger]

mix.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
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"},
44
"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"},
56
"earmark": {:hex, :earmark, "1.4.4", "4821b8d05cda507189d51f2caeef370cf1e18ca5d7dfb7d31e9cafe6688106a4", [:mix], [], "hexpm", "1f93aba7340574847c0f609da787f0d79efcab51b044bb6e242cae5aca9d264d"},
67
"earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"},
8+
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
79
"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"},
810
"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"},
911
"file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},

test/commandex_test.exs

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
defmodule CommandexTest do
22
use ExUnit.Case
33
doctest Commandex
4-
alias Commandex.RegisterUser
54

65
76
@password "test1234"
@@ -58,18 +57,105 @@ defmodule CommandexTest do
5857
end
5958
end
6059

60+
describe "param/2 macro" do
61+
test "raises if duplicate defined" do
62+
assert_raise ArgumentError, fn ->
63+
defmodule ExampleParamInvalid do
64+
import Commandex
65+
66+
command do
67+
param :key_1
68+
param :key_2
69+
param :key_1
70+
end
71+
end
72+
end
73+
end
74+
end
75+
76+
describe "data/1 macro" do
77+
test "raises if duplicate defined" do
78+
assert_raise ArgumentError, fn ->
79+
defmodule ExampleDataInvalid do
80+
import Commandex
81+
82+
command do
83+
data :key_1
84+
data :key_2
85+
data :key_1
86+
end
87+
end
88+
end
89+
end
90+
end
91+
92+
describe "pipeline/1 macro" do
93+
test "accepts valid pipeline arguments" do
94+
try do
95+
defmodule ExamplePipelineValid do
96+
import Commandex
97+
98+
command do
99+
pipeline :example
100+
pipeline {ExamplePipelineValid, :example}
101+
pipeline {ExamplePipelineValid, :example_args, ["test"]}
102+
pipeline &ExamplePipelineValid.example_single/1
103+
pipeline &ExamplePipelineValid.example/3
104+
end
105+
106+
def example(command, _params, _data) do
107+
command
108+
end
109+
110+
def example_single(command) do
111+
command
112+
end
113+
114+
def example_args(command, _params, _data, _custom_value) do
115+
command
116+
end
117+
end
118+
119+
ExamplePipelineValid.run()
120+
rescue
121+
FunctionClauseError -> flunk("Should not raise.")
122+
end
123+
end
124+
125+
test "raises if invalid argument defined" do
126+
assert_raise ArgumentError, fn ->
127+
defmodule ExamplePipelineInvalid do
128+
import Commandex
129+
130+
command do
131+
pipeline 1234
132+
end
133+
end
134+
end
135+
end
136+
end
137+
138+
describe "halt/1" do
139+
test "ignores remaining pipelines" do
140+
command = RegisterUser.run(%{agree_tos: false})
141+
142+
refute command.success
143+
assert command.errors === %{tos: :not_accepted}
144+
end
145+
end
146+
61147
describe "run/0" do
62148
test "is defined if no params are defined" do
63-
assert Kernel.function_exported?(Commandex.GenerateReport, :run, 0)
149+
assert Kernel.function_exported?(GenerateReport, :run, 0)
64150

65-
command = Commandex.GenerateReport.run()
151+
command = GenerateReport.run()
66152
assert command.success
67153
assert command.data.total_valid > 0
68154
assert command.data.total_invalid > 0
69155
end
70156

71157
test "is not defined if params are defined" do
72-
refute Kernel.function_exported?(Commandex.RegisterUser, :run, 0)
158+
refute Kernel.function_exported?(RegisterUser, :run, 0)
73159
end
74160
end
75161

test/support/generate_report.ex

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
defmodule Commandex.GenerateReport do
1+
defmodule GenerateReport do
22
@moduledoc """
33
Example command that generates fake data.
44
@@ -11,17 +11,18 @@ defmodule Commandex.GenerateReport do
1111
data :total_valid
1212
data :total_invalid
1313

14-
pipeline :calculate_valid
15-
pipeline :calculate_invalid
14+
pipeline :fetch_data
15+
pipeline :calculate_results
1616
end
1717

18-
def calculate_valid(command, _params, _data) do
18+
def fetch_data(command, _params, _data) do
19+
# Not real.
1920
command
20-
|> put_data(:total_valid, :rand.uniform(1_000_000))
2121
end
2222

23-
def calculate_invalid(command, _params, _data) do
23+
def calculate_results(command, _params, _data) do
2424
command
25-
|> put_data(:total_invalid, :rand.uniform(1_000_000))
25+
|> put_data(:total_valid, 183_220)
26+
|> put_data(:total_invalid, 781_215)
2627
end
2728
end

test/support/register_user.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
defmodule Commandex.RegisterUser do
1+
defmodule RegisterUser do
22
@moduledoc """
33
Example command that registers a user.
44
"""

0 commit comments

Comments
 (0)