-
Notifications
You must be signed in to change notification settings - Fork 8
/
event.ex
81 lines (66 loc) · 2.92 KB
/
event.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
defmodule Ecspanse.Event do
@moduledoc """
Events act as a one-way communication channel to Ecspanse,
enabling elements outside of the framework to dispatch data asynchronously into Ecspanse Systems.
Events are also used internally to communicate between Systems.
The events are defined by invoking `use Ecspanse.Event` in their module definition.
Events are scheduled using the `Ecspanse.event/2` function.
Any events scheduled within the current frame will be batched and
then made accessible to the systems in the following `t:Ecspanse.Frame.t/0`.
The batched events from the current frame are cleared once that frame ends.
This implies each system has a single opportunity to process an event that has been scheduled.
## Options
- `:fields` - a list with all the event struct keys and their initial values (if any)
For example: `[:direction, type: :hero]`
An `inserted_at` field with the `Elixir.System` time of the creation is added to all events automatically.
There are two ways of providing the events with their field values:
1. At compile time, when invoking the `use Ecspanse.Event`, by providing the `:fields` option.
```elixir
defmodule Demo.Events.HeroMoved do
use Ecspanse.Event, fields: [:direction, type: :hero]
end
```
2. At runtime when creating the events from specs: `t:Ecspanse.Event.event_spec()`
```elixir
Ecspanse.event({Demo.Events.HeroMoved, [direction: :left]})
```
> #### Note {: .info}
> There are many ways to filter events in the Systems by their struct like:
> ```elixir
> Enum.filter(events, &match?(%Demo.Events.MoveHero{direction: :right}, &1)) # this allows further pattern matching in the event struct
> # or
> Enum.filter(events, & &1.__struct__ == Demo.Events.MoveHero)
> # or
> Enum.filter(events, & fn %event_module{} -> event_module == Demo.Events.MoveHero end)
> ```
"""
@typedoc """
An `event_spec` is the definition required to create an event.
## Examples
```elixir
Demo.Events.MoveHero
{Demo.Events.MoveHero, [direction: :left]}
```
"""
@type event_spec ::
(event_module :: module())
| {event_module :: module(), event_fields :: keyword()}
defmacro __using__(opts) do
quote bind_quoted: [opts: opts], location: :keep do
Module.register_attribute(__MODULE__, :ecs_type, accumulate: false)
Module.put_attribute(__MODULE__, :ecs_type, :event)
fields = Keyword.get(opts, :fields, [])
unless is_list(fields) do
raise ArgumentError,
"Invalid fields for Event: #{Kernel.inspect(__MODULE__)}. The `:fields` option must be a list with all the Event struct keys and their default values (if any). Eg: [:foo, :bar, baz: 1]"
end
fields = Keyword.put_new(fields, :inserted_at, nil)
@enforce_keys [:inserted_at]
defstruct fields
@doc false
def __ecs_type__ do
@ecs_type
end
end
end
end