decide.rb 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bde47991ca269c4a20a47e138fb8eeb5d1135262a390991bb4b946daa778362d
4
- data.tar.gz: 5c3712f51013003b099ee4e3f7a568adecd92b0cd5d988c2f4140bb578f88e0c
3
+ metadata.gz: 41af414c2f92dc8e43dad1ac61cf6ffb939fa65bc7217223b29e9780067ce5d3
4
+ data.tar.gz: 96342dea929121644eca8d924fbcd1112fbb21ec34fb666c00e87261cf7d4dc1
5
5
  SHA512:
6
- metadata.gz: ae016ee97e995c8338221555a0cd444aa600a6ff2e22fa83e53fe2743747dca391392b07d032a3100cce9240978cd0605f86fb355692d0938bf0fd34560c8222
7
- data.tar.gz: 97ca74eb8729f4fbd1fdfe86e545cb428ef58974cfc7b91645ea246f0db140e0432229e2bec5e686898dd4a365e36419ea8dfd6ca903d643a6524c4db470380d
6
+ metadata.gz: acec8f3699e15b22ee30780b2b2e30cc95950c9d18701dbcaddc5788723780e0d6c09e16066c000db8a24ec87e62eadf20469c391c5140af662b9b951b61e41b
7
+ data.tar.gz: b088dec6fb91fe9417f83b73ebe58dd638d31ffb78f1245e62afa826a5f416d1bede83c41cc0d612afa29d50afeb119703a42e5ec3570daad08003b1bb304eb0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ # 0.5.0
2
+
3
+ * Support pattern matching for commands and events
4
+ * Support passing state to decider and evolve matchers
5
+ * Remove explicit arguments for handlers
6
+ * Remove redundant bang methods - raise error in catch-all if needed
7
+ * Add Left|Right value wrappers for composition
8
+ * Use `emit` to return events in `decide`
9
+
1
10
  # 0.4.1
2
11
 
3
12
  * `define` returns new class, not object
data/README.md CHANGED
@@ -49,7 +49,7 @@ ValueDecider = Decider.define do
49
49
  initial_state State.new(value: 0)
50
50
 
51
51
  # decide command with state
52
- decide Commands::Increase do |command, state|
52
+ decide Commands::Increase do
53
53
  # return collection of events
54
54
  if state.max?
55
55
  []
@@ -58,7 +58,7 @@ ValueDecider = Decider.define do
58
58
  end
59
59
  end
60
60
 
61
- decide Commands::Decrease do |command, state|
61
+ decide Commands::Decrease do
62
62
  if state.min?
63
63
  []
64
64
  else
@@ -67,17 +67,17 @@ ValueDecider = Decider.define do
67
67
  end
68
68
 
69
69
  # evolve state with events
70
- evolve Events::ValueIncreased do |state, event|
70
+ evolve Events::ValueIncreased do
71
71
  # return new state
72
72
  state.with(value: state.value + 1)
73
73
  end
74
74
 
75
- evolve Events::ValueDecreased do |state, event|
75
+ evolve Events::ValueDecreased do
76
76
  # state is immutable Data object
77
77
  state.with(value: state.value - 1)
78
78
  end
79
79
 
80
- terminal? do |state|
80
+ terminal? do
81
81
  state <= 0
82
82
  end
83
83
  end
@@ -96,15 +96,15 @@ Right = Data.define(:value)
96
96
  left = Decider.define do
97
97
  initial_state Left.new(value: 0)
98
98
 
99
- decide Commands::LeftCommand do |command, state|
99
+ decide Commands::LeftCommand do
100
100
  [Events::LeftEvent.new(value: command.value)]
101
101
  end
102
102
 
103
- evolve Events::LeftEvent do |state, event|
103
+ evolve Events::LeftEvent do
104
104
  state.with(value: state.value + 1)
105
105
  end
106
106
 
107
- terminal? do |state|
107
+ terminal? do
108
108
  state <= 0
109
109
  end
110
110
  end
@@ -112,15 +112,15 @@ end
112
112
  right = Decider.define do
113
113
  initial_state Right.new(value: 0)
114
114
 
115
- decide Commands::RightCommand do |command, state|
115
+ decide Commands::RightCommand do
116
116
  [Events::RightEvent.new(value: command.value)]
117
117
  end
118
118
 
119
- evolve Events::RightEvent do |state, event|
119
+ evolve Events::RightEvent do
120
120
  state.with(value: state.value + 1)
121
121
  end
122
122
 
123
- terminal? do |state|
123
+ terminal? do
124
124
  state <= 0
125
125
  end
126
126
  end
@@ -128,13 +128,13 @@ end
128
128
  Composition = Decider.compose(left, right)
129
129
 
130
130
  state = Composition.initial_state
131
- #> #<data left=#<data Left value=0>, right=#<data Right value=0>>
131
+ #> #<data Decider::Pair left=#<data Left value=0>, right=#<data Right value=0>>
132
132
 
133
- events = Composition.decide(Commands::LeftCommand.new(value: 1), state)
134
- #> [#<data value=1>]
133
+ events = Composition.decide(Decider::Left.new(Commands::LeftCommand.new(value: 1)), state)
134
+ #> [#<Decider::Left value=#<data value=1>]
135
135
 
136
136
  state = events.reduce(state, &Composition.method(:evolve))
137
- #> #<data left=#<data value=1>, right=#<data value=0>>
137
+ #> #<data Decider::Pair left=#<data value=1>, right=#<data value=0>>
138
138
  ```
139
139
 
140
140
  ## Development
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Decider
4
- VERSION = "0.4.1"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/decider.rb CHANGED
@@ -4,82 +4,100 @@ module Decider
4
4
  StateAlreadyDefined = Class.new(StandardError)
5
5
  StateNotDefined = Class.new(StandardError)
6
6
 
7
- class Composition < Array
8
- BLANK = Object.new
9
- private_constant :BLANK
7
+ Pair = Data.define(:left, :right)
10
8
 
11
- def initialize(left, right)
12
- super([left, right]).freeze
9
+ class Left
10
+ attr_reader :value
11
+
12
+ def initialize(value)
13
+ @value = value
13
14
  end
14
15
 
15
- def with(left: BLANK, right: BLANK)
16
- Composition.new(
17
- (left == BLANK) ? self.left : left,
18
- (right == BLANK) ? self.right : right
19
- )
16
+ def deconstruct
17
+ [:left, value]
18
+ end
19
+
20
+ def ==(other)
21
+ value == other.value
20
22
  end
23
+ end
24
+
25
+ class Right
26
+ attr_reader :value
21
27
 
22
- def left
23
- self[0]
28
+ def initialize(value)
29
+ @value = value
24
30
  end
25
31
 
26
- def right
27
- self[1]
32
+ def deconstruct
33
+ [:right, value]
34
+ end
35
+
36
+ def ==(other)
37
+ value == other.value
28
38
  end
29
39
  end
30
40
 
31
41
  class Module < ::Module
32
- def initialize(initial_state:, deciders:, evolutions:, terminal:)
33
- define_method(:initial_state) do
34
- initial_state
35
- end
36
-
37
- define_method(:commands) do
38
- deciders.keys
39
- end
42
+ DECIDE_FALLBACK = proc { [nil, proc {}] }
43
+ EVOLVE_FALLBACK = proc { [nil, proc { state }] }
40
44
 
41
- define_method(:events) do
42
- evolutions.keys
45
+ Decide = Data.define(:command, :state, :_events) do
46
+ def emit(*events)
47
+ _events.push(*events)
43
48
  end
49
+ end
50
+ Evolve = Data.define(:state, :event)
51
+ Terminal = Data.define(:state)
44
52
 
45
- define_method(:decide!) do |command, state|
46
- case deciders.find { |key, _| key === command }
47
- in [_, handler]
48
- handler.call(command, state)
49
- else
50
- raise ArgumentError, "Unknown command: #{command.inspect}"
51
- end
53
+ def initialize(initial_state:, deciders:, evolutions:, terminal:)
54
+ define_method(:initial_state) do
55
+ initial_state
52
56
  end
53
57
 
54
58
  define_method(:decide) do |command, state|
55
- case deciders.find { |key, _| key === command }
56
- in [_, handler]
57
- handler.call(command, state)
58
- else
59
- []
60
- end
61
- end
62
-
63
- define_method(:evolve!) do |state, event|
64
- case evolutions.find { |key, _| key === event }
65
- in [_, handler]
66
- handler.call(state, event)
67
- else
68
- raise ArgumentError, "Unknown event: #{event.inspect}"
69
- end
59
+ context = Decide.new(command: command, state: state, _events: [])
60
+
61
+ deciders.find(DECIDE_FALLBACK) do |args, _|
62
+ case args
63
+ in [Proc => fn]
64
+ context.instance_exec(&fn)
65
+ in [ctype]
66
+ command in ^ctype
67
+ in [ctype, stype]
68
+ [command, state] in [^ctype, ^stype]
69
+ else
70
+ false
71
+ end
72
+ end => [_, handler]
73
+
74
+ context.instance_exec(&handler)
75
+ context._events
70
76
  end
71
77
 
72
78
  define_method(:evolve) do |state, event|
73
- case evolutions.find { |key, _| key === event }
74
- in [_, handler]
75
- handler.call(state, event)
76
- else
77
- state
78
- end
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)
79
95
  end
80
96
 
81
97
  define_method(:terminal?) do |state|
82
- terminal.call(state)
98
+ context = Terminal.new(state: state)
99
+
100
+ context.instance_exec(&terminal)
83
101
  end
84
102
  end
85
103
  end
@@ -93,7 +111,7 @@ module Decider
93
111
  @initial_state = DEFAULT
94
112
  @deciders = {}
95
113
  @evolutions = {}
96
- @terminal = ->(_state) { false }
114
+ @terminal = proc { false }
97
115
  end
98
116
 
99
117
  def build(&block)
@@ -125,12 +143,12 @@ module Decider
125
143
  @initial_state = state
126
144
  end
127
145
 
128
- def decide(command, &block)
129
- deciders[command] = block
146
+ def decide(*args, &block)
147
+ deciders[args] = block
130
148
  end
131
149
 
132
- def evolve(event, &block)
133
- evolutions[event] = block
150
+ def evolve(*args, &block)
151
+ evolutions[args] = block
134
152
  end
135
153
 
136
154
  def terminal?(&block)
@@ -146,37 +164,32 @@ module Decider
146
164
 
147
165
  def self.compose(left, right)
148
166
  define do
149
- initial_state Composition.new(left.initial_state, right.initial_state)
167
+ initial_state Pair.new(
168
+ left: left.initial_state,
169
+ right: right.initial_state
170
+ )
150
171
 
151
- left.commands.each do |klass|
152
- decide klass do |command, state|
153
- left.decide(command, state.left)
154
- end
172
+ decide proc { command in [:left, _] } do
173
+ left.decide(command.value, state.left).each { emit Left.new(_1) }
155
174
  end
156
175
 
157
- right.commands.each do |klass|
158
- decide klass do |command, state|
159
- right.decide(command, state.right)
160
- end
176
+ decide proc { command in [:right, _] } do
177
+ right.decide(command.value, state.right).each { emit Right.new(_1) }
161
178
  end
162
179
 
163
- left.events.each do |klass|
164
- evolve klass do |state, event|
165
- state.with(
166
- left: left.evolve(state.left, event)
167
- )
168
- end
180
+ evolve proc { event in [:left, _] } do
181
+ state.with(
182
+ left: left.evolve(state.left, event.value)
183
+ )
169
184
  end
170
185
 
171
- right.events.each do |klass|
172
- evolve klass do |state, event|
173
- state.with(
174
- right: right.evolve(state.right, event)
175
- )
176
- end
186
+ evolve proc { event in [:right, _] } do
187
+ state.with(
188
+ right: right.evolve(state.right, event.value)
189
+ )
177
190
  end
178
191
 
179
- terminal? do |state|
192
+ terminal? do
180
193
  left.terminal?(state.left) && right.terminal?(state.right)
181
194
  end
182
195
  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.1
4
+ version: 0.5.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-11 00:00:00.000000000 Z
11
+ date: 2024-12-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Functional Event Sourcing Decider in Ruby
14
14
  email: