Skip to content

Commit 8b578a2

Browse files
Mache ZeitraumProtokoll derivable
Mit dem `@derive` Attribut kann eine Implementierung abgeleitet werden, indem struct Keys fuer den Zeitruam oder start und ende als Option annotiert werden. Co-Authored-By Joern Lang <joern.lang@studitemps.de>
1 parent 59811db commit 8b578a2

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed

lib/zeitraum_protokoll.ex

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,30 @@
11
defprotocol Shared.ZeitraumProtokoll do
22
@spec als_intervall(t) :: Timex.Interval.t()
33
def als_intervall(zeitraum)
4+
5+
@impl true
6+
defmacro __deriving__(module, options) do
7+
if zeitraum_key = Keyword.get(options, :zeitraum) do
8+
quote do
9+
defimpl Shared.ZeitraumProtokoll, for: unquote(module) do
10+
def als_intervall(zeitraum), do: Map.fetch!(zeitraum, unquote(zeitraum_key))
11+
end
12+
end
13+
else
14+
start_key = Keyword.get(options, :start, :start)
15+
end_key = Keyword.get(options, :ende, :ende)
16+
17+
quote do
18+
defimpl Shared.ZeitraumProtokoll, for: unquote(module) do
19+
def als_intervall(zeitraum) do
20+
start = Map.fetch!(zeitraum, unquote(start_key))
21+
ende = Map.fetch!(zeitraum, unquote(end_key))
22+
Shared.Zeitperiode.new(start, ende)
23+
end
24+
end
25+
end
26+
end
27+
end
428
end
529

630
defimpl Shared.ZeitraumProtokoll, for: Shared.Month do

lib/zeitraum_protokoll_test.exs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
defmodule Shared.ZeitraumProtokollTest do
2+
use ExUnit.Case, async: true
3+
4+
alias Shared.ZeitraumProtokoll
5+
6+
describe "derive with default options (start and ende keys)" do
7+
defmodule DefaultKeys do
8+
@derive ZeitraumProtokoll
9+
defstruct [:start, :ende]
10+
end
11+
12+
test "converts struct with default start/ende keys to interval" do
13+
start_date = ~D[2024-01-01]
14+
end_date = ~D[2024-01-31]
15+
16+
struct = %DefaultKeys{start: start_date, ende: end_date}
17+
interval = ZeitraumProtokoll.als_intervall(struct)
18+
19+
assert interval.from == ~N[2024-01-01 00:00:00]
20+
assert interval.until == ~N[2024-02-01 00:00:00]
21+
end
22+
end
23+
24+
describe "derive with both custom start and ende keys" do
25+
defmodule CustomKeys do
26+
@derive {ZeitraumProtokoll, start: :from_date, ende: :to_date}
27+
defstruct [:from_date, :to_date]
28+
end
29+
30+
test "converts struct with both custom keys to interval" do
31+
start_date = ~D[2024-04-01]
32+
end_date = ~D[2024-04-30]
33+
34+
struct = %CustomKeys{from_date: start_date, to_date: end_date}
35+
interval = ZeitraumProtokoll.als_intervall(struct)
36+
37+
assert interval.from == ~N[2024-04-01 00:00:00]
38+
assert interval.until == ~N[2024-05-01 00:00:00]
39+
end
40+
end
41+
42+
describe "derive with zeitraum option" do
43+
defmodule WithZeitraumKey do
44+
@derive {ZeitraumProtokoll, zeitraum: :interval}
45+
defstruct [:interval, :other_field]
46+
end
47+
48+
test "fetches interval directly from zeitraum key" do
49+
interval = Shared.Zeitperiode.new(~D[2024-05-01], ~D[2024-05-31])
50+
struct = %WithZeitraumKey{interval: interval, other_field: "test"}
51+
52+
result = ZeitraumProtokoll.als_intervall(struct)
53+
54+
assert result == interval
55+
assert result.from == ~N[2024-05-01 00:00:00]
56+
assert result.until == ~N[2024-06-01 00:00:00]
57+
end
58+
59+
test "returns interval as-is without creating a new one" do
60+
interval = Shared.Zeitperiode.new(~D[2024-06-01], ~D[2024-06-30])
61+
struct = %WithZeitraumKey{interval: interval, other_field: "test"}
62+
63+
result = ZeitraumProtokoll.als_intervall(struct)
64+
65+
# Should be the exact same interval, not a new one
66+
assert result == interval
67+
end
68+
end
69+
70+
describe "error handling" do
71+
defmodule MissingDefaultKeys do
72+
@derive ZeitraumProtokoll
73+
defstruct [:other_field]
74+
end
75+
76+
test "raises when required default keys are missing" do
77+
struct = %MissingDefaultKeys{other_field: "value"}
78+
79+
assert_raise KeyError, fn ->
80+
ZeitraumProtokoll.als_intervall(struct)
81+
end
82+
end
83+
84+
defmodule MissingCustomKeys do
85+
@derive {ZeitraumProtokoll, start: :custom_start}
86+
defstruct [:other_field]
87+
end
88+
89+
test "raises when required custom keys are missing" do
90+
struct = %MissingCustomKeys{other_field: "value"}
91+
92+
assert_raise KeyError, fn ->
93+
ZeitraumProtokoll.als_intervall(struct)
94+
end
95+
end
96+
97+
defmodule MissingZeitraumKey do
98+
@derive {ZeitraumProtokoll, zeitraum: :missing_key}
99+
defstruct [:other_field]
100+
end
101+
102+
test "raises when zeitraum key is missing" do
103+
struct = %MissingZeitraumKey{other_field: "value"}
104+
105+
assert_raise KeyError, fn ->
106+
ZeitraumProtokoll.als_intervall(struct)
107+
end
108+
end
109+
end
110+
end

mix.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ defmodule JehovakelExTimes.MixProject do
88
elixir: "~> 1.14",
99
elixirc_paths: elixirc_paths(Mix.env()),
1010
start_permanent: Mix.env() == :prod,
11+
consolidate_protocols: Mix.env() != :test,
1112
test_paths: ["lib"],
1213
test_coverage: [tool: ExCoveralls],
1314
deps: deps(),

0 commit comments

Comments
 (0)