decide.rb 0.5.3 → 0.5.5
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +138 -3
- data/examples/decide.md +2 -2
- data/examples/evolve.md +98 -10
- data/examples/state.md +13 -23
- data/lib/decider/version.rb +1 -1
- data/lib/decider.rb +84 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a644014eccc9464b607d16d44c6181de06b712c1aab8e2d33d407de838723fe8
|
4
|
+
data.tar.gz: 1dbdd3c6a7b3b7efa189d857afa28da43d4f72ac1cc3ca72801bb86b854dfa73
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 751a52fb88eef2b129cd439765c6d9083759fd3eda0727b831202d4a903e86532fc41ba4633753f0a3318aa197d64a974d612c3d8abedc0fe102d1c28a101fd7
|
7
|
+
data.tar.gz: 2b658d6758c820c6d199c59a0275ea4cf49eee221570c4c8447f72b240207e9f717e1f3537da02f67512baf3976e448d7611ca49bbf5088032f88e79b2b89989
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,138 @@
|
|
1
|
+
# 0.5.5
|
2
|
+
|
3
|
+
* Add `lmap_on_command` extension that takes proc that maps command and returns a new decider
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
decider = Decider.define do
|
7
|
+
initial_state 0
|
8
|
+
|
9
|
+
decide :increase do
|
10
|
+
emit :increased
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
lmap = decider.lmap_on_command(->(command) { command.to_sym })
|
15
|
+
lmap.decide("increase", 0)
|
16
|
+
# => [:increased]
|
17
|
+
```
|
18
|
+
|
19
|
+
* Add `map` extension that works the same way as `rmap_on_state` but `Decider.map` takes function first
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
# equivalent
|
23
|
+
Decider.rmap_on_state(decider, fn)
|
24
|
+
Decider.map(fn, decider)
|
25
|
+
decider.rmap_on_state(fn)
|
26
|
+
decider.map(fn)
|
27
|
+
```
|
28
|
+
|
29
|
+
* Add `map2` extension that takes function with two arguments and two deciders and returns a decider:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
dx = Decider.define do
|
33
|
+
initial_state({score: 0})
|
34
|
+
|
35
|
+
decide :score do
|
36
|
+
emit :scored
|
37
|
+
end
|
38
|
+
|
39
|
+
evolve :scored do
|
40
|
+
{score: state[:score] + 1}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
dy = Decider.define do
|
45
|
+
initial_state({time: 0})
|
46
|
+
|
47
|
+
decide :tick do
|
48
|
+
emit :ticked
|
49
|
+
end
|
50
|
+
|
51
|
+
evolve :ticked do
|
52
|
+
{time: state[:time] + 1}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
decider = Decider.map2(
|
57
|
+
->(sx, sy) { {score: sx[:score], time: sy[:time]} }, dx, dy
|
58
|
+
)
|
59
|
+
|
60
|
+
decider.initial_state
|
61
|
+
# => {score: 0, time: 0}
|
62
|
+
decider.decide(:score, decider.initial_state)
|
63
|
+
# => [:scored]
|
64
|
+
decider.decide(:tick, decider.initial_state)
|
65
|
+
# => [:ticked]
|
66
|
+
decider.evolve(decider.initial_state, :scored)
|
67
|
+
# => {score: 1, time: 0}
|
68
|
+
decider.evolve(decider.initial_state, :ticked)
|
69
|
+
# => {score: 0, time: 1}
|
70
|
+
```
|
71
|
+
|
72
|
+
# 0.5.4
|
73
|
+
|
74
|
+
* Add `lmap_on_event` and `rmap_on_event` extensions that takes proc that maps event in or out and returns a new decider
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
decider = Decider.define do
|
78
|
+
initial_state 0
|
79
|
+
|
80
|
+
decide :increase do
|
81
|
+
emit :increased
|
82
|
+
end
|
83
|
+
|
84
|
+
evolve :increased do
|
85
|
+
state + 1
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
lmap = decider.lmap_on_event(->(event) { event.to_sym })
|
90
|
+
lmap.evolve(0, "increased")
|
91
|
+
# => 1
|
92
|
+
|
93
|
+
rmap = decider.rmap_on_event(->(event) { event.to_s })
|
94
|
+
rmap.decide(:increase, 0)
|
95
|
+
# => "increased"
|
96
|
+
```
|
97
|
+
|
98
|
+
* Add `lmap_on_state` and `rmap_on_state` extensions that takes proc that maps state in or out and returns a new decider
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
decider = Decider.define do
|
102
|
+
initial_state :symbol
|
103
|
+
|
104
|
+
decide :command, :state do
|
105
|
+
emit :called
|
106
|
+
end
|
107
|
+
|
108
|
+
evolve :state, :called do
|
109
|
+
:new_state
|
110
|
+
end
|
111
|
+
end
|
112
|
+
decider.initial_state
|
113
|
+
# => :symbol
|
114
|
+
|
115
|
+
lmap = decider.lmap_on_state(
|
116
|
+
->(state) { state.to_sym }
|
117
|
+
)
|
118
|
+
lmap.initial_state
|
119
|
+
# => :symbol
|
120
|
+
lmap.decide(:command, "symbol")
|
121
|
+
# => [:called]
|
122
|
+
lmap.evolve("symbol", :called)
|
123
|
+
# => :new_state
|
124
|
+
|
125
|
+
rmap = inner.rmap_on_state(
|
126
|
+
->(state) { state.to_s }
|
127
|
+
)
|
128
|
+
rmap.initial_state
|
129
|
+
# => "symbol"
|
130
|
+
rmap.decide(:command, :symbol)
|
131
|
+
# => [:called]
|
132
|
+
rmap.evolve(:symbol, :called)
|
133
|
+
# => "new_state"
|
134
|
+
```
|
135
|
+
|
1
136
|
# 0.5.3
|
2
137
|
|
3
138
|
* Add shortcut for evolving:
|
@@ -20,7 +155,7 @@ inner = Decider.define do
|
|
20
155
|
initial_state 0
|
21
156
|
|
22
157
|
decide :increase do
|
23
|
-
:increased
|
158
|
+
emit :increased
|
24
159
|
end
|
25
160
|
|
26
161
|
evolve :increased do
|
@@ -28,7 +163,7 @@ inner = Decider.define do
|
|
28
163
|
end
|
29
164
|
end
|
30
165
|
|
31
|
-
outer =
|
166
|
+
outer = inner.dimap_on_event(
|
32
167
|
fl: ->(event) { event.to_sym },
|
33
168
|
fr: ->(event) { event.to_s }
|
34
169
|
)
|
@@ -51,7 +186,7 @@ end
|
|
51
186
|
inner.initial_state
|
52
187
|
# => :symbol
|
53
188
|
|
54
|
-
outer =
|
189
|
+
outer = inner.dimap_on_state(
|
55
190
|
fl: ->(state) { state.to_sym },
|
56
191
|
fr: ->(state) { state.to_s }
|
57
192
|
)
|
data/examples/decide.md
CHANGED
@@ -18,7 +18,7 @@ decider = Decider.define do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
decider.decide Command.new(value: 10), decider.initial_state
|
21
|
-
#> [Event
|
21
|
+
#> [#<data Event value=15>]
|
22
22
|
```
|
23
23
|
|
24
24
|
## Match by command and state
|
@@ -138,7 +138,7 @@ decider = Decider.define do
|
|
138
138
|
emit [:started, command.value]
|
139
139
|
end
|
140
140
|
|
141
|
-
decide proc { [command, state] in [Stop,
|
141
|
+
decide proc { [command, state] in [Stop, :started] } do
|
142
142
|
emit :stopped
|
143
143
|
end
|
144
144
|
end
|
data/examples/evolve.md
CHANGED
@@ -1,25 +1,88 @@
|
|
1
1
|
# Evolve
|
2
2
|
|
3
|
-
##
|
3
|
+
## Match by event
|
4
4
|
|
5
5
|
```ruby
|
6
|
+
Event = Data.define(:value)
|
7
|
+
|
6
8
|
decider = Decider.define do
|
7
9
|
initial_state :initial
|
10
|
+
|
11
|
+
evolve Event do
|
12
|
+
event.value
|
13
|
+
end
|
8
14
|
end
|
15
|
+
|
16
|
+
decider.evolve decider.initial_state, Event.new(value: :changed)
|
17
|
+
#> :changed
|
9
18
|
```
|
10
19
|
|
11
|
-
|
20
|
+
## Match by state and event
|
12
21
|
|
13
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>
|
14
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
|
15
71
|
#> :initial
|
16
72
|
```
|
17
73
|
|
18
|
-
|
74
|
+
If you want to raise error, define a catch-all as last one:
|
19
75
|
|
20
76
|
```ruby
|
21
|
-
decider
|
22
|
-
|
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)
|
23
86
|
```
|
24
87
|
|
25
88
|
## Events
|
@@ -30,11 +93,11 @@ Events can be primitives like symbols:
|
|
30
93
|
decider = Decider.define do
|
31
94
|
initial_state :initial
|
32
95
|
|
33
|
-
evolve :started do
|
96
|
+
evolve :started do
|
34
97
|
:started
|
35
98
|
end
|
36
99
|
|
37
|
-
evolve :stopped do
|
100
|
+
evolve :stopped do
|
38
101
|
:stopped
|
39
102
|
end
|
40
103
|
end
|
@@ -50,13 +113,38 @@ Stopped = Data.define
|
|
50
113
|
decider = Decider.define do
|
51
114
|
initial_state State.new(speed: 0)
|
52
115
|
|
53
|
-
evolve Started do
|
54
|
-
|
116
|
+
evolve State, Started do
|
117
|
+
state.with(speed: event.speed)
|
55
118
|
end
|
56
119
|
|
57
|
-
evolve Stopped do
|
120
|
+
evolve State, Stopped do
|
58
121
|
State.new(speed: 0)
|
59
122
|
end
|
60
123
|
end
|
61
124
|
```
|
62
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/state.md
CHANGED
@@ -8,11 +8,11 @@ State = Data.define(:value)
|
|
8
8
|
decider = Decider.define do
|
9
9
|
initial_state State.new(value: 0)
|
10
10
|
|
11
|
-
decide Commands::Command do
|
12
|
-
|
11
|
+
decide Commands::Command do
|
12
|
+
emit Events::Event.new(value: command.value)
|
13
13
|
end
|
14
14
|
|
15
|
-
evolve Events::Event do
|
15
|
+
evolve Events::Event do
|
16
16
|
state.with(value: state.value + 1)
|
17
17
|
end
|
18
18
|
end
|
@@ -24,26 +24,16 @@ end
|
|
24
24
|
decider = Decider.define do
|
25
25
|
initial_state :turned_off
|
26
26
|
|
27
|
-
decide Commands::TurnOn do
|
28
|
-
|
29
|
-
in :turned_off
|
30
|
-
[Events::TurnedOn.new]
|
31
|
-
else
|
32
|
-
[]
|
33
|
-
end
|
27
|
+
decide Commands::TurnOn, :turned_off do
|
28
|
+
emit Events::TurnedOn.new
|
34
29
|
end
|
35
30
|
|
36
|
-
decide Commands::TurnOff do
|
37
|
-
|
38
|
-
in :turned_on
|
39
|
-
[Events::TurnedOn.new]
|
40
|
-
else
|
41
|
-
[]
|
42
|
-
end
|
31
|
+
decide Commands::TurnOff, :turned_on do
|
32
|
+
emit Events::TurnedOff.new
|
43
33
|
end
|
44
34
|
|
45
|
-
evolve Events::TurnedOn do
|
46
|
-
:
|
35
|
+
evolve Events::TurnedOn do
|
36
|
+
:turned_on
|
47
37
|
end
|
48
38
|
end
|
49
39
|
```
|
@@ -54,12 +44,12 @@ end
|
|
54
44
|
decider = Decider.define do
|
55
45
|
initial_state []
|
56
46
|
|
57
|
-
decide Commands::Command do
|
58
|
-
|
47
|
+
decide Commands::Command, [] do
|
48
|
+
emit Events::Event.new(value: command.value)
|
59
49
|
end
|
60
50
|
|
61
|
-
evolve Events::Event do
|
62
|
-
state
|
51
|
+
evolve Events::Event do
|
52
|
+
state + [event]
|
63
53
|
end
|
64
54
|
end
|
65
55
|
```
|
data/lib/decider/version.rb
CHANGED
data/lib/decider.rb
CHANGED
@@ -108,10 +108,34 @@ module Decider
|
|
108
108
|
context.instance_exec(&terminal)
|
109
109
|
end
|
110
110
|
|
111
|
+
define_method(:lmap_on_command) do |fn|
|
112
|
+
Decider.lmap_on_command(self, fn)
|
113
|
+
end
|
114
|
+
|
115
|
+
define_method(:lmap_on_state) do |fn|
|
116
|
+
Decider.lmap_on_state(self, fn)
|
117
|
+
end
|
118
|
+
|
119
|
+
define_method(:map) do |fn|
|
120
|
+
Decider.map(fn, self)
|
121
|
+
end
|
122
|
+
|
123
|
+
define_method(:rmap_on_state) do |fn|
|
124
|
+
Decider.rmap_on_state(self, fn)
|
125
|
+
end
|
126
|
+
|
111
127
|
define_method(:dimap_on_state) do |fl:, fr:|
|
112
128
|
Decider.dimap_on_state(self, fl: fl, fr: fr)
|
113
129
|
end
|
114
130
|
|
131
|
+
define_method(:lmap_on_event) do |fn|
|
132
|
+
Decider.lmap_on_event(self, fn)
|
133
|
+
end
|
134
|
+
|
135
|
+
define_method(:rmap_on_event) do |fn|
|
136
|
+
Decider.rmap_on_event(self, fn)
|
137
|
+
end
|
138
|
+
|
115
139
|
define_method(:dimap_on_event) do |fl:, fr:|
|
116
140
|
Decider.dimap_on_event(self, fl: fl, fr: fr)
|
117
141
|
end
|
@@ -211,6 +235,36 @@ module Decider
|
|
211
235
|
end
|
212
236
|
end
|
213
237
|
|
238
|
+
def self.lmap_on_command(decider, fn)
|
239
|
+
define do
|
240
|
+
initial_state decider.initial_state
|
241
|
+
|
242
|
+
decide proc { true } do
|
243
|
+
decider.decide(fn.call(command), state).each(&method(:emit))
|
244
|
+
end
|
245
|
+
|
246
|
+
evolve proc { true } do
|
247
|
+
decider.evolve(state, event)
|
248
|
+
end
|
249
|
+
|
250
|
+
terminal? do
|
251
|
+
decider.terminal?(state)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def self.lmap_on_state(decider, fn)
|
257
|
+
dimap_on_state(decider, fl: fn, fr: ->(state) { state })
|
258
|
+
end
|
259
|
+
|
260
|
+
def self.map(fn, decider)
|
261
|
+
dimap_on_state(decider, fl: ->(state) { state }, fr: fn)
|
262
|
+
end
|
263
|
+
|
264
|
+
def self.rmap_on_state(decider, fn)
|
265
|
+
dimap_on_state(decider, fl: ->(state) { state }, fr: fn)
|
266
|
+
end
|
267
|
+
|
214
268
|
def self.dimap_on_state(decider, fl:, fr:)
|
215
269
|
define do
|
216
270
|
initial_state fr.call(decider.initial_state)
|
@@ -229,6 +283,14 @@ module Decider
|
|
229
283
|
end
|
230
284
|
end
|
231
285
|
|
286
|
+
def self.lmap_on_event(decider, fn)
|
287
|
+
dimap_on_event(decider, fl: fn, fr: ->(event) { event })
|
288
|
+
end
|
289
|
+
|
290
|
+
def self.rmap_on_event(decider, fn)
|
291
|
+
dimap_on_event(decider, fl: ->(event) { event }, fr: fn)
|
292
|
+
end
|
293
|
+
|
232
294
|
def self.dimap_on_event(decider, fl:, fr:)
|
233
295
|
define do
|
234
296
|
initial_state decider.initial_state
|
@@ -248,4 +310,26 @@ module Decider
|
|
248
310
|
end
|
249
311
|
end
|
250
312
|
end
|
313
|
+
|
314
|
+
def self.map2(fn, dx, dy)
|
315
|
+
define do
|
316
|
+
initial_state fn.call(dx.initial_state, dy.initial_state)
|
317
|
+
|
318
|
+
decide proc { true } do
|
319
|
+
dx.decide(command, state).each(&method(:emit))
|
320
|
+
dy.decide(command, state).each(&method(:emit))
|
321
|
+
end
|
322
|
+
|
323
|
+
evolve proc { true } do
|
324
|
+
fn.call(
|
325
|
+
dx.evolve(state, event),
|
326
|
+
dy.evolve(state, event)
|
327
|
+
)
|
328
|
+
end
|
329
|
+
|
330
|
+
terminal? do
|
331
|
+
dx.terminal?(state) && dy.terminal?(state)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
251
335
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: decide.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Dudulski
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-06-22 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: concurrent-ruby
|