Skip to content

Instantly share code, notes, and snippets.

@jandudulski
Created November 16, 2024 23:31
Show Gist options
  • Select an option

  • Save jandudulski/d69acfefd97fd58abb257f2c679d965f to your computer and use it in GitHub Desktop.

Select an option

Save jandudulski/d69acfefd97fd58abb257f2c679d965f to your computer and use it in GitHub Desktop.

Revisions

  1. jandudulski created this gist Nov 16, 2024.
    179 changes: 179 additions & 0 deletions example.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,179 @@
    # 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