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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d34cbcdaf2713fe8bd44d975d9d9bd40801af014cc84e3e37c69bee8ef14b1b2
4
- data.tar.gz: 8f82294e16473b2ae75040cd5b1da79c3ddca4d77f103ea86c57a498f004b1d4
3
+ metadata.gz: a644014eccc9464b607d16d44c6181de06b712c1aab8e2d33d407de838723fe8
4
+ data.tar.gz: 1dbdd3c6a7b3b7efa189d857afa28da43d4f72ac1cc3ca72801bb86b854dfa73
5
5
  SHA512:
6
- metadata.gz: 400fb7bb3bb77f17578bf15b36f4d88213694562c1626736c0433ddce2b1fa43ad84dd3a87b6b8eb641f862838aff0848210fe40caeffb20234779aa6db0b8be
7
- data.tar.gz: 7b8ecedf12b070c414aa7a41d30675d0038f439f6d892990ad74a9289ad3d2df006c78ea402557b3f80c2ec36343d434563b44d58f267b30af919d848ded3469
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 = decider.dimap_on_event(
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 = decider.dimap_on_state(
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.new(value: 15)]
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, [:started, _]] } do
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
- ## Handling unknown events
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
- Return state (nothing changed) by default:
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
- Raise error when using `evolve!`:
74
+ If you want to raise error, define a catch-all as last one:
19
75
 
20
76
  ```ruby
21
- decider.evolve! decider.initial_state, :unknown
22
- #> raise ArgumentError, Unknown event
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 |state, event|
96
+ evolve :started do
34
97
  :started
35
98
  end
36
99
 
37
- evolve :stopped do |state, event|
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 |state, event|
54
- State.new(speed: event.speed)
116
+ evolve State, Started do
117
+ state.with(speed: event.speed)
55
118
  end
56
119
 
57
- evolve Stopped do |state, event|
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 |command, state|
12
- [Events::Event.new(value: command.value)]
11
+ decide Commands::Command do
12
+ emit Events::Event.new(value: command.value)
13
13
  end
14
14
 
15
- evolve Events::Event do |state, event|
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 |_command, state|
28
- case state
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 |_command, state|
37
- case state
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 |_state, _event|
46
- :turned_off
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 |command, _state|
58
- [Events::Event.new(value: command.value)]
47
+ decide Commands::Command, [] do
48
+ emit Events::Event.new(value: command.value)
59
49
  end
60
50
 
61
- evolve Events::Event do |state, event|
62
- state = state + [event]
51
+ evolve Events::Event do
52
+ state + [event]
63
53
  end
64
54
  end
65
55
  ```
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Decider
4
- VERSION = "0.5.3"
4
+ VERSION = "0.5.5"
5
5
  end
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.3
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-04-15 00:00:00.000000000 Z
10
+ date: 2025-06-22 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: concurrent-ruby