Skip to content

Instantly share code, notes, and snippets.

@jandudulski
Created November 16, 2024 23:31
Show Gist options
  • Save jandudulski/d69acfefd97fd58abb257f2c679d965f to your computer and use it in GitHub Desktop.
Save jandudulski/d69acfefd97fd58abb257f2c679d965f to your computer and use it in GitHub Desktop.
Bulb decider in Ruby
# 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