Mix.install([
{:youtube, github: "brooklinjazz/youtube"},
{:hidden_cell, github: "brooklinjazz/hidden_cell"},
{:tested_cell, github: "brooklinjazz/tested_cell"},
{:utils, path: "#{__DIR__}/../utils"}
])
You're going to create a TrafficLightServer
GenServer that mimics a traffic light.
- Create a
TrafficLightServer
which stores the state of the traffic light:red
,:green
, or:yellow
. It should start as:green
. - Define a
current_light/1
function which returns the current light color. - Define a
transition/1
function which transitions the current light from its current color to the next color. - Prefer
call
overcast
to avoid concurrency issues.
flowchart LR
Green --> Yellow --> Red --> Green
style Green fill:lightgreen
style Yellow fill:lightyellow
style Red fill:lightcoral
For example:
{:ok, pid} = TrafficLightServer.start_link([])
:green = TrafficLightServer.current_light(pid)
:yellow = TrafficLightServer.transition(pid)
:yellow = TrafficLightServer.current_light(pid)
:red = TrafficLightServer.transition(pid)
:red = TrafficLightServer.current_light(pid)
:green = TrafficLightServer.transition(pid)
:green = TrafficLightServer.current_light(pid)
Enter your answer in the Elixir cell below.
ExUnit.start(auto_run: false)
defmodule Assertion do
use ExUnit.Case
test "Traffic Light Server Exercise" do
try do
Process.flag(:trap_exit, true)
defmodule TrafficLightServer do
end
assert Kernel.function_exported?(TrafficLightServer, :start_link, 1),
"Ensure you define a start_link/1 function which accepts a list of options."
assert Kernel.function_exported?(TrafficLightServer, :init, 1),
"Ensure you define an init/1 function which returns `{:ok, initial_state}`"
assert {:ok, pid} = TrafficLightServer.start_link([])
assert Kernel.function_exported?(TrafficLightServer, :current_light, 1),
"Ensure you define a current_light/1 function which accepts a pid"
assert :green == TrafficLightServer.current_light(pid)
assert Kernel.function_exported?(TrafficLightServer, :transition, 1),
"Ensure you define a transition/1 function which accepts a pid"
TrafficLightServer.transition(pid)
assert :yellow == TrafficLightServer.current_light(pid)
TrafficLightServer.transition(pid)
assert :red == TrafficLightServer.current_light(pid)
TrafficLightServer.transition(pid)
assert :green == TrafficLightServer.current_light(pid)
catch
error ->
flunk("""
Your solution threw the following error:
#{inspect(error)}
""")
:exit, {error, {GenServer, message_type, [_pid, message, _timeout]}} ->
flunk("""
GenServer crashed with the following error:
#{inspect(error)}
When it received: #{inspect(message)} #{message_type}
Likely you need to define the corresponding handler for #{inspect(message)}.
Ensure you defined a `handle_call/3`, `handle_info/2`, or `handle_cast/2` or appropriate handler function.
def handle_call(:message, _from, state) do
...
end
Then ensure you call [GenServer.call/2](https://hexdocs.pm/elixir/GenServer.html#call/2), [GenServer.cast/2](https://hexdocs.pm/elixir/GenServer.html#cast/2), or otherwise send the message correctly.
GenServer.call(pid, :message)
""")
:exit, error ->
flunk("""
Unhandled exit with the following error:
#{inspect(error)}
""")
after
# all warnings and errors are printed to the previous Kino Frame
# to avoid cluttering the test results display.
Process.sleep(10)
Kino.render(Kino.Markdown.new("### Test Results
<hr/>"))
end
end
end
ExUnit.run()
# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
# Unfortunately, this results in duplication of warnings.
defmodule TrafficLightServer do
end
You may find the following commented cell useful for testing your solution.
# {:ok, pid} = TrafficLightServer.start_link([])
# :green = TrafficLightServer.current_light(pid)
# :yellow = TrafficLightServer.transition(pid)
# :yellow = TrafficLightServer.current_light(pid)
# :red = TrafficLightServer.transition(pid)
# :red = TrafficLightServer.current_light(pid)
# :green = TrafficLightServer.transition(pid)
# :green = TrafficLightServer.current_light(pid)
You're going to create a TrafficGrid
GenServer that manages five TrafficLightServers
.
- Create a
TrafficGrid
which stores the pids of fiveTrafficLightServers
. - Define a
current_lights/1
function that returns the current light of eachTrafficLightServer
. - Define a
transition/1
function that transitions one of the five lights (in order, from beginning to end). - Prefer
call
overcast
to avoid concurrency issues.
flowchart
TG[TrafficGrid]
TLS1[TrafficLightServer]
TLS2[TrafficLightServer]
TLS3[TrafficLightServer]
TLS4[TrafficLightServer]
TLS5[TrafficLightServer]
G1[Green]
G2[Green]
G3[Green]
G4[Green]
G5[Green]
Y1[Yellow]
Y2[Yellow]
Y3[Yellow]
Y4[Yellow]
Y5[Yellow]
R1[Red]
R2[Red]
R3[Red]
R4[Red]
R5[Red]
TG --> TLS1
TG --> TLS2
TG --> TLS3
TG --> TLS4
TG --> TLS5
TLS1 --> G1 --> Y1 --> R1 --> G1
TLS2 --> G2 --> Y2 --> R2 --> G2
TLS3 --> G3 --> Y3 --> R3 --> G3
TLS4 --> G4 --> Y4 --> R4 --> G4
TLS5 --> G5 --> Y5 --> R5 --> G5
style G1 fill:lightgreen
style G2 fill:lightgreen
style G3 fill:lightgreen
style G4 fill:lightgreen
style G5 fill:lightgreen
style Y1 fill:lightyellow
style Y2 fill:lightyellow
style Y3 fill:lightyellow
style Y4 fill:lightyellow
style Y5 fill:lightyellow
style R1 fill:lightcoral
style R2 fill:lightcoral
style R3 fill:lightcoral
style R4 fill:lightcoral
style R5 fill:lightcoral
For example:
{:ok, pid} = TrafficGrid.start_link([])
# all lights start green
[:green, :green, :green, :green, :green] = TrafficGrid.current_lights(pid)
# transition the first light from green to yellow
[:yellow, :green, :green, :green, :green] = TrafficGrid.transition(pid)
[:yellow, :green, :green, :green, :green] = TrafficGrid.current_lights(pid)
# transition the second light from green to yellow
[:yellow, :yellow, :green, :green, :green] = TrafficGrid.transition(pid)
[:yellow, :yellow, :green, :green, :green] = TrafficGrid.current_lights(pid)
# transition the third light from green to yellow
[:yellow, :yellow, :yellow, :green, :green] = TrafficGrid.transition(pid)
[:yellow, :yellow, :yellow, :green, :green] = TrafficGrid.current_lights(pid)
# transition the fourth light from green to yellow
[:yellow, :yellow, :yellow, :yellow, :green] = TrafficGrid.transition(pid)
[:yellow, :yellow, :yellow, :yellow, :green] = TrafficGrid.current_lights(pid)
# transition the fifth light from green to yellow
[:yellow, :yellow, :yellow, :yellow, :yellow] = TrafficGrid.transition(pid)
[:yellow, :yellow, :yellow, :yellow, :yellow] = TrafficGrid.current_lights(pid)
# transition the first light from yellow to red
[:red, :yellow, :yellow, :yellow, :yellow] = TrafficGrid.transition(pid)
[:red, :yellow, :yellow, :yellow, :yellow] = TrafficGrid.current_lights(pid)
# ... etc
Enter your answer in the Elixir cell below.
ExUnit.start(auto_run: false)
defmodule Assertion do
use ExUnit.Case
test "Traffic Grid Exercise" do
try do
Process.flag(:trap_exit, true)
defmodule TrafficGrid do
end
assert Kernel.function_exported?(TrafficGrid, :start_link, 1),
"Ensure you define a start_link/1 function which accepts a list of options."
assert Kernel.function_exported?(TrafficGrid, :init, 1),
"Ensure you define an init/1 function which returns `{:ok, initial_state}`"
assert {:ok, pid} = TrafficGrid.start_link([])
assert Kernel.function_exported?(TrafficGrid, :current_lights, 1),
"Ensure you define a current_lights/1 function which accepts a pid"
assert [:green, :green, :green, :green, :green] == TrafficGrid.current_lights(pid)
assert Kernel.function_exported?(TrafficGrid, :transition, 1),
"Ensure you define a transition/1 function which accepts a pid"
assert [:yellow, :green, :green, :green, :green] == TrafficGrid.transition(pid)
assert [:yellow, :green, :green, :green, :green] == TrafficGrid.current_lights(pid)
assert [:yellow, :yellow, :green, :green, :green] == TrafficGrid.transition(pid)
assert [:yellow, :yellow, :green, :green, :green] == TrafficGrid.current_lights(pid)
assert [:yellow, :yellow, :yellow, :green, :green] == TrafficGrid.transition(pid)
assert [:yellow, :yellow, :yellow, :green, :green] == TrafficGrid.current_lights(pid)
assert [:yellow, :yellow, :yellow, :yellow, :green] == TrafficGrid.transition(pid)
assert [:yellow, :yellow, :yellow, :yellow, :green] == TrafficGrid.current_lights(pid)
assert [:yellow, :yellow, :yellow, :yellow, :yellow] == TrafficGrid.transition(pid)
assert [:yellow, :yellow, :yellow, :yellow, :yellow] == TrafficGrid.current_lights(pid)
assert [:red, :yellow, :yellow, :yellow, :yellow] == TrafficGrid.transition(pid)
assert [:red, :yellow, :yellow, :yellow, :yellow] == TrafficGrid.current_lights(pid)
assert [:red, :red, :yellow, :yellow, :yellow] == TrafficGrid.transition(pid)
assert [:red, :red, :yellow, :yellow, :yellow] == TrafficGrid.current_lights(pid)
assert [:red, :red, :red, :yellow, :yellow] == TrafficGrid.transition(pid)
assert [:red, :red, :red, :yellow, :yellow] == TrafficGrid.current_lights(pid)
assert [:red, :red, :red, :red, :yellow] == TrafficGrid.transition(pid)
assert [:red, :red, :red, :red, :yellow] == TrafficGrid.current_lights(pid)
assert [:red, :red, :red, :red, :red] == TrafficGrid.transition(pid)
assert [:red, :red, :red, :red, :red] == TrafficGrid.current_lights(pid)
assert [:green, :red, :red, :red, :red] == TrafficGrid.transition(pid)
assert [:green, :red, :red, :red, :red] == TrafficGrid.current_lights(pid)
catch
error ->
flunk("""
Your solution threw the following error:
#{inspect(error)}
""")
:exit, {error, {GenServer, message_type, [_pid, message, _timeout]}} ->
flunk("""
GenServer crashed with the following error:
#{inspect(error)}
When it received: #{inspect(message)} #{message_type}
Likely you need to define the corresponding handler for #{inspect(message)}.
Ensure you defined a `handle_call/3`, `handle_info/2`, or `handle_cast/2` or appropriate handler function.
def handle_call(:message, _from, state) do
...
end
Then ensure you call [GenServer.call/2](https://hexdocs.pm/elixir/GenServer.html#call/2), [GenServer.cast/2](https://hexdocs.pm/elixir/GenServer.html#cast/2), or otherwise send the message correctly.
GenServer.call(pid, :message)
""")
:exit, error ->
flunk("""
Unhandled exit with the following error:
#{inspect(error)}
""")
after
# all warnings and errors are printed to the previous Kino Frame
# to avoid cluttering the test results display.
Process.sleep(10)
Kino.render(Kino.Markdown.new("### Test Results
<hr/>"))
end
end
end
ExUnit.run()
# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
# Unfortunately, this results in duplication of warnings.
defmodule TrafficGrid do
end
You may find the following commented cell useful for testing your solution.
# {:ok, pid} = TrafficGrid.start_link([])
# [:green, :green, :green, :green, :green] == TrafficGrid.current_lights(pid)
# [:yellow, :green, :green, :green, :green] == TrafficGrid.transition(pid)
# [:yellow, :green, :green, :green, :green] == TrafficGrid.current_lights(pid)
# [:yellow, :yellow, :green, :green, :green] == TrafficGrid.transition(pid)
# [:yellow, :yellow, :green, :green, :green] == TrafficGrid.current_lights(pid)
# [:yellow, :yellow, :yellow, :green, :green] == TrafficGrid.transition(pid)
# [:yellow, :yellow, :yellow, :green, :green] == TrafficGrid.current_lights(pid)
# [:yellow, :yellow, :yellow, :yellow, :green] == TrafficGrid.transition(pid)
# [:yellow, :yellow, :yellow, :yellow, :green] == TrafficGrid.current_lights(pid)
# [:yellow, :yellow, :yellow, :yellow, :yellow] == TrafficGrid.transition(pid)
# [:yellow, :yellow, :yellow, :yellow, :yellow] == TrafficGrid.current_lights(pid)
# [:red, :yellow, :yellow, :yellow, :yellow] == TrafficGrid.transition(pid)
# [:red, :yellow, :yellow, :yellow, :yellow] == TrafficGrid.current_lights(pid)
# [:red, :red, :yellow, :yellow, :yellow] == TrafficGrid.transition(pid)
# [:red, :red, :yellow, :yellow, :yellow] == TrafficGrid.current_lights(pid)
# [:red, :red, :red, :yellow, :yellow] == TrafficGrid.transition(pid)
# [:red, :red, :red, :yellow, :yellow] == TrafficGrid.current_lights(pid)
# [:red, :red, :red, :red, :yellow] == TrafficGrid.transition(pid)
# [:red, :red, :red, :red, :yellow] == TrafficGrid.current_lights(pid)
# [:red, :red, :red, :red, :red] == TrafficGrid.transition(pid)
# [:red, :red, :red, :red, :red] == TrafficGrid.current_lights(pid)
# [:green, :red, :red, :red, :red] == TrafficGrid.transition(pid)
# [:green, :red, :red, :red, :red] == TrafficGrid.current_lights(pid)
Run the following in your command line from the beta_curriculum folder to track and save your progress in a Git commit.
$ git add .
$ git commit -m "finish traffic light server exercise"
Previous | Next |
---|---|
Generic Server | Rock Paper Scissors GenServer |