decider 0.10.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of decider might be problematic. Click here for more details.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +14 -0
- data/Rakefile +52 -6
- data/VERSION +1 -0
- data/bin/8ball +29 -0
- data/bin/decider +5 -0
- data/lib/decider.rb +5 -390
- data/test/decider_test.rb +12 -0
- data/test/test_helper.rb +10 -0
- metadata +57 -97
- checksums.yaml +0 -7
- data/.standard.yml +0 -3
- data/CHANGELOG.md +0 -381
- data/LICENSE.txt +0 -21
- data/README.md +0 -152
- data/Steepfile +0 -26
- data/examples/decide.md +0 -180
- data/examples/evolve.md +0 -150
- data/examples/infra.md +0 -15
- data/examples/state.md +0 -55
- data/lib/decider/event_sourcing.rb +0 -25
- data/lib/decider/in_memory.rb +0 -26
- data/lib/decider/reactor.rb +0 -142
- data/lib/decider/state.rb +0 -25
- data/lib/decider/version.rb +0 -5
- data/lib/decider/view.rb +0 -169
- data/mise.toml +0 -17
- data/sig/decider.rbs +0 -16
data/README.md
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
# Decider
|
|
2
|
-
|
|
3
|
-
This gem provides simple DSL for building Functional Event Sourcing Decider in Ruby. To learn more about the pattern read the original [article by Jérémie Chassaing](https://thinkbeforecoding.com/post/2021/12/17/functional-event-sourcing-decider).
|
|
4
|
-
|
|
5
|
-
Special credits for [Ismael Celis for inspiration](https://ismaelcelis.com/posts/decide-evolve-react-pattern-in-ruby/).
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
gem install decider
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
or add to Gemfile
|
|
14
|
-
|
|
15
|
-
```ruby
|
|
16
|
-
gem "decider"
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Usage
|
|
20
|
-
|
|
21
|
-
```ruby
|
|
22
|
-
require "decider"
|
|
23
|
-
|
|
24
|
-
State = Data.define(:value) do
|
|
25
|
-
def max?
|
|
26
|
-
value >= MAX
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def min?
|
|
30
|
-
value <= MIN
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
module Commands
|
|
35
|
-
Increase = Data.define
|
|
36
|
-
Decrease = Data.define
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
module Events
|
|
40
|
-
ValueIncreased = Data.define
|
|
41
|
-
ValueDecreased = Data.define
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
MIN = 0
|
|
45
|
-
MAX = 100
|
|
46
|
-
|
|
47
|
-
ValueDecider = Decider.define do
|
|
48
|
-
# define intial state
|
|
49
|
-
initial_state State.new(value: 0)
|
|
50
|
-
|
|
51
|
-
# decide command with state
|
|
52
|
-
decide Commands::Increase do
|
|
53
|
-
# return collection of events
|
|
54
|
-
if state.max?
|
|
55
|
-
[]
|
|
56
|
-
else
|
|
57
|
-
[Events::ValueIncreased.new]
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
decide Commands::Decrease do
|
|
62
|
-
if state.min?
|
|
63
|
-
[]
|
|
64
|
-
else
|
|
65
|
-
[Events::ValueDecreased.new]
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# evolve state with events
|
|
70
|
-
evolve Events::ValueIncreased do
|
|
71
|
-
# return new state
|
|
72
|
-
state.with(value: state.value + 1)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
evolve Events::ValueDecreased do
|
|
76
|
-
# state is immutable Data object
|
|
77
|
-
state.with(value: state.value - 1)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
terminal? do
|
|
81
|
-
state <= 0
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
state = ValueDecider.initial_state
|
|
86
|
-
events = ValueDecider.decide(Commands::Increase.new, state)
|
|
87
|
-
new_state = events.reduce(state) { |state, event| ValueDecider.evolve(state, events)
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
You can also compose deciders:
|
|
91
|
-
|
|
92
|
-
```ruby
|
|
93
|
-
Left = Data.define(:value)
|
|
94
|
-
Right = Data.define(:value)
|
|
95
|
-
|
|
96
|
-
left = Decider.define do
|
|
97
|
-
initial_state Left.new(value: 0)
|
|
98
|
-
|
|
99
|
-
decide Commands::LeftCommand do
|
|
100
|
-
[Events::LeftEvent.new(value: command.value)]
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
evolve Events::LeftEvent do
|
|
104
|
-
state.with(value: state.value + 1)
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
terminal? do
|
|
108
|
-
state <= 0
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
right = Decider.define do
|
|
113
|
-
initial_state Right.new(value: 0)
|
|
114
|
-
|
|
115
|
-
decide Commands::RightCommand do
|
|
116
|
-
[Events::RightEvent.new(value: command.value)]
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
evolve Events::RightEvent do
|
|
120
|
-
state.with(value: state.value + 1)
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
terminal? do
|
|
124
|
-
state <= 0
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
Composition = Decider.compose(left, right)
|
|
129
|
-
|
|
130
|
-
state = Composition.initial_state
|
|
131
|
-
#> #<data Decider::Pair left=#<data Left value=0>, right=#<data Right value=0>>
|
|
132
|
-
|
|
133
|
-
events = Composition.decide(Decider::Left.new(Commands::LeftCommand.new(value: 1)), state)
|
|
134
|
-
#> [#<Decider::Left value=#<data value=1>]
|
|
135
|
-
|
|
136
|
-
state = events.reduce(state, &Composition.method(:evolve))
|
|
137
|
-
#> #<data Decider::Pair left=#<data value=1>, right=#<data value=0>>
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
## Development
|
|
141
|
-
|
|
142
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
143
|
-
|
|
144
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
145
|
-
|
|
146
|
-
## Contributing
|
|
147
|
-
|
|
148
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/decider.
|
|
149
|
-
|
|
150
|
-
## License
|
|
151
|
-
|
|
152
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Steepfile
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
D = Steep::Diagnostic
|
|
2
|
-
|
|
3
|
-
target :lib do
|
|
4
|
-
signature "sig"
|
|
5
|
-
|
|
6
|
-
check "lib"
|
|
7
|
-
check "Gemfile"
|
|
8
|
-
|
|
9
|
-
# library "pathname" # Standard libraries
|
|
10
|
-
# library "strong_json" # Gems
|
|
11
|
-
|
|
12
|
-
# configure_code_diagnostics(D::Ruby.strict) # `strict` diagnostics setting
|
|
13
|
-
configure_code_diagnostics(D::Ruby.lenient) # `lenient` diagnostics setting
|
|
14
|
-
# configure_code_diagnostics(D::Ruby.silent) # `silent` diagnostics setting
|
|
15
|
-
# configure_code_diagnostics do |hash| # You can setup everything yourself
|
|
16
|
-
# hash[D::Ruby::NoMethod] = :information
|
|
17
|
-
# end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# target :test do
|
|
21
|
-
# signature "sig", "sig-private"
|
|
22
|
-
|
|
23
|
-
# check "test"
|
|
24
|
-
|
|
25
|
-
# # library "pathname" # Standard libraries
|
|
26
|
-
# end
|
data/examples/decide.md
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
# Decide
|
|
2
|
-
|
|
3
|
-
## Match by command
|
|
4
|
-
|
|
5
|
-
```ruby
|
|
6
|
-
Command = Data.define(:value)
|
|
7
|
-
State = Data.define(:value)
|
|
8
|
-
|
|
9
|
-
decider = Decider.define do
|
|
10
|
-
initial_state State.new(value: 5)
|
|
11
|
-
|
|
12
|
-
decide Command do
|
|
13
|
-
# emit events
|
|
14
|
-
emit Event.new(
|
|
15
|
-
value: command.value + state.value
|
|
16
|
-
)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
decider.decide Command.new(value: 10), decider.initial_state
|
|
21
|
-
#> [#<data Event value=15>]
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Match by command and state
|
|
25
|
-
|
|
26
|
-
```ruby
|
|
27
|
-
decider = Decider.define do
|
|
28
|
-
initial_state :turned_off
|
|
29
|
-
|
|
30
|
-
decide :turn_on, :turned_off do
|
|
31
|
-
emit :turned_on
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
decide :turn_off, :turned_on do
|
|
35
|
-
emit :turned_off
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
decider.decide :turn_on, decider.initial_state
|
|
40
|
-
#> [:turned_on]
|
|
41
|
-
decider.decide :turn_off, :turned_on
|
|
42
|
-
#> [:turned_off]
|
|
43
|
-
decider.decide :turn_on, :turned_on
|
|
44
|
-
#> []
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Match by pattern matching
|
|
48
|
-
|
|
49
|
-
State = Data.define(:status)
|
|
50
|
-
TurnOn = Data.define
|
|
51
|
-
TurnOff = Data.define
|
|
52
|
-
|
|
53
|
-
```ruby
|
|
54
|
-
decider = Decider.define do
|
|
55
|
-
initial_state State.new(status: :turned_off)
|
|
56
|
-
|
|
57
|
-
decide proc { [command, state] in [TurnOn, State(status: :turned_off)] } do
|
|
58
|
-
emit :turned_on
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
decide proc { [command, state] in [TurnOff, State(status: :turned_on)] } do
|
|
62
|
-
emit :turned_off
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
decider.decide :turn_on, decider.initial_state
|
|
67
|
-
#> [:turned_on]
|
|
68
|
-
decider.decide :turn_off, :turned_on
|
|
69
|
-
#> [:turned_off]
|
|
70
|
-
decider.decide :turn_on, :turned_on
|
|
71
|
-
#> []
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Handling unknown commands
|
|
75
|
-
|
|
76
|
-
```ruby
|
|
77
|
-
decider = Decider.define do
|
|
78
|
-
initial_state :initial
|
|
79
|
-
end
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Return empty list of events (nothing changed) by default:
|
|
83
|
-
|
|
84
|
-
```ruby
|
|
85
|
-
decider.decide :unknown, decider.initial_state
|
|
86
|
-
#> []
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
If you want to raise error, define a catch-all as last one:
|
|
90
|
-
|
|
91
|
-
```ruby
|
|
92
|
-
decider = Decider.define do
|
|
93
|
-
initial_state :initial
|
|
94
|
-
|
|
95
|
-
decide proc { true } do
|
|
96
|
-
raise ArgumentError, "Unknown command #{command}"
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
decider.decide :unknown, decider.initial_state
|
|
100
|
-
#> Unknown command unknown (ArgumentError)
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Commands
|
|
104
|
-
|
|
105
|
-
Commands can be primitives like symbols:
|
|
106
|
-
|
|
107
|
-
```ruby
|
|
108
|
-
decider = Decider.define do
|
|
109
|
-
initial_state :initial
|
|
110
|
-
|
|
111
|
-
decide proc { [command, state] in [:start, :initial | :stopped] } do
|
|
112
|
-
emit :started
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
decide proc { [command, state] in [:stop, :started] } do
|
|
116
|
-
emit :stopped
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
decider.decide :start, decider.initial_state
|
|
121
|
-
#> [:started]
|
|
122
|
-
decider.decide :stop, :started
|
|
123
|
-
#> [:stopped]
|
|
124
|
-
decider.decider :start, :started
|
|
125
|
-
#> []
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Or any classes like [Dry::Struct](https://dry-rb.org/gems/dry-struct/) or [Data](https://rubyapi.org/3.3/o/data):
|
|
129
|
-
|
|
130
|
-
```ruby
|
|
131
|
-
Start = Data.define(:value)
|
|
132
|
-
Stop = Data.define
|
|
133
|
-
|
|
134
|
-
decider = Decider.define do
|
|
135
|
-
initial_state :initial
|
|
136
|
-
|
|
137
|
-
decide proc { [command, state] in [Start, :initial | :stopped] } do
|
|
138
|
-
emit [:started, command.value]
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
decide proc { [command, state] in [Stop, :started] } do
|
|
142
|
-
emit :stopped
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
decider.decide Start.new(value: 10), decider.initial_state
|
|
147
|
-
#> [[:started, 10]]
|
|
148
|
-
decider.decide Stop.new, [:started, 10]
|
|
149
|
-
#> [:stopped]
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## Emitting Events
|
|
153
|
-
|
|
154
|
-
Decide can emit 0, 1 or more events:
|
|
155
|
-
|
|
156
|
-
```ruby
|
|
157
|
-
decider = Decider.define do
|
|
158
|
-
initial_state :initial
|
|
159
|
-
|
|
160
|
-
decide :none do
|
|
161
|
-
# noop
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
decide :one do
|
|
165
|
-
emit :event
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
decide :multiple do
|
|
169
|
-
emit :one
|
|
170
|
-
emit :two
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
decider.decide :none, decider.initial_state
|
|
175
|
-
#> []
|
|
176
|
-
decider.decide :one, decider.initial_state
|
|
177
|
-
#> [:event]
|
|
178
|
-
decider.decide :multiple, decider.initial_state
|
|
179
|
-
#> [:one, :two]
|
|
180
|
-
```
|
data/examples/evolve.md
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
# Evolve
|
|
2
|
-
|
|
3
|
-
## Match by event
|
|
4
|
-
|
|
5
|
-
```ruby
|
|
6
|
-
Event = Data.define(:value)
|
|
7
|
-
|
|
8
|
-
decider = Decider.define do
|
|
9
|
-
initial_state :initial
|
|
10
|
-
|
|
11
|
-
evolve Event do
|
|
12
|
-
event.value
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
decider.evolve decider.initial_state, Event.new(value: :changed)
|
|
17
|
-
#> :changed
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Match by state and event
|
|
21
|
-
|
|
22
|
-
```ruby
|
|
23
|
-
decider = Decider.define do
|
|
24
|
-
initial_state :turned_off
|
|
25
|
-
|
|
26
|
-
evolve :turned_on, :turned_off do
|
|
27
|
-
event
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
evolve :turned_off, :turned_on do
|
|
31
|
-
event
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
decider.evolve decider.initial_state, :turned_on
|
|
36
|
-
#> :turned_on
|
|
37
|
-
decider.evolve :turned_on, :turned_off
|
|
38
|
-
#> :turned_off
|
|
39
|
-
decider.evolve :turned_on, :unknown
|
|
40
|
-
#> :turned_on
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Match by pattern matching
|
|
44
|
-
|
|
45
|
-
```ruby
|
|
46
|
-
State = Data.define(:value)
|
|
47
|
-
Event = Data.define(:value)
|
|
48
|
-
|
|
49
|
-
decider = Decider.define do
|
|
50
|
-
initial_state State.new(value: :turned_off)
|
|
51
|
-
|
|
52
|
-
evolve proc { [state, event] in [State(value: :turned_off), Event(value: :turned_on)] } do
|
|
53
|
-
state.with(value: event.value)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
decider.evolve decider.initial_state, Event.new(value: :turned_on)
|
|
58
|
-
#> #<data State value=:turned_on>
|
|
59
|
-
decider.evolve decider.initial_state, :unknown
|
|
60
|
-
#> #<data State value=:turned_off>
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Handling unknown events
|
|
64
|
-
|
|
65
|
-
```ruby
|
|
66
|
-
decider = Decider.define do
|
|
67
|
-
initial_state :initial
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
decider.decide decider.initial_state, :initial
|
|
71
|
-
#> :initial
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
If you want to raise error, define a catch-all as last one:
|
|
75
|
-
|
|
76
|
-
```ruby
|
|
77
|
-
decider = Decider.define do
|
|
78
|
-
initial_state :initial
|
|
79
|
-
|
|
80
|
-
evolve proc { true } do
|
|
81
|
-
raise ArgumentError, "Unknown event #{event}"
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
decider.evolve decider.initial_state, :unknown
|
|
85
|
-
#> Unknown event unknown (ArgumentError)
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Events
|
|
89
|
-
|
|
90
|
-
Events can be primitives like symbols:
|
|
91
|
-
|
|
92
|
-
```ruby
|
|
93
|
-
decider = Decider.define do
|
|
94
|
-
initial_state :initial
|
|
95
|
-
|
|
96
|
-
evolve :started do
|
|
97
|
-
:started
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
evolve :stopped do
|
|
101
|
-
:stopped
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Or any classes like [Dry::Struct](https://dry-rb.org/gems/dry-struct/) or [Data](https://rubyapi.org/3.3/o/data):
|
|
107
|
-
|
|
108
|
-
```ruby
|
|
109
|
-
State = Data.define(:speed)
|
|
110
|
-
Started = Data.define(:speed)
|
|
111
|
-
Stopped = Data.define
|
|
112
|
-
|
|
113
|
-
decider = Decider.define do
|
|
114
|
-
initial_state State.new(speed: 0)
|
|
115
|
-
|
|
116
|
-
evolve State, Started do
|
|
117
|
-
state.with(speed: event.speed)
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
evolve State, Stopped do
|
|
121
|
-
State.new(speed: 0)
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Calculate state
|
|
127
|
-
|
|
128
|
-
In most cases you want to take a collection of events and reduce them with `evolve` to calculate the state, like:
|
|
129
|
-
|
|
130
|
-
```ruby
|
|
131
|
-
decider = Decider.define do
|
|
132
|
-
initial_state 0
|
|
133
|
-
|
|
134
|
-
evolve :increased do
|
|
135
|
-
state + 1
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
[:increased, :increased].reduce(decider.initial_state) { |state, event| decider.evolve(state, event) }
|
|
140
|
-
#> 2
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
You can shortcut that with `&`:
|
|
144
|
-
|
|
145
|
-
```ruby
|
|
146
|
-
[:increased, :increased].reduce(decider.initial_state, &decider.method(:evolve))
|
|
147
|
-
#> 2
|
|
148
|
-
[:increased, :increased].reduce(decider.initial_state, &decider.evolve)
|
|
149
|
-
#> 2
|
|
150
|
-
```
|
data/examples/infra.md
DELETED
data/examples/state.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# State examples
|
|
2
|
-
|
|
3
|
-
## Data
|
|
4
|
-
|
|
5
|
-
```ruby
|
|
6
|
-
State = Data.define(:value)
|
|
7
|
-
|
|
8
|
-
decider = Decider.define do
|
|
9
|
-
initial_state State.new(value: 0)
|
|
10
|
-
|
|
11
|
-
decide Commands::Command do
|
|
12
|
-
emit Events::Event.new(value: command.value)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
evolve Events::Event do
|
|
16
|
-
state.with(value: state.value + 1)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Primitive
|
|
22
|
-
|
|
23
|
-
```ruby
|
|
24
|
-
decider = Decider.define do
|
|
25
|
-
initial_state :turned_off
|
|
26
|
-
|
|
27
|
-
decide Commands::TurnOn, :turned_off do
|
|
28
|
-
emit Events::TurnedOn.new
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
decide Commands::TurnOff, :turned_on do
|
|
32
|
-
emit Events::TurnedOff.new
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
evolve Events::TurnedOn do
|
|
36
|
-
:turned_on
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## List
|
|
42
|
-
|
|
43
|
-
```ruby
|
|
44
|
-
decider = Decider.define do
|
|
45
|
-
initial_state []
|
|
46
|
-
|
|
47
|
-
decide Commands::Command, [] do
|
|
48
|
-
emit Events::Event.new(value: command.value)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
evolve Events::Event do
|
|
52
|
-
state + [event]
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
```
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Decider
|
|
4
|
-
class EventSourcing
|
|
5
|
-
def initialize(decider:, event_store:)
|
|
6
|
-
@decider = decider
|
|
7
|
-
@event_store = event_store
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def call(command, stream_name:)
|
|
11
|
-
events = event_store.read.stream(stream_name)
|
|
12
|
-
state = events.reduce(decider.initial_state, &decider.method(:evolve))
|
|
13
|
-
|
|
14
|
-
new_events = decider.decide(command, state)
|
|
15
|
-
|
|
16
|
-
event_store.append(new_events, stream_name: stream_name, expected_version: events.count)
|
|
17
|
-
|
|
18
|
-
[new_events, events.count + new_events.count]
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
attr_reader :decider, :event_store
|
|
24
|
-
end
|
|
25
|
-
end
|
data/lib/decider/in_memory.rb
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "concurrent-ruby"
|
|
4
|
-
|
|
5
|
-
module Decider
|
|
6
|
-
class InMemory
|
|
7
|
-
def initialize(decider)
|
|
8
|
-
@decider = decider
|
|
9
|
-
@atom = Concurrent::Atom.new(decider.initial_state)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def call(command)
|
|
13
|
-
events = decider.decide(command, state)
|
|
14
|
-
atom.swap { |state| events.reduce(state, &decider.method(:evolve)) }
|
|
15
|
-
events
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def state
|
|
19
|
-
atom.value
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
attr_reader :decider, :atom
|
|
25
|
-
end
|
|
26
|
-
end
|