decide.rb 0.2.0 → 0.3.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: 1d042a0810111e7fcb0f9226ad109b20903a8305689df72d25a04caaa17deeff
4
- data.tar.gz: d45f4d9aa5edc832dbdf303c8b0ba2e5aa6b7293c8c00614f9f8dfc69186c5c3
3
+ metadata.gz: da945c90e06dab8eb7c391135319bf4f9e126fd2e1904ac4481aabfc4df8943a
4
+ data.tar.gz: 9e70f93d1ee072b555f7a44b8babc2c0fb0bef50df27949d3bd45654b8c05ed8
5
5
  SHA512:
6
- metadata.gz: bf2b770bdabf04def99cbb942d6de633b00a9be6637adec05e81ad52228022e92e0cfc8e71e2e9ac12501febcdb411386242eb341d25e4d0be8f52b62f761ff6
7
- data.tar.gz: 614461548e7859a8524cbd6e29961790443e89a10e69d49ec690448af7204afb04a71bf984e8825de3d7b57496753e836e98c0b7ff9c4475d2715838ffaa0075
6
+ metadata.gz: 67a5f37bf266ce6ca5ca35820cc384fa502d33ca0dae794527d160dc7b247eb7e376c96f6dedfe204c348c25436d64db6c934d9fc407daf8e8421c38e23758bd
7
+ data.tar.gz: aea4f6f4de5a4509ce7b908fb04c59e6546f96c01502b762b564e5c8f44b7b235f380bf98631cf2f906d08eeaf210cde02564ce6060b5fbf9677fcbdd24b675f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # 0.3.0
2
+
3
+ * Rename `Decider.state` to `Decider.initial_state`
4
+ * Allow to use anything as state
5
+ * Use tuple-like array for composition
6
+ * Do not raise error when deciding unknown command
7
+ * Do not raise error when evolving unknown event
8
+
1
9
  # 0.2.0
2
10
 
3
11
  * Added `terminal?` function
data/README.md CHANGED
@@ -21,6 +21,16 @@ gem "decide.rb"
21
21
  ```ruby
22
22
  require "decider"
23
23
 
24
+ State = Data.define(:value) do
25
+ def max?
26
+ value >= MAX
27
+ end
28
+
29
+ def min?
30
+ value <= MIN
31
+ end
32
+ end
33
+
24
34
  module Commands
25
35
  Increase = Data.define
26
36
  Decrease = Data.define
@@ -36,16 +46,7 @@ MAX = 100
36
46
 
37
47
  ValueDecider = Decider.define do
38
48
  # define intial state
39
- state value: 0 do
40
- # you can define custom methods on state
41
- def max?
42
- value >= MAX
43
- end
44
-
45
- def min?
46
- value <= MIN
47
- end
48
- end
49
+ initial_state State.new(value: 0)
49
50
 
50
51
  # decide command with state
51
52
  decide Commands::Increase do |command, state|
@@ -89,8 +90,11 @@ new_state = events.reduce(state) { |state, event| ValueDecider.evolve(state, eve
89
90
  You can also compose deciders:
90
91
 
91
92
  ```ruby
93
+ Left = Data.define(:value)
94
+ Right = Data.define(:value)
95
+
92
96
  left = Decider.define do
93
- state value: 0
97
+ initial_state Left.new(value: 0)
94
98
 
95
99
  decide Commands::LeftCommand do |command, state|
96
100
  [Events::LeftEvent.new(value: command.value)]
@@ -106,7 +110,7 @@ left = Decider.define do
106
110
  end
107
111
 
108
112
  right = Decider.define do
109
- state value: 0
113
+ initial_state Right.new(value: 0)
110
114
 
111
115
  decide Commands::RightCommand do |command, state|
112
116
  [Events::RightEvent.new(value: command.value)]
@@ -124,7 +128,7 @@ end
124
128
  Composition = Decider.compose(left, right)
125
129
 
126
130
  state = Composition.initial_state
127
- #> #<data left=#<data value=0>, right=#<data value=0>>
131
+ #> #<data left=#<data Left value=0>, right=#<data Right value=0>>
128
132
 
129
133
  events = Composition.decide(Commands::LeftCommand.new(value: 1), state)
130
134
  #> [#<data value=1>]
@@ -0,0 +1,23 @@
1
+ # Decide
2
+
3
+ ## Handling unknown commands
4
+
5
+ ```ruby
6
+ decider = Decider.define do
7
+ initial_state :initial
8
+ end
9
+ ```
10
+
11
+ Return empty list of events (nothing changed) by default:
12
+
13
+ ```ruby
14
+ decider.decide :unknown, decider.initial_state
15
+ #> []
16
+ ```
17
+
18
+ Raise error when using `decide!`:
19
+
20
+ ```ruby
21
+ decider.decide! :unknown, decider.initial_state
22
+ #> raise ArgumentError, Unknown command
23
+ ```
@@ -0,0 +1,23 @@
1
+ # Evolve
2
+
3
+ ## Handling unknown events
4
+
5
+ ```ruby
6
+ decider = Decider.define do
7
+ initial_state :initial
8
+ end
9
+ ```
10
+
11
+ Return state (nothing changed) by default:
12
+
13
+ ```ruby
14
+ decider.evolve decider.initial_state, :unknown
15
+ #> :initial
16
+ ```
17
+
18
+ Raise error when using `evolve!`:
19
+
20
+ ```ruby
21
+ decider.evolve! decider.initial_state, :unknown
22
+ #> raise ArgumentError, Unknown event
23
+ ```
data/examples/state.md ADDED
@@ -0,0 +1,65 @@
1
+ # State examples
2
+
3
+ ## Data
4
+
5
+ ```ruby
6
+ State = Data.define(:value)
7
+
8
+ decider = Decider.define do
9
+ initial_state State.new(value: 0)
10
+
11
+ decide Commands::Command do |command, state|
12
+ [Events::Event.new(value: command.value)]
13
+ end
14
+
15
+ evolve Events::Event do |state, event|
16
+ state.with(value: state.value + 1)
17
+ end
18
+ end
19
+ ```
20
+
21
+ ## Primitive
22
+
23
+ ```ruby
24
+ decider = Decider.define do
25
+ initial_state :turned_off
26
+
27
+ decide Commands::TurnOn do |_command, state|
28
+ case state
29
+ in :turned_off
30
+ [Events::TurnedOn.new]
31
+ else
32
+ []
33
+ end
34
+ end
35
+
36
+ decide Commands::TurnOff do |_command, state|
37
+ case state
38
+ in :turned_on
39
+ [Events::TurnedOn.new]
40
+ else
41
+ []
42
+ end
43
+ end
44
+
45
+ evolve Events::TurnedOn do |_state, _event|
46
+ :turned_off
47
+ end
48
+ end
49
+ ```
50
+
51
+ ## List
52
+
53
+ ```ruby
54
+ decider = Decider.define do
55
+ initial_state []
56
+
57
+ decide Commands::Command do |command, _state|
58
+ [Events::Event.new(value: command.value)]
59
+ end
60
+
61
+ evolve Events::Event do |state, event|
62
+ state = state + [event]
63
+ end
64
+ end
65
+ ```
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Decider
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/decider.rb CHANGED
@@ -4,10 +4,34 @@ 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
10
+
11
+ def initialize(left, right)
12
+ super([left, right]).freeze
13
+ end
14
+
15
+ def with(left: BLANK, right: BLANK)
16
+ Composition.new(
17
+ (left == BLANK) ? self.left : left,
18
+ (right == BLANK) ? self.right : right
19
+ )
20
+ end
21
+
22
+ def left
23
+ self[0]
24
+ end
25
+
26
+ def right
27
+ self[1]
28
+ end
29
+ end
30
+
7
31
  class Module < ::Module
8
- def initialize(initial_state_args:, deciders:, evolvers:, terminal:)
32
+ def initialize(initial_state:, deciders:, evolvers:, terminal:)
9
33
  define_method(:initial_state) do
10
- new(**initial_state_args)
34
+ initial_state
11
35
  end
12
36
 
13
37
  define_method(:commands) do
@@ -18,7 +42,7 @@ module Decider
18
42
  evolvers.keys
19
43
  end
20
44
 
21
- define_method(:decide) do |command, state|
45
+ define_method(:decide!) do |command, state|
22
46
  handler = deciders.fetch(command.class) {
23
47
  raise ArgumentError, "Unknown command: #{command.class}"
24
48
  }
@@ -26,7 +50,15 @@ module Decider
26
50
  handler.call(command, state)
27
51
  end
28
52
 
29
- define_method(:evolve) do |state, event|
53
+ define_method(:decide) do |command, state|
54
+ handler = deciders.fetch(command.class) {
55
+ return []
56
+ }
57
+
58
+ handler.call(command, state)
59
+ end
60
+
61
+ define_method(:evolve!) do |state, event|
30
62
  handler = evolvers.fetch(event.class) {
31
63
  raise ArgumentError, "Unknown event: #{event.class}"
32
64
  }
@@ -34,6 +66,14 @@ module Decider
34
66
  handler.call(state, event)
35
67
  end
36
68
 
69
+ define_method(:evolve) do |state, event|
70
+ handler = evolvers.fetch(event.class) {
71
+ return state
72
+ }
73
+
74
+ handler.call(state, event)
75
+ end
76
+
37
77
  define_method(:terminal?) do |state|
38
78
  terminal.call(state)
39
79
  end
@@ -46,7 +86,7 @@ module Decider
46
86
  attr_reader :module
47
87
 
48
88
  def initialize
49
- @state = DEFAULT
89
+ @initial_state = DEFAULT
50
90
  @deciders = {}
51
91
  @evolvers = {}
52
92
  @terminal = ->(_state) { false }
@@ -55,29 +95,30 @@ module Decider
55
95
  def build(&block)
56
96
  instance_exec(&block) if block_given?
57
97
 
58
- raise StateNotDefined if @state == DEFAULT
98
+ raise StateNotDefined if @initial_state == DEFAULT
99
+
100
+ decider = Object.new
59
101
 
60
102
  @module = Module.new(
61
- initial_state_args: initial_state_args,
103
+ initial_state: @initial_state,
62
104
  deciders: deciders,
63
105
  evolvers: evolvers,
64
106
  terminal: terminal
65
107
  )
66
108
 
67
- @state.extend(@module)
109
+ decider.extend(@module)
68
110
 
69
- @state
111
+ decider
70
112
  end
71
113
 
72
114
  private
73
115
 
74
- attr_reader :initial_state_args, :deciders, :evolvers, :terminal
116
+ attr_reader :deciders, :evolvers, :terminal
75
117
 
76
- def state(**kwargs, &block)
77
- raise StateAlreadyDefined if @state != DEFAULT
118
+ def initial_state(state)
119
+ raise StateAlreadyDefined if @initial_state != DEFAULT
78
120
 
79
- @state = Data.define(*kwargs.keys, &block)
80
- @initial_state_args = kwargs
121
+ @initial_state = state
81
122
  end
82
123
 
83
124
  def decide(command, &block)
@@ -101,7 +142,7 @@ module Decider
101
142
 
102
143
  def self.compose(left, right)
103
144
  define do
104
- state left: left.initial_state, right: right.initial_state
145
+ initial_state Composition.new(left.initial_state, right.initial_state)
105
146
 
106
147
  left.commands.each do |klass|
107
148
  decide klass do |command, state|
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.2.0
4
+ version: 0.3.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-04 00:00:00.000000000 Z
11
+ date: 2024-11-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Functional Event Sourcing Decider in Ruby
14
14
  email:
@@ -23,6 +23,9 @@ files:
23
23
  - README.md
24
24
  - Rakefile
25
25
  - Steepfile
26
+ - examples/decide.md
27
+ - examples/evolve.md
28
+ - examples/state.md
26
29
  - lib/decider.rb
27
30
  - lib/decider/version.rb
28
31
  - sig/decider.rbs