decide.rb 0.5.2 → 0.5.4
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 +128 -0
- 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 +58 -18
- 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: 67225cdb4ecbd0f8577bc2c9744e85958a6fc74252e29014bb957e0c927ee0d9
|
4
|
+
data.tar.gz: 62bf259ddc73c780bc7993e4b597c90e45a047a4981263e81afdc44dcf07399a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7e5a790ab4d45b0d73a189ecdb19907ca139742a61f02dd146c38aa8e61619c55f744095085d89292f3f53976f60ecec17799b3dcc58166a0e5201eb3b8641a
|
7
|
+
data.tar.gz: f27228fda1b9ca07c19cb01d004576ef106de6a2c96c7ce19a4a4b7976b4a5260dca7c91bb59d2404009387152ccd0ce10537e00fe9e76d0e8054992946055d3
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,131 @@
|
|
1
|
+
# 0.5.4
|
2
|
+
|
3
|
+
* Add `lmap_on_event` and `rmap_on_event` extensions that takes proc that maps event in or out and returns a new decider
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
decider = Decider.define do
|
7
|
+
initial_state 0
|
8
|
+
|
9
|
+
decide :increase do
|
10
|
+
:increased
|
11
|
+
end
|
12
|
+
|
13
|
+
evolve :increased do
|
14
|
+
state + 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
lmap = decider.lmap_on_event(->(event) { event.to_sym })
|
19
|
+
lmap.evolve(0, "increased")
|
20
|
+
# => 1
|
21
|
+
|
22
|
+
rmap = decider.rmap_on_event(->(event) { event.to_s })
|
23
|
+
rmap.decide(:increase, 0)
|
24
|
+
# => "increased"
|
25
|
+
```
|
26
|
+
|
27
|
+
* Add `lmap_on_state` and `rmap_on_state` extensions that takes proc that maps state in or out and returns a new decider
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
decider = Decider.define do
|
31
|
+
initial_state :symbol
|
32
|
+
|
33
|
+
decide :command, :state do
|
34
|
+
emit :called
|
35
|
+
end
|
36
|
+
|
37
|
+
evolve :state, :called do
|
38
|
+
:new_state
|
39
|
+
end
|
40
|
+
end
|
41
|
+
decider.initial_state
|
42
|
+
# => :symbol
|
43
|
+
|
44
|
+
lmap = decider.lmap_on_state(
|
45
|
+
->(state) { state.to_sym }
|
46
|
+
)
|
47
|
+
lmap.initial_state
|
48
|
+
# => :symbol
|
49
|
+
lmap.decide(:command, "symbol")
|
50
|
+
# => [:called]
|
51
|
+
lmap.evolve("symbol", :called)
|
52
|
+
# => :new_state
|
53
|
+
|
54
|
+
rmap = inner.rmap_on_state(
|
55
|
+
->(state) { state.to_s }
|
56
|
+
)
|
57
|
+
rmap.initial_state
|
58
|
+
# => "symbol"
|
59
|
+
rmap.decide(:command, :symbol)
|
60
|
+
# => [:called]
|
61
|
+
rmap.evolve(:symbol, :called)
|
62
|
+
# => "new_state"
|
63
|
+
```
|
64
|
+
|
65
|
+
# 0.5.3
|
66
|
+
|
67
|
+
* Add shortcut for evolving:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
# this three are the same now
|
71
|
+
[event, event].reduce(state) { |s, e| decider.evolve(s, e) }
|
72
|
+
[event, event].reduce(state, &decider.method(:evolve))
|
73
|
+
|
74
|
+
# new
|
75
|
+
[event, event].reduce(state, &decider.evolve)
|
76
|
+
```
|
77
|
+
|
78
|
+
# 0.5.2
|
79
|
+
|
80
|
+
* Add `dimap_on_event` extension that takes procs that maps event in and out and returns a new decider
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
inner = Decider.define do
|
84
|
+
initial_state 0
|
85
|
+
|
86
|
+
decide :increase do
|
87
|
+
:increased
|
88
|
+
end
|
89
|
+
|
90
|
+
evolve :increased do
|
91
|
+
state + 1
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
outer = inner.dimap_on_event(
|
96
|
+
fl: ->(event) { event.to_sym },
|
97
|
+
fr: ->(event) { event.to_s }
|
98
|
+
)
|
99
|
+
|
100
|
+
outer.decide(:increase, 0)
|
101
|
+
# => "increased"
|
102
|
+
|
103
|
+
outer.evolve(0, "increased")
|
104
|
+
# => 1
|
105
|
+
```
|
106
|
+
|
107
|
+
# 0.5.1
|
108
|
+
|
109
|
+
* Add `dimap_on_state` extension that takes procs that maps state in and out and returns a new decider
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
inner = Decider.define do
|
113
|
+
initial_state :symbol
|
114
|
+
end
|
115
|
+
inner.initial_state
|
116
|
+
# => :symbol
|
117
|
+
|
118
|
+
outer = inner.dimap_on_state(
|
119
|
+
fl: ->(state) { state.to_sym },
|
120
|
+
fr: ->(state) { state.to_s }
|
121
|
+
)
|
122
|
+
outer.initial_state
|
123
|
+
# => "symbol"
|
124
|
+
|
125
|
+
# under the hood it will run inner.decide(:command, :symbol)
|
126
|
+
outer.decide(:command, "symbol")
|
127
|
+
```
|
128
|
+
|
1
129
|
# 0.5.0
|
2
130
|
|
3
131
|
* Support pattern matching for commands and events
|
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
@@ -47,7 +47,11 @@ module Decider
|
|
47
47
|
_events.push(*events)
|
48
48
|
end
|
49
49
|
end
|
50
|
-
Evolve = Data.define(:state, :event)
|
50
|
+
Evolve = Data.define(:state, :event) do
|
51
|
+
def self.build(state, event)
|
52
|
+
new(state: state, event: event)
|
53
|
+
end
|
54
|
+
end
|
51
55
|
Terminal = Data.define(:state)
|
52
56
|
|
53
57
|
def initialize(initial_state:, deciders:, evolutions:, terminal:)
|
@@ -75,23 +79,27 @@ module Decider
|
|
75
79
|
context._events
|
76
80
|
end
|
77
81
|
|
78
|
-
define_method(:evolve) do |
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
82
|
+
define_method(:evolve) do |*args|
|
83
|
+
if args.empty?
|
84
|
+
->(state, event) { evolve(state, event) }
|
85
|
+
else
|
86
|
+
context = Evolve.build(*args)
|
87
|
+
|
88
|
+
evolutions.find(EVOLVE_FALLBACK) do |args, _|
|
89
|
+
case args
|
90
|
+
in [Proc => fn]
|
91
|
+
context.instance_exec(&fn)
|
92
|
+
in [etype]
|
93
|
+
context.event in ^etype
|
94
|
+
in [stype, etype]
|
95
|
+
[context.state, context.event] in [^stype, ^etype]
|
96
|
+
else
|
97
|
+
false
|
98
|
+
end
|
99
|
+
end => [_, handler]
|
100
|
+
|
101
|
+
context.instance_exec(&handler)
|
102
|
+
end
|
95
103
|
end
|
96
104
|
|
97
105
|
define_method(:terminal?) do |state|
|
@@ -100,10 +108,26 @@ module Decider
|
|
100
108
|
context.instance_exec(&terminal)
|
101
109
|
end
|
102
110
|
|
111
|
+
define_method(:lmap_on_state) do |fn|
|
112
|
+
Decider.lmap_on_state(self, fn)
|
113
|
+
end
|
114
|
+
|
115
|
+
define_method(:rmap_on_state) do |fn|
|
116
|
+
Decider.rmap_on_state(self, fn)
|
117
|
+
end
|
118
|
+
|
103
119
|
define_method(:dimap_on_state) do |fl:, fr:|
|
104
120
|
Decider.dimap_on_state(self, fl: fl, fr: fr)
|
105
121
|
end
|
106
122
|
|
123
|
+
define_method(:lmap_on_event) do |fn|
|
124
|
+
Decider.lmap_on_event(self, fn)
|
125
|
+
end
|
126
|
+
|
127
|
+
define_method(:rmap_on_event) do |fn|
|
128
|
+
Decider.rmap_on_event(self, fn)
|
129
|
+
end
|
130
|
+
|
107
131
|
define_method(:dimap_on_event) do |fl:, fr:|
|
108
132
|
Decider.dimap_on_event(self, fl: fl, fr: fr)
|
109
133
|
end
|
@@ -203,6 +227,14 @@ module Decider
|
|
203
227
|
end
|
204
228
|
end
|
205
229
|
|
230
|
+
def self.lmap_on_state(decider, fn)
|
231
|
+
dimap_on_state(decider, fl: fn, fr: ->(state) { state })
|
232
|
+
end
|
233
|
+
|
234
|
+
def self.rmap_on_state(decider, fn)
|
235
|
+
dimap_on_state(decider, fl: ->(state) { state }, fr: fn)
|
236
|
+
end
|
237
|
+
|
206
238
|
def self.dimap_on_state(decider, fl:, fr:)
|
207
239
|
define do
|
208
240
|
initial_state fr.call(decider.initial_state)
|
@@ -221,6 +253,14 @@ module Decider
|
|
221
253
|
end
|
222
254
|
end
|
223
255
|
|
256
|
+
def self.lmap_on_event(decider, fn)
|
257
|
+
dimap_on_event(decider, fl: fn, fr: ->(event) { event })
|
258
|
+
end
|
259
|
+
|
260
|
+
def self.rmap_on_event(decider, fn)
|
261
|
+
dimap_on_event(decider, fl: ->(event) { event }, fr: fn)
|
262
|
+
end
|
263
|
+
|
224
264
|
def self.dimap_on_event(decider, fl:, fr:)
|
225
265
|
define do
|
226
266
|
initial_state decider.initial_state
|
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.4
|
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-21 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: concurrent-ruby
|