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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef8af0a9aa14ef5a19fce005eba64d4b4504277493434c4091510e49f1378a39
4
- data.tar.gz: 4ca62248b625d9bc3926749565c0ac4ef066a7fb46bd3cf0ec605f0c72d3e4b1
3
+ metadata.gz: 67225cdb4ecbd0f8577bc2c9744e85958a6fc74252e29014bb957e0c927ee0d9
4
+ data.tar.gz: 62bf259ddc73c780bc7993e4b597c90e45a047a4981263e81afdc44dcf07399a
5
5
  SHA512:
6
- metadata.gz: 2326979a7350bf881484052c80cba5147f160a923f41b39c80420da5424e9239b62b6758460efeef81198fb83aec4e026a933a503661cf2ced329bb83dbbd12d
7
- data.tar.gz: 07d38ce7613facb15a5e553482cf7b43d05f8b2743c3cac9b26f1fc9ad909cd87e24c03b20fc0258b554a4d9c08fa03123c6f27a36f661d31db054cef52c5221
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.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.2"
4
+ VERSION = "0.5.4"
5
5
  end
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 |state, event|
79
- context = Evolve.new(state: state, event: event)
80
-
81
- evolutions.find(EVOLVE_FALLBACK) do |args, _|
82
- case args
83
- in [Proc => fn]
84
- context.instance_exec(&fn)
85
- in [etype]
86
- event in ^etype
87
- in [stype, etype]
88
- [state, event] in [^stype, ^etype]
89
- else
90
- false
91
- end
92
- end => [_, handler]
93
-
94
- context.instance_exec(&handler)
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.2
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-04-12 00:00:00.000000000 Z
10
+ date: 2025-06-21 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: concurrent-ruby