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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 560335da25429790af3eb660d743eb7233c3dad032f9582462de1142c9cf3d91
4
- data.tar.gz: c42275236a358e3eac7189cf6e7f57cdc3106aa9e00f203b96d139f24360ee91
3
+ metadata.gz: ae980af0098467da425746f3d2cb9cf41eb67301dea7b369d36ddbba09357414
4
+ data.tar.gz: 42b2d4110c43de9aa88c33704caf9e53ca9f3bdc346ea0c5108ffe3f34c35d2d
5
5
  SHA512:
6
- metadata.gz: 0da501705ecb75d91b1696f468c9360bc950e7cc0f5ac1ce52a3427beccb2bba8eca1688bee73e874c46c40f17d579ddde4b132a2e6fe04e3737b7091f99619d
7
- data.tar.gz: ea55c13433fb463d9746da6fe08016353bc0d38c3aae988f0b7249cfac86ed5325b0bcbb02c3833e5997f43db61f7b293a822988a851273dc39b087d7e4b2e14
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)
@@ -1,141 +1,141 @@
1
1
  # frozen_string_literal: true
2
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)
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
- context.instance_exec(&handler)
29
- context._actions
30
- end
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
- define_method(:lmap_on_action_result) do |fn|
33
- Decider::Reactor.lmap_on_action_result(fn, self)
34
- end
33
+ define_method(:lmap_on_action_result) do |fn|
34
+ Decider::Reactor.lmap_on_action_result(fn, self)
35
+ end
35
36
 
36
- define_method(:rmap_on_action) do |fn|
37
- Decider::Reactor.rmap_on_action(fn, self)
38
- end
37
+ define_method(:rmap_on_action) do |fn|
38
+ Decider::Reactor.rmap_on_action(fn, self)
39
+ end
39
40
 
40
- define_method(:map_on_action) do |fn|
41
- Decider::Reactor.rmap_on_action(fn, self)
42
- end
41
+ define_method(:map_on_action) do |fn|
42
+ Decider::Reactor.rmap_on_action(fn, self)
43
+ end
43
44
 
44
- define_method(:combine_with_decider) do |decider|
45
- Decider::Reactor.combine_with_decider(self, decider)
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
- class Builder
51
- DEFAULT = Object.new
51
+ class Builder
52
+ DEFAULT = Object.new
52
53
 
53
- attr_reader :module
54
-
55
- def initialize
56
- @reactions = {}
57
- end
54
+ def initialize
55
+ @reactions = {}
56
+ end
58
57
 
59
- def build(&block)
60
- instance_exec(&block) if block_given?
58
+ def build(&block)
59
+ instance_exec(&block) if block_given?
61
60
 
62
- reactor = Class.new
61
+ reactor = Class.new
63
62
 
64
- @module = Module.new(
65
- reactions: reactions
66
- )
63
+ mod = Module.new(
64
+ reactions: reactions
65
+ )
67
66
 
68
- reactor.extend(@module)
67
+ reactor.extend(mod)
69
68
 
70
- reactor
71
- end
69
+ reactor
70
+ end
72
71
 
73
- private
72
+ private
74
73
 
75
- attr_reader :reactions
74
+ attr_reader :reactions
76
75
 
77
- def react(arg, &block)
78
- reactions[arg] = block
76
+ def react(arg, &block)
77
+ reactions[arg] = block
78
+ end
79
79
  end
80
- end
81
- private_constant :Builder
80
+ private_constant :Builder
82
81
 
83
- def self.define(&block)
84
- builder = Builder.new
85
- builder.build(&block)
86
- end
82
+ def self.define(&block)
83
+ builder = Builder.new
84
+ builder.build(&block)
85
+ end
87
86
 
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
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
- 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)
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
- 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
- }
107
+ def self.map_on_action(fn, reactor)
108
+ rmap_on_action(fn, reactor)
109
+ end
129
110
 
130
- fn.call([command], [], state).each { |event| emit event }
131
- end
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
- evolve proc { true } do
134
- decider.evolve(state, event)
135
- end
132
+ evolve proc { true } do
133
+ decider.evolve(state, event)
134
+ end
136
135
 
137
- terminal? do
138
- decider.terminal?(state)
136
+ terminal? do
137
+ decider.terminal?(state)
138
+ end
139
139
  end
140
140
  end
141
141
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Decider
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.0"
5
5
  end
@@ -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
- require "decider/reactor"
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
- @module = Module.new(
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(@module)
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.7.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: 2025-07-14 00:00:00.000000000 Z
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.2
103
+ rubygems_version: 3.6.9
103
104
  specification_version: 4
104
105
  summary: Functional Event Sourcing Decider in Ruby
105
106
  test_files: []