decide.rb 0.7.0 → 0.8.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 +22 -2
- data/lib/decider/reactor.rb +104 -104
- data/lib/decider/version.rb +1 -1
- data/lib/decider/view.rb +132 -0
- data/lib/decider.rb +4 -5
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae980af0098467da425746f3d2cb9cf41eb67301dea7b369d36ddbba09357414
|
4
|
+
data.tar.gz: 42b2d4110c43de9aa88c33704caf9e53ca9f3bdc346ea0c5108ffe3f34c35d2d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d49815edc97d5d4ae52b6c46b320f2b560bb137f7c45cd87b52855d6e420c3bd342985f423c42c9283d4ef3adfa218b14605e409362e5c8ffb81c6ea1803c2d
|
7
|
+
data.tar.gz: 3e36c74937ce1bbf65df117fa541f1ca9a51d6ff859db7ee8ab5d941a14b34f7ec61a643384f58e28c920b7368f15bd855f578d4ca53e2471a46c0452b88125a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
# 0.8.0
|
2
|
+
|
3
|
+
* Add view that can evolve from initial state
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
view = Decider::View.define do
|
7
|
+
initial_state 0
|
8
|
+
|
9
|
+
evolve :increased do
|
10
|
+
state + 1
|
11
|
+
end
|
12
|
+
|
13
|
+
evolve :decreased do
|
14
|
+
state - 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
* Add `lmap_on_event`, `dimap_on_state`, `lmap_on_state` and `rmap_on_state` extensions for view
|
20
|
+
|
1
21
|
# 0.7.0
|
2
22
|
|
3
23
|
* Add reactor that can react to action results and issue actions
|
@@ -6,7 +26,7 @@
|
|
6
26
|
ActionResult = Data.define(:value)
|
7
27
|
Action = Data.define(:value)
|
8
28
|
|
9
|
-
reactor = Reactor.define do
|
29
|
+
reactor = Decider::Reactor.define do
|
10
30
|
react :action_result do
|
11
31
|
issue :action
|
12
32
|
issue :another_action
|
@@ -82,7 +102,7 @@ deciders.evolve(state, [id, event])
|
|
82
102
|
|
83
103
|
```ruby
|
84
104
|
decider = Decider.map(fn.curry, deciderx)
|
85
|
-
decider = Decider.apply(decider, decidery)
|
105
|
+
decider = Decider.apply(decider, decidery)
|
86
106
|
decider = Decider.apply(decider, deciderz)
|
87
107
|
# or
|
88
108
|
deciderx.map(fn.curry).apply(decidery).apply(deciderz)
|
data/lib/decider/reactor.rb
CHANGED
@@ -1,141 +1,141 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Decider
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
module Decider
|
4
|
+
module Reactor
|
5
|
+
class Module < ::Module
|
6
|
+
REACT_FALLBACK = proc { [nil, proc {}] }
|
7
|
+
|
8
|
+
React = Data.define(:action_result, :_actions) do
|
9
|
+
def issue(*actions)
|
10
|
+
_actions.push(*actions)
|
11
|
+
end
|
10
12
|
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
13
|
|
28
|
-
|
29
|
-
|
30
|
-
|
14
|
+
def initialize(reactions:)
|
15
|
+
define_method(:react) do |action_result|
|
16
|
+
context = React.new(action_result: action_result, _actions: [])
|
17
|
+
|
18
|
+
reactions.find(REACT_FALLBACK) do |arg, _|
|
19
|
+
case arg
|
20
|
+
in Proc => fn
|
21
|
+
context.instance_exec(&fn)
|
22
|
+
in artype
|
23
|
+
action_result in ^artype
|
24
|
+
else
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end => [_, handler]
|
28
|
+
|
29
|
+
context.instance_exec(&handler)
|
30
|
+
context._actions
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
define_method(:lmap_on_action_result) do |fn|
|
34
|
+
Decider::Reactor.lmap_on_action_result(fn, self)
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
define_method(:rmap_on_action) do |fn|
|
38
|
+
Decider::Reactor.rmap_on_action(fn, self)
|
39
|
+
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
define_method(:map_on_action) do |fn|
|
42
|
+
Decider::Reactor.rmap_on_action(fn, self)
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
|
45
|
+
define_method(:combine_with_decider) do |decider|
|
46
|
+
Decider::Reactor.combine_with_decider(self, decider)
|
47
|
+
end
|
46
48
|
end
|
47
49
|
end
|
48
|
-
end
|
49
50
|
|
50
|
-
|
51
|
-
|
51
|
+
class Builder
|
52
|
+
DEFAULT = Object.new
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
@reactions = {}
|
57
|
-
end
|
54
|
+
def initialize
|
55
|
+
@reactions = {}
|
56
|
+
end
|
58
57
|
|
59
|
-
|
60
|
-
|
58
|
+
def build(&block)
|
59
|
+
instance_exec(&block) if block_given?
|
61
60
|
|
62
|
-
|
61
|
+
reactor = Class.new
|
63
62
|
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
mod = Module.new(
|
64
|
+
reactions: reactions
|
65
|
+
)
|
67
66
|
|
68
|
-
|
67
|
+
reactor.extend(mod)
|
69
68
|
|
70
|
-
|
71
|
-
|
69
|
+
reactor
|
70
|
+
end
|
72
71
|
|
73
|
-
|
72
|
+
private
|
74
73
|
|
75
|
-
|
74
|
+
attr_reader :reactions
|
76
75
|
|
77
|
-
|
78
|
-
|
76
|
+
def react(arg, &block)
|
77
|
+
reactions[arg] = block
|
78
|
+
end
|
79
79
|
end
|
80
|
-
|
81
|
-
private_constant :Builder
|
80
|
+
private_constant :Builder
|
82
81
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
82
|
+
def self.define(&block)
|
83
|
+
builder = Builder.new
|
84
|
+
builder.build(&block)
|
85
|
+
end
|
87
86
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
87
|
+
def self.lmap_on_action_result(fn, reactor)
|
88
|
+
define do
|
89
|
+
react proc { true } do
|
90
|
+
reactor.react(fn.call(action_result)).each do |action|
|
91
|
+
issue action
|
92
|
+
end
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
96
|
-
end
|
97
96
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
97
|
+
def self.rmap_on_action(fn, reactor)
|
98
|
+
define do
|
99
|
+
react proc { true } do
|
100
|
+
reactor.react(action_result).each do |action|
|
101
|
+
issue fn.call(action)
|
102
|
+
end
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
106
|
-
end
|
107
106
|
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
}
|
107
|
+
def self.map_on_action(fn, reactor)
|
108
|
+
rmap_on_action(fn, reactor)
|
109
|
+
end
|
129
110
|
|
130
|
-
|
131
|
-
|
111
|
+
def self.combine_with_decider(reactor, decider)
|
112
|
+
Decider.define do
|
113
|
+
initial_state decider.initial_state
|
114
|
+
|
115
|
+
decide proc { true } do
|
116
|
+
fn = ->(commands, events, ds) {
|
117
|
+
case commands
|
118
|
+
in []
|
119
|
+
events
|
120
|
+
in [head, *tail]
|
121
|
+
new_events = decider.decide(head, ds)
|
122
|
+
new_commands = new_events.flat_map { |action_result| reactor.react(action_result) }
|
123
|
+
new_state = new_events.reduce(ds, &decider.evolve)
|
124
|
+
|
125
|
+
fn.call(tail + new_commands, events + new_events, new_state)
|
126
|
+
end
|
127
|
+
}
|
128
|
+
|
129
|
+
fn.call([command], [], state).each { |event| emit event }
|
130
|
+
end
|
132
131
|
|
133
|
-
|
134
|
-
|
135
|
-
|
132
|
+
evolve proc { true } do
|
133
|
+
decider.evolve(state, event)
|
134
|
+
end
|
136
135
|
|
137
|
-
|
138
|
-
|
136
|
+
terminal? do
|
137
|
+
decider.terminal?(state)
|
138
|
+
end
|
139
139
|
end
|
140
140
|
end
|
141
141
|
end
|
data/lib/decider/version.rb
CHANGED
data/lib/decider/view.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decider::View
|
4
|
+
class Module < ::Module
|
5
|
+
EVOLVE_FALLBACK = proc { [nil, proc { state }] }
|
6
|
+
|
7
|
+
Evolve = Data.define(:state, :event) do
|
8
|
+
def self.build(state, event)
|
9
|
+
new(state: state, event: event)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(initial_state:, evolutions:)
|
14
|
+
define_method(:initial_state) do
|
15
|
+
initial_state
|
16
|
+
end
|
17
|
+
|
18
|
+
define_method(:evolve) do |*args|
|
19
|
+
if args.empty?
|
20
|
+
->(state, event) { evolve(state, event) }
|
21
|
+
else
|
22
|
+
context = Evolve.build(*args)
|
23
|
+
|
24
|
+
evolutions.find(EVOLVE_FALLBACK) do |args, _|
|
25
|
+
case args
|
26
|
+
in [Proc => fn]
|
27
|
+
context.instance_exec(&fn)
|
28
|
+
in [etype]
|
29
|
+
context.event in ^etype
|
30
|
+
in [stype, etype]
|
31
|
+
[context.state, context.event] in [^stype, ^etype]
|
32
|
+
else
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end => [_, handler]
|
36
|
+
|
37
|
+
context.instance_exec(&handler)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
define_method(:lmap_on_event) do |fn|
|
42
|
+
Decider::View.lmap_on_event(fn, self)
|
43
|
+
end
|
44
|
+
|
45
|
+
define_method(:lmap_on_state) do |fn|
|
46
|
+
Decider::View.lmap_on_state(fn, self)
|
47
|
+
end
|
48
|
+
|
49
|
+
define_method(:rmap_on_state) do |fn|
|
50
|
+
Decider::View.rmap_on_state(fn, self)
|
51
|
+
end
|
52
|
+
|
53
|
+
define_method(:dimap_on_state) do |fl:, fr:|
|
54
|
+
Decider::View.dimap_on_state(fl, fr, self)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Builder
|
60
|
+
DEFAULT = Object.new
|
61
|
+
|
62
|
+
def initialize
|
63
|
+
@initial_state = DEFAULT
|
64
|
+
@evolutions = {}
|
65
|
+
end
|
66
|
+
|
67
|
+
def build(&block)
|
68
|
+
instance_exec(&block) if block_given?
|
69
|
+
|
70
|
+
raise StateNotDefined if @initial_state == DEFAULT
|
71
|
+
|
72
|
+
view = Class.new
|
73
|
+
|
74
|
+
mod = Module.new(
|
75
|
+
initial_state: @initial_state,
|
76
|
+
evolutions: evolutions
|
77
|
+
)
|
78
|
+
|
79
|
+
view.extend(mod)
|
80
|
+
|
81
|
+
view
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
attr_reader :evolutions
|
87
|
+
|
88
|
+
def initial_state(state)
|
89
|
+
raise StateAlreadyDefined if @initial_state != DEFAULT
|
90
|
+
|
91
|
+
@initial_state = state
|
92
|
+
end
|
93
|
+
|
94
|
+
def evolve(*args, &block)
|
95
|
+
evolutions[args] = block
|
96
|
+
end
|
97
|
+
end
|
98
|
+
private_constant :Builder
|
99
|
+
|
100
|
+
def self.define(&block)
|
101
|
+
builder = Builder.new
|
102
|
+
builder.build(&block)
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.lmap_on_event(fn, view)
|
106
|
+
define do
|
107
|
+
initial_state view.initial_state
|
108
|
+
|
109
|
+
evolve proc { true } do
|
110
|
+
view.evolve(state, fn.call(event))
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.lmap_on_state(fn, view)
|
116
|
+
dimap_on_state(fn, ->(state) { state }, view)
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.rmap_on_state(fn, view)
|
120
|
+
dimap_on_state(->(state) { state }, fn, view)
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.dimap_on_state(fl, fr, view)
|
124
|
+
define do
|
125
|
+
initial_state fr.call(view.initial_state)
|
126
|
+
|
127
|
+
evolve proc { true } do
|
128
|
+
fr.call(view.evolve(fl.call(state), event))
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/decider.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative "decider/reactor"
|
4
|
+
require_relative "decider/view"
|
4
5
|
|
5
6
|
module Decider
|
6
7
|
StateAlreadyDefined = Class.new(StandardError)
|
@@ -155,8 +156,6 @@ module Decider
|
|
155
156
|
class Builder
|
156
157
|
DEFAULT = Object.new
|
157
158
|
|
158
|
-
attr_reader :module
|
159
|
-
|
160
159
|
def initialize
|
161
160
|
@initial_state = DEFAULT
|
162
161
|
@deciders = {}
|
@@ -171,14 +170,14 @@ module Decider
|
|
171
170
|
|
172
171
|
decider = Class.new
|
173
172
|
|
174
|
-
|
173
|
+
mod = Module.new(
|
175
174
|
initial_state: @initial_state,
|
176
175
|
deciders: deciders,
|
177
176
|
evolutions: evolutions,
|
178
177
|
terminal: terminal
|
179
178
|
)
|
180
179
|
|
181
|
-
decider.extend(
|
180
|
+
decider.extend(mod)
|
182
181
|
|
183
182
|
decider
|
184
183
|
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.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Dudulski
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: concurrent-ruby
|
@@ -74,6 +74,7 @@ files:
|
|
74
74
|
- lib/decider/reactor.rb
|
75
75
|
- lib/decider/state.rb
|
76
76
|
- lib/decider/version.rb
|
77
|
+
- lib/decider/view.rb
|
77
78
|
- sig/decider.rbs
|
78
79
|
homepage: https://github.com/jandudulski/decide.rb
|
79
80
|
licenses:
|
@@ -99,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
100
|
- !ruby/object:Gem::Version
|
100
101
|
version: '0'
|
101
102
|
requirements: []
|
102
|
-
rubygems_version: 3.6.
|
103
|
+
rubygems_version: 3.6.9
|
103
104
|
specification_version: 4
|
104
105
|
summary: Functional Event Sourcing Decider in Ruby
|
105
106
|
test_files: []
|