Created
November 16, 2024 23:31
-
-
Save jandudulski/d69acfefd97fd58abb257f2c679d965f to your computer and use it in GitHub Desktop.
Bulb decider in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Example based on https://github.com/thinkbeforecoding/dddeu-2023-deciders/blob/main/run.fsx | |
require "bundler/inline" | |
gemfile do | |
source "https://rubygems.org" | |
gem "minitest" | |
gem "decide.rb", "~> 0.4.1" | |
end | |
require "minitest/autorun" | |
require "decider" | |
module Bulb | |
module Commands | |
Fit = Data.define(:max_uses) | |
SwitchOn = Data.define | |
SwitchOff = Data.define | |
end | |
module Events | |
Fitted = Data.define(:max_uses) | |
SwitchedOn = Data.define | |
SwitchedOff = Data.define | |
Blew = Data.define | |
end | |
module States | |
NotFitted = Data.define | |
Working = Data.define(:status, :remaining_uses) | |
Blown = Data.define | |
end | |
Decider = ::Decider.define do | |
initial_state States::NotFitted.new | |
terminal? { _1.is_a? Blown } | |
decide Commands::Fit do |command, state| | |
case state | |
in States::NotFitted | |
[Events::Fitted.new(command.max_uses)] | |
else | |
raise "Bulb has already been fitted" | |
end | |
end | |
decide Commands::SwitchOn do |command, state| | |
case state | |
in States::Working(status: :off, remaining_uses:) if remaining_uses > 0 | |
[Events::SwitchedOn.new] | |
in States::Working(status: :off) | |
[Events::Blew.new] | |
else | |
[] | |
end | |
end | |
decide Commands::SwitchOff do |command, state| | |
case state | |
in States::Working(status: :on) | |
[Events::SwitchedOff.new] | |
else | |
[] | |
end | |
end | |
evolve Events::Fitted do |state, event| | |
States::Working.new(status: :off, remaining_uses: event.max_uses) | |
end | |
evolve Events::SwitchedOn do |state, event| | |
state.with(status: :on, remaining_uses: state.remaining_uses - 1) | |
end | |
evolve Events::SwitchedOff do |state, event| | |
state.with(status: :off) | |
end | |
evolve Events::Blew do |state, event| | |
States::Blown.new | |
end | |
end | |
end | |
class TestDecide < Minitest::Test | |
def decider = Bulb::Decider | |
def given(events) | |
@state = events.reduce(decider.initial_state, &decider.method(:evolve)) | |
self | |
end | |
def when(command) | |
@events = decider.decide(command, @state) | |
self | |
end | |
def expect(expected) | |
assert_equal(expected, @events) | |
end | |
def test_fit | |
given( | |
[] | |
).when( | |
Bulb::Commands::Fit.new(max_uses: 5) | |
).expect( | |
[Bulb::Events::Fitted.new(max_uses: 5)] | |
) | |
end | |
def test_switch_on | |
given( | |
[Bulb::Events::Fitted.new(max_uses: 5)] | |
).when( | |
Bulb::Commands::SwitchOn.new | |
).expect( | |
[Bulb::Events::SwitchedOn.new] | |
) | |
end | |
def test_switch_on_already_on | |
given( | |
[ | |
Bulb::Events::Fitted.new(max_uses: 5), | |
Bulb::Events::SwitchedOn.new | |
] | |
).when( | |
Bulb::Commands::SwitchOn.new | |
).expect( | |
[] | |
) | |
end | |
def test_switch_off | |
given( | |
[ | |
Bulb::Events::Fitted.new(max_uses: 5), | |
Bulb::Events::SwitchedOn.new | |
] | |
).when( | |
Bulb::Commands::SwitchOff.new | |
).expect( | |
[Bulb::Events::SwitchedOff.new] | |
) | |
end | |
def test_switch_off_already_off | |
given( | |
[ | |
Bulb::Events::Fitted.new(max_uses: 5), | |
Bulb::Events::SwitchedOn.new, | |
Bulb::Events::SwitchedOff.new | |
] | |
).when( | |
Bulb::Commands::SwitchOff.new | |
).expect( | |
[] | |
) | |
end | |
def test_blew | |
given( | |
[ | |
Bulb::Events::Fitted.new(max_uses: 1), | |
Bulb::Events::SwitchedOn.new, | |
Bulb::Events::SwitchedOff.new | |
] | |
).when( | |
Bulb::Commands::SwitchOn.new | |
).expect( | |
[Bulb::Events::Blew.new] | |
) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment