pavlos / gen_fsm

Elixir wrapper around OTP's gen_fsm
Other
40 stars 3 forks source link

Turnstile example for the docs #5

Closed gausby closed 8 years ago

gausby commented 8 years ago

I've been working on a turnstile example for the Readme. It implement the logic for a turnstile that accept money. When a coin is inserted it will go to unlocked and when it is entered it will go back to the locked state. There is a functionality to see and reset the number of coins that has been inserted.

  defmodule Turnstile do
    use GenFSM

    # Initialize the turnstile state machine with zero coins
    def start_link do
      GenFSM.start_link(__MODULE__, 0)
    end

    # Public API
    # The user can enter and insert_coins into our turnstile. The
    # outcome of these actions depends on the state of the turnstile
    # lock.
    def enter(pid) do
      GenFSM.send_event(pid, :enter)
    end
    def insert_coin(pid) do
      GenFSM.send_event(pid, :coin)
    end
    def empty(pid) do
      GenFSM.send_event(pid, :empty)
    end

    # Internal API
    def init(number_of_coins) do
      # Initialize the turnstile state machine in the locked state.
      # The state machine is initialized with a given number of
      # coins in its bank.
      {:ok, :locked, number_of_coins}
    end

    def unlocked(:coin, state) do
      # If another coin is inserted we will just eat the coin.
      {:next_state, :unlocked, state + 1}
    end
    def unlocked(:enter, state) do
      # One can enter the door when it is unlocked, but it will switch
      # state to locked when the door has been entered.
      {:next_state, :locked, state}
    end

    def locked(:coin, state) do
      # Increment the bank and set the state to unlocked, allowing
      # someone to enter.
      {:next_state, :unlocked, state + 1}
    end
    def locked(:enter, state) do
      # The door will not let anyone enter before it has been unlocked
      # by inserting a coin. Entering in this state will not to
      # anything.
      {:next_state, :locked, state}
    end
    def locked(:empty, state) do
      # The turnstile can be emptied when it is locked. This will reset
      # the bank to zero and print the number of coins (we do not take
      # authorization into consideration here). When the turnstile is
      # emptied it will remain in the locked state.
      IO.inspect state
      {:next_state, :locked, 0}
    end
  end

  # usage:
  # {:ok, pid} = Turnstile.start_link()
  #
  # Turnstile.enter(pid) # locked
  # Turnstile.insert_coin(pid) # has 1 coin
  # Turnstile.insert_coin(pid) # has 2 coins
  # Turnstile.insert_coin(pid) # has 3 coins
  # Turnstile.enter(pid) # unlocked
  # Turnstile.enter(pid) # locked
  # Turnstile.empty(pid) # yield 3 coins, has zero coins now

I had some trouble doing a sync call, but it could be due to my brain needs sleep right now. See if you like it and improve on it :)

pavlos commented 8 years ago

I'll try this out, but would you prefer to send me a pull request so that github gives you credit for the contribution?

gausby commented 8 years ago

Thanks. I can make it into a PR tomorrow. I got to catch some z's now.

Do you think the example is clear enough?

pavlos commented 8 years ago

This rocks. I'm going to close this issue since this PR (https://github.com/pavlos/gen_fsm/pull/4) addresses it

gausby commented 8 years ago

For the readme we could probably do a quick door example that can have locked/unlocked states and can be locked, unlocked and entered. I don't know if that would make for a shorter example. The turnstile could be moved to an examples/-folder.