decide.rb 0.1.0 → 0.2.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 +5 -0
- data/README.md +55 -4
- data/Steepfile +26 -0
- data/lib/decider/version.rb +1 -1
- data/lib/decider.rb +59 -3
- data/sig/decider.rbs +13 -0
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d042a0810111e7fcb0f9226ad109b20903a8305689df72d25a04caaa17deeff
|
4
|
+
data.tar.gz: d45f4d9aa5edc832dbdf303c8b0ba2e5aa6b7293c8c00614f9f8dfc69186c5c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf2b770bdabf04def99cbb942d6de633b00a9be6637adec05e81ad52228022e92e0cfc8e71e2e9ac12501febcdb411386242eb341d25e4d0be8f52b62f761ff6
|
7
|
+
data.tar.gz: 614461548e7859a8524cbd6e29961790443e89a10e69d49ec690448af7204afb04a71bf984e8825de3d7b57496753e836e98c0b7ff9c4475d2715838ffaa0075
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -19,7 +19,7 @@ gem "decide.rb"
|
|
19
19
|
## Usage
|
20
20
|
|
21
21
|
```ruby
|
22
|
-
require "
|
22
|
+
require "decider"
|
23
23
|
|
24
24
|
module Commands
|
25
25
|
Increase = Data.define
|
@@ -31,10 +31,10 @@ module Events
|
|
31
31
|
ValueDecreased = Data.define
|
32
32
|
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
MAX = 100
|
34
|
+
MIN = 0
|
35
|
+
MAX = 100
|
37
36
|
|
37
|
+
ValueDecider = Decider.define do
|
38
38
|
# define intial state
|
39
39
|
state value: 0 do
|
40
40
|
# you can define custom methods on state
|
@@ -75,6 +75,10 @@ ValueDecider = Decider.define do
|
|
75
75
|
# state is immutable Data object
|
76
76
|
state.with(value: state.value - 1)
|
77
77
|
end
|
78
|
+
|
79
|
+
terminal? do |state|
|
80
|
+
state <= 0
|
81
|
+
end
|
78
82
|
end
|
79
83
|
|
80
84
|
state = ValueDecider.initial_state
|
@@ -82,6 +86,53 @@ events = ValueDecider.decide(Commands::Increase.new, state)
|
|
82
86
|
new_state = events.reduce(state) { |state, event| ValueDecider.evolve(state, events)
|
83
87
|
```
|
84
88
|
|
89
|
+
You can also compose deciders:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
left = Decider.define do
|
93
|
+
state value: 0
|
94
|
+
|
95
|
+
decide Commands::LeftCommand do |command, state|
|
96
|
+
[Events::LeftEvent.new(value: command.value)]
|
97
|
+
end
|
98
|
+
|
99
|
+
evolve Events::LeftEvent do |state, event|
|
100
|
+
state.with(value: state.value + 1)
|
101
|
+
end
|
102
|
+
|
103
|
+
terminal? do |state|
|
104
|
+
state <= 0
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
right = Decider.define do
|
109
|
+
state value: 0
|
110
|
+
|
111
|
+
decide Commands::RightCommand do |command, state|
|
112
|
+
[Events::RightEvent.new(value: command.value)]
|
113
|
+
end
|
114
|
+
|
115
|
+
evolve Events::RightEvent do |state, event|
|
116
|
+
state.with(value: state.value + 1)
|
117
|
+
end
|
118
|
+
|
119
|
+
terminal? do |state|
|
120
|
+
state <= 0
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
Composition = Decider.compose(left, right)
|
125
|
+
|
126
|
+
state = Composition.initial_state
|
127
|
+
#> #<data left=#<data value=0>, right=#<data value=0>>
|
128
|
+
|
129
|
+
events = Composition.decide(Commands::LeftCommand.new(value: 1), state)
|
130
|
+
#> [#<data value=1>]
|
131
|
+
|
132
|
+
state = events.reduce(state, &Composition.method(:evolve))
|
133
|
+
#> #<data left=#<data value=1>, right=#<data value=0>>
|
134
|
+
```
|
135
|
+
|
85
136
|
## Development
|
86
137
|
|
87
138
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/Steepfile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
D = Steep::Diagnostic
|
2
|
+
|
3
|
+
target :lib do
|
4
|
+
signature "sig"
|
5
|
+
|
6
|
+
check "lib"
|
7
|
+
check "Gemfile"
|
8
|
+
|
9
|
+
# library "pathname" # Standard libraries
|
10
|
+
# library "strong_json" # Gems
|
11
|
+
|
12
|
+
# configure_code_diagnostics(D::Ruby.strict) # `strict` diagnostics setting
|
13
|
+
configure_code_diagnostics(D::Ruby.lenient) # `lenient` diagnostics setting
|
14
|
+
# configure_code_diagnostics(D::Ruby.silent) # `silent` diagnostics setting
|
15
|
+
# configure_code_diagnostics do |hash| # You can setup everything yourself
|
16
|
+
# hash[D::Ruby::NoMethod] = :information
|
17
|
+
# end
|
18
|
+
end
|
19
|
+
|
20
|
+
# target :test do
|
21
|
+
# signature "sig", "sig-private"
|
22
|
+
|
23
|
+
# check "test"
|
24
|
+
|
25
|
+
# # library "pathname" # Standard libraries
|
26
|
+
# end
|
data/lib/decider/version.rb
CHANGED
data/lib/decider.rb
CHANGED
@@ -5,11 +5,19 @@ module Decider
|
|
5
5
|
StateNotDefined = Class.new(StandardError)
|
6
6
|
|
7
7
|
class Module < ::Module
|
8
|
-
def initialize(initial_state_args:, deciders:, evolvers:)
|
8
|
+
def initialize(initial_state_args:, deciders:, evolvers:, terminal:)
|
9
9
|
define_method(:initial_state) do
|
10
10
|
new(**initial_state_args)
|
11
11
|
end
|
12
12
|
|
13
|
+
define_method(:commands) do
|
14
|
+
deciders.keys
|
15
|
+
end
|
16
|
+
|
17
|
+
define_method(:events) do
|
18
|
+
evolvers.keys
|
19
|
+
end
|
20
|
+
|
13
21
|
define_method(:decide) do |command, state|
|
14
22
|
handler = deciders.fetch(command.class) {
|
15
23
|
raise ArgumentError, "Unknown command: #{command.class}"
|
@@ -25,6 +33,10 @@ module Decider
|
|
25
33
|
|
26
34
|
handler.call(state, event)
|
27
35
|
end
|
36
|
+
|
37
|
+
define_method(:terminal?) do |state|
|
38
|
+
terminal.call(state)
|
39
|
+
end
|
28
40
|
end
|
29
41
|
end
|
30
42
|
|
@@ -37,6 +49,7 @@ module Decider
|
|
37
49
|
@state = DEFAULT
|
38
50
|
@deciders = {}
|
39
51
|
@evolvers = {}
|
52
|
+
@terminal = ->(_state) { false }
|
40
53
|
end
|
41
54
|
|
42
55
|
def build(&block)
|
@@ -47,7 +60,8 @@ module Decider
|
|
47
60
|
@module = Module.new(
|
48
61
|
initial_state_args: initial_state_args,
|
49
62
|
deciders: deciders,
|
50
|
-
evolvers: evolvers
|
63
|
+
evolvers: evolvers,
|
64
|
+
terminal: terminal
|
51
65
|
)
|
52
66
|
|
53
67
|
@state.extend(@module)
|
@@ -57,7 +71,7 @@ module Decider
|
|
57
71
|
|
58
72
|
private
|
59
73
|
|
60
|
-
attr_reader :initial_state_args, :deciders, :evolvers
|
74
|
+
attr_reader :initial_state_args, :deciders, :evolvers, :terminal
|
61
75
|
|
62
76
|
def state(**kwargs, &block)
|
63
77
|
raise StateAlreadyDefined if @state != DEFAULT
|
@@ -73,6 +87,10 @@ module Decider
|
|
73
87
|
def evolve(event, &block)
|
74
88
|
evolvers[event] = block
|
75
89
|
end
|
90
|
+
|
91
|
+
def terminal?(&block)
|
92
|
+
@terminal = block
|
93
|
+
end
|
76
94
|
end
|
77
95
|
private_constant :Builder
|
78
96
|
|
@@ -80,4 +98,42 @@ module Decider
|
|
80
98
|
builder = Builder.new
|
81
99
|
builder.build(&block)
|
82
100
|
end
|
101
|
+
|
102
|
+
def self.compose(left, right)
|
103
|
+
define do
|
104
|
+
state left: left.initial_state, right: right.initial_state
|
105
|
+
|
106
|
+
left.commands.each do |klass|
|
107
|
+
decide klass do |command, state|
|
108
|
+
left.decide(command, state.left)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
right.commands.each do |klass|
|
113
|
+
decide klass do |command, state|
|
114
|
+
right.decide(command, state.right)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
left.events.each do |klass|
|
119
|
+
evolve klass do |state, event|
|
120
|
+
state.with(
|
121
|
+
left: left.evolve(state.left, event)
|
122
|
+
)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
right.events.each do |klass|
|
127
|
+
evolve klass do |state, event|
|
128
|
+
state.with(
|
129
|
+
right: right.evolve(state.right, event)
|
130
|
+
)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
terminal? do |state|
|
135
|
+
left.terminal?(state.left) && right.terminal?(state.right)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
83
139
|
end
|
data/sig/decider.rbs
CHANGED
@@ -1,3 +1,16 @@
|
|
1
1
|
module Decider
|
2
2
|
VERSION: String
|
3
|
+
|
4
|
+
interface _Decider[C, S, E]
|
5
|
+
def decide: (C, S) -> Array[E]
|
6
|
+
|
7
|
+
def evolve: (S, E) -> S
|
8
|
+
|
9
|
+
def initial_state: () -> S
|
10
|
+
|
11
|
+
def terminal?: (S) -> bool
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.compose: [C1, S1, E1, C2, S2, E2] (_Decider[C1, S1, E1], _Decider[C2, S2, E2]) -> _Decider[C1 | C2, S1 & S2, E1 | E2]
|
15
|
+
def self.define: [C, S, E] () -> _Decider[C, S, E]
|
3
16
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: decide.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Dudulski
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Functional Event Sourcing Decider in Ruby
|
14
14
|
email:
|
@@ -22,19 +22,20 @@ files:
|
|
22
22
|
- LICENSE.txt
|
23
23
|
- README.md
|
24
24
|
- Rakefile
|
25
|
+
- Steepfile
|
25
26
|
- lib/decider.rb
|
26
27
|
- lib/decider/version.rb
|
27
28
|
- sig/decider.rbs
|
28
|
-
homepage: https://github.com/jandudulski/
|
29
|
+
homepage: https://github.com/jandudulski/decide.rb
|
29
30
|
licenses:
|
30
31
|
- MIT
|
31
32
|
metadata:
|
32
33
|
allowed_push_host: https://rubygems.org
|
33
|
-
bug_tracker_uri: https://github.com/jandudulski/
|
34
|
-
changelog_uri: https://github.com/jandudulski/
|
35
|
-
documentation_uri: https://github.com/jandudulski/
|
36
|
-
homepage_uri: https://github.com/jandudulski/
|
37
|
-
source_code_uri: https://github.com/jandudulski/
|
34
|
+
bug_tracker_uri: https://github.com/jandudulski/decide.rb/issues
|
35
|
+
changelog_uri: https://github.com/jandudulski/decide.rb/CHANGELOG.md
|
36
|
+
documentation_uri: https://github.com/jandudulski/decide.rb
|
37
|
+
homepage_uri: https://github.com/jandudulski/decide.rb
|
38
|
+
source_code_uri: https://github.com/jandudulski/decide.rb
|
38
39
|
post_install_message:
|
39
40
|
rdoc_options: []
|
40
41
|
require_paths:
|