decide.rb 0.6.1 → 0.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +78 -0
- data/lib/decider/reactor.rb +142 -0
- data/lib/decider/version.rb +1 -1
- data/lib/decider.rb +35 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 560335da25429790af3eb660d743eb7233c3dad032f9582462de1142c9cf3d91
|
4
|
+
data.tar.gz: c42275236a358e3eac7189cf6e7f57cdc3106aa9e00f203b96d139f24360ee91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0da501705ecb75d91b1696f468c9360bc950e7cc0f5ac1ce52a3427beccb2bba8eca1688bee73e874c46c40f17d579ddde4b132a2e6fe04e3737b7091f99619d
|
7
|
+
data.tar.gz: ea55c13433fb463d9746da6fe08016353bc0d38c3aae988f0b7249cfac86ed5325b0bcbb02c3833e5997f43db61f7b293a822988a851273dc39b087d7e4b2e14
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,81 @@
|
|
1
|
+
# 0.7.0
|
2
|
+
|
3
|
+
* Add reactor that can react to action results and issue actions
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
ActionResult = Data.define(:value)
|
7
|
+
Action = Data.define(:value)
|
8
|
+
|
9
|
+
reactor = Reactor.define do
|
10
|
+
react :action_result do
|
11
|
+
issue :action
|
12
|
+
issue :another_action
|
13
|
+
end
|
14
|
+
|
15
|
+
react proc { action_result in ActionResult(value: 42) } do
|
16
|
+
issue Action.new(value: "the answer")
|
17
|
+
end
|
18
|
+
|
19
|
+
react ActionResult do
|
20
|
+
issue Action.new(value: action_result.value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
reactor.react(:action_result)
|
25
|
+
# => [:action, :another_action]
|
26
|
+
reactor.react(ActionResult.new(value: 42)
|
27
|
+
# => #<data Action value="the answer">
|
28
|
+
reactor.react(ActionResult.new(value: 1)
|
29
|
+
# => #<data Action value=1>
|
30
|
+
```
|
31
|
+
|
32
|
+
* Add `lmap_on_action_result` extension to reactor
|
33
|
+
* Add `rmap_on_action` (aliased to `map_on_action`) extensions to reactor
|
34
|
+
* Add `combine_with_decider` extension to reactor
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
decider = Decider.define do
|
38
|
+
initial_state 0
|
39
|
+
|
40
|
+
decide :action do
|
41
|
+
emit :result
|
42
|
+
end
|
43
|
+
|
44
|
+
decide :another_action do
|
45
|
+
emit :another_result
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
reactor = Decider::Reactor.define do
|
50
|
+
react :result do
|
51
|
+
issue :another_action
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
decider = reactor.combine_with_decider(decider)
|
56
|
+
decider.decide(:action)
|
57
|
+
# => [:result, :another_result]
|
58
|
+
```
|
59
|
+
|
60
|
+
# 0.6.2
|
61
|
+
|
62
|
+
* Add `many` extension that takes a decider and manage many instances
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
deciders = Decider.many(decider)
|
66
|
+
# or
|
67
|
+
deciders = decider.many
|
68
|
+
|
69
|
+
deciders.initial_state
|
70
|
+
# => {}
|
71
|
+
|
72
|
+
deciders.decide([id, command], state)
|
73
|
+
# => [[id, event], [id, event]]
|
74
|
+
|
75
|
+
deciders.evolve(state, [id, event])
|
76
|
+
# => state
|
77
|
+
```
|
78
|
+
|
1
79
|
# 0.6.1
|
2
80
|
|
3
81
|
* Add `apply` extension for creating applicatives
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decider::Reactor
|
4
|
+
class Module < ::Module
|
5
|
+
REACT_FALLBACK = proc { [nil, proc {}] }
|
6
|
+
|
7
|
+
React = Data.define(:action_result, :_actions) do
|
8
|
+
def issue(*actions)
|
9
|
+
_actions.push(*actions)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(reactions:)
|
14
|
+
define_method(:react) do |action_result|
|
15
|
+
context = React.new(action_result: action_result, _actions: [])
|
16
|
+
|
17
|
+
reactions.find(REACT_FALLBACK) do |arg, _|
|
18
|
+
case arg
|
19
|
+
in Proc => fn
|
20
|
+
context.instance_exec(&fn)
|
21
|
+
in artype
|
22
|
+
action_result in ^artype
|
23
|
+
else
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end => [_, handler]
|
27
|
+
|
28
|
+
context.instance_exec(&handler)
|
29
|
+
context._actions
|
30
|
+
end
|
31
|
+
|
32
|
+
define_method(:lmap_on_action_result) do |fn|
|
33
|
+
Decider::Reactor.lmap_on_action_result(fn, self)
|
34
|
+
end
|
35
|
+
|
36
|
+
define_method(:rmap_on_action) do |fn|
|
37
|
+
Decider::Reactor.rmap_on_action(fn, self)
|
38
|
+
end
|
39
|
+
|
40
|
+
define_method(:map_on_action) do |fn|
|
41
|
+
Decider::Reactor.rmap_on_action(fn, self)
|
42
|
+
end
|
43
|
+
|
44
|
+
define_method(:combine_with_decider) do |decider|
|
45
|
+
Decider::Reactor.combine_with_decider(self, decider)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Builder
|
51
|
+
DEFAULT = Object.new
|
52
|
+
|
53
|
+
attr_reader :module
|
54
|
+
|
55
|
+
def initialize
|
56
|
+
@reactions = {}
|
57
|
+
end
|
58
|
+
|
59
|
+
def build(&block)
|
60
|
+
instance_exec(&block) if block_given?
|
61
|
+
|
62
|
+
reactor = Class.new
|
63
|
+
|
64
|
+
@module = Module.new(
|
65
|
+
reactions: reactions
|
66
|
+
)
|
67
|
+
|
68
|
+
reactor.extend(@module)
|
69
|
+
|
70
|
+
reactor
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
attr_reader :reactions
|
76
|
+
|
77
|
+
def react(arg, &block)
|
78
|
+
reactions[arg] = block
|
79
|
+
end
|
80
|
+
end
|
81
|
+
private_constant :Builder
|
82
|
+
|
83
|
+
def self.define(&block)
|
84
|
+
builder = Builder.new
|
85
|
+
builder.build(&block)
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.lmap_on_action_result(fn, reactor)
|
89
|
+
define do
|
90
|
+
react proc { true } do
|
91
|
+
reactor.react(fn.call(action_result)).each do |action|
|
92
|
+
issue action
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.rmap_on_action(fn, reactor)
|
99
|
+
define do
|
100
|
+
react proc { true } do
|
101
|
+
reactor.react(action_result).each do |action|
|
102
|
+
issue fn.call(action)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.map_on_action(fn, reactor)
|
109
|
+
rmap_on_action(fn, reactor)
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.combine_with_decider(reactor, decider)
|
113
|
+
Decider.define do
|
114
|
+
initial_state decider.initial_state
|
115
|
+
|
116
|
+
decide proc { true } do
|
117
|
+
fn = ->(commands, events, ds) {
|
118
|
+
case commands
|
119
|
+
in []
|
120
|
+
events
|
121
|
+
in [head, *tail]
|
122
|
+
new_events = decider.decide(head, ds)
|
123
|
+
new_commands = new_events.flat_map { |action_result| reactor.react(action_result) }
|
124
|
+
new_state = new_events.reduce(ds, &decider.evolve)
|
125
|
+
|
126
|
+
fn.call(tail + new_commands, events + new_events, new_state)
|
127
|
+
end
|
128
|
+
}
|
129
|
+
|
130
|
+
fn.call([command], [], state).each { |event| emit event }
|
131
|
+
end
|
132
|
+
|
133
|
+
evolve proc { true } do
|
134
|
+
decider.evolve(state, event)
|
135
|
+
end
|
136
|
+
|
137
|
+
terminal? do
|
138
|
+
decider.terminal?(state)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
data/lib/decider/version.rb
CHANGED
data/lib/decider.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "decider/reactor"
|
4
|
+
|
3
5
|
module Decider
|
4
6
|
StateAlreadyDefined = Class.new(StandardError)
|
5
7
|
StateNotDefined = Class.new(StandardError)
|
@@ -140,6 +142,10 @@ module Decider
|
|
140
142
|
Decider.dimap_on_event(fl, fr, self)
|
141
143
|
end
|
142
144
|
|
145
|
+
define_method(:many) do
|
146
|
+
Decider.many(self)
|
147
|
+
end
|
148
|
+
|
143
149
|
define_method(:apply) do |f|
|
144
150
|
Decider.apply(self, f)
|
145
151
|
end
|
@@ -315,6 +321,35 @@ module Decider
|
|
315
321
|
end
|
316
322
|
end
|
317
323
|
|
324
|
+
def self.many(decider)
|
325
|
+
define do
|
326
|
+
initial_state({})
|
327
|
+
|
328
|
+
decide proc { [command, state] in [[_id, _], Hash] } do
|
329
|
+
command => [id, command]
|
330
|
+
|
331
|
+
ds = state.fetch(id) { decider.initial_state }
|
332
|
+
|
333
|
+
decider.decide(command, ds).each do |event|
|
334
|
+
emit [id, event]
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
evolve proc { [state, event] in [Hash, [_id, _]] } do
|
339
|
+
event => [id, event]
|
340
|
+
|
341
|
+
ds = state.fetch(id) { decider.initial_state }
|
342
|
+
ds = decider.evolve(ds, event)
|
343
|
+
|
344
|
+
state.merge(id => ds)
|
345
|
+
end
|
346
|
+
|
347
|
+
terminal? do
|
348
|
+
state.any? && state.all? { |_, ds| decider.terminal?(ds) }
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
318
353
|
def self.apply(f, x)
|
319
354
|
map2(->(f, x) { f.call(x) }, f, x)
|
320
355
|
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.
|
4
|
+
version: 0.7.0
|
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-07-14 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: concurrent-ruby
|
@@ -71,6 +71,7 @@ files:
|
|
71
71
|
- lib/decider.rb
|
72
72
|
- lib/decider/event_sourcing.rb
|
73
73
|
- lib/decider/in_memory.rb
|
74
|
+
- lib/decider/reactor.rb
|
74
75
|
- lib/decider/state.rb
|
75
76
|
- lib/decider/version.rb
|
76
77
|
- sig/decider.rbs
|