- Rename gem to
decider - Support
manyinView
Big Thanks to skalnik for transfering the name ❤️
- Fix support for Ruby > 3.4.5
- Add view that can evolve from initial state
view = Decider::View.define do
initial_state 0
evolve :increased do
state + 1
end
evolve :decreased do
state - 1
end
end- Add
lmap_on_event,dimap_on_state,lmap_on_stateandrmap_on_stateextensions for view
- Add reactor that can react to action results and issue actions
ActionResult = Data.define(:value)
Action = Data.define(:value)
reactor = Decider::Reactor.define do
react :action_result do
issue :action
issue :another_action
end
react proc { action_result in ActionResult(value: 42) } do
issue Action.new(value: "the answer")
end
react ActionResult do
issue Action.new(value: action_result.value)
end
end
reactor.react(:action_result)
# => [:action, :another_action]
reactor.react(ActionResult.new(value: 42)
# => #<data Action value="the answer">
reactor.react(ActionResult.new(value: 1)
# => #<data Action value=1>- Add
lmap_on_action_resultextension to reactor - Add
rmap_on_action(aliased tomap_on_action) extensions to reactor - Add
combine_with_deciderextension to reactor
decider = Decider.define do
initial_state 0
decide :action do
emit :result
end
decide :another_action do
emit :another_result
end
end
reactor = Decider::Reactor.define do
react :result do
issue :another_action
end
end
decider = reactor.combine_with_decider(decider)
decider.decide(:action)
# => [:result, :another_result]- Add
manyextension that takes a decider and manage many instances
deciders = Decider.many(decider)
# or
deciders = decider.many
deciders.initial_state
# => {}
deciders.decide([id, command], state)
# => [[id, event], [id, event]]
deciders.evolve(state, [id, event])
# => state- Add
applyextension for creating applicatives
decider = Decider.map(fn.curry, deciderx)
decider = Decider.apply(decider, decidery)
decider = Decider.apply(decider, deciderz)
# or
deciderx.map(fn.curry).apply(decidery).apply(deciderz)- All extensions takes decider as last argument
# Before
Decider.dimap_on_state(decider, fl:, fr:)
Decider.dimap_on_event(decider, fl:, fr:)
Decider.lmap_on_event(decider, fn)
Decider.lmap_on_command(decider, fn)
Decider.lmap_on_state(decider, fn)
Decider.rmap_on_event(decider, fn)
Decider.rmap_on_state(decider, fn)
# After
Decider.dimap_on_state(fl, fr, decider)
Decider.dimap_on_event(fl, fr, decider)
Decider.lmap_on_event(fn, decider)
Decider.lmap_on_command(fn, decider)
Decider.lmap_on_state(fn, decider)
Decider.rmap_on_event(fn, decider)
Decider.rmap_on_state(fn, decider)- Add
lmap_on_commandextension that takes proc that maps command and returns a new decider
decider = Decider.define do
initial_state 0
decide :increase do
emit :increased
end
end
lmap = decider.lmap_on_command(->(command) { command.to_sym })
lmap.decide("increase", 0)
# => [:increased]- Add
mapextension that works the same way asrmap_on_statebutDecider.maptakes function first
# equivalent
Decider.rmap_on_state(decider, fn)
Decider.map(fn, decider)
decider.rmap_on_state(fn)
decider.map(fn)- Add
map2extension that takes function with two arguments and two deciders and returns a decider:
dx = Decider.define do
initial_state({score: 0})
decide :score do
emit :scored
end
evolve :scored do
{score: state[:score] + 1}
end
end
dy = Decider.define do
initial_state({time: 0})
decide :tick do
emit :ticked
end
evolve :ticked do
{time: state[:time] + 1}
end
end
decider = Decider.map2(
->(sx, sy) { {score: sx[:score], time: sy[:time]} }, dx, dy
)
decider.initial_state
# => {score: 0, time: 0}
decider.decide(:score, decider.initial_state)
# => [:scored]
decider.decide(:tick, decider.initial_state)
# => [:ticked]
decider.evolve(decider.initial_state, :scored)
# => {score: 1, time: 0}
decider.evolve(decider.initial_state, :ticked)
# => {score: 0, time: 1}- Add
lmap_on_eventandrmap_on_eventextensions that takes proc that maps event in or out and returns a new decider
decider = Decider.define do
initial_state 0
decide :increase do
emit :increased
end
evolve :increased do
state + 1
end
end
lmap = decider.lmap_on_event(->(event) { event.to_sym })
lmap.evolve(0, "increased")
# => 1
rmap = decider.rmap_on_event(->(event) { event.to_s })
rmap.decide(:increase, 0)
# => "increased"- Add
lmap_on_stateandrmap_on_stateextensions that takes proc that maps state in or out and returns a new decider
decider = Decider.define do
initial_state :symbol
decide :command, :state do
emit :called
end
evolve :state, :called do
:new_state
end
end
decider.initial_state
# => :symbol
lmap = decider.lmap_on_state(
->(state) { state.to_sym }
)
lmap.initial_state
# => :symbol
lmap.decide(:command, "symbol")
# => [:called]
lmap.evolve("symbol", :called)
# => :new_state
rmap = inner.rmap_on_state(
->(state) { state.to_s }
)
rmap.initial_state
# => "symbol"
rmap.decide(:command, :symbol)
# => [:called]
rmap.evolve(:symbol, :called)
# => "new_state"- Add shortcut for evolving:
# this three are the same now
[event, event].reduce(state) { |s, e| decider.evolve(s, e) }
[event, event].reduce(state, &decider.method(:evolve))
# new
[event, event].reduce(state, &decider.evolve)- Add
dimap_on_eventextension that takes procs that maps event in and out and returns a new decider
inner = Decider.define do
initial_state 0
decide :increase do
emit :increased
end
evolve :increased do
state + 1
end
end
outer = inner.dimap_on_event(
fl: ->(event) { event.to_sym },
fr: ->(event) { event.to_s }
)
outer.decide(:increase, 0)
# => "increased"
outer.evolve(0, "increased")
# => 1- Add
dimap_on_stateextension that takes procs that maps state in and out and returns a new decider
inner = Decider.define do
initial_state :symbol
end
inner.initial_state
# => :symbol
outer = inner.dimap_on_state(
fl: ->(state) { state.to_sym },
fr: ->(state) { state.to_s }
)
outer.initial_state
# => "symbol"
# under the hood it will run inner.decide(:command, :symbol)
outer.decide(:command, "symbol")- Support pattern matching for commands and events
- Support passing state to decider and evolve matchers
- Remove explicit arguments for handlers
- Remove redundant bang methods - raise error in catch-all if needed
- Add Left|Right value wrappers for composition
- Use
emitto return events indecide
definereturns new class, not object
- Accept more data structures for commands and events
- Rename
Decider.statetoDecider.initial_state - Allow to use anything as state
- Use tuple-like array for composition
- Do not raise error when deciding unknown command
- Do not raise error when evolving unknown event
- Added
terminal?function Decider.compose(left, right)
- Initial release