decide.rb 0.2.0 → 0.3.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: 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