diff --git a/lib/display.ex b/lib/display.ex index 47f04e8..13667b5 100644 --- a/lib/display.ex +++ b/lib/display.ex @@ -16,34 +16,6 @@ defmodule Display do {:noreply, %{state | clear_screen: false}} end - def handle_cast({:invalid, koan, modules}, state) do - Notifications.invalid_koan(koan, modules) - |> IO.puts - - {:noreply, state} - end - - def handle_cast({:show_failure, failure, module, name}, state) do - format(failure, module, name) - |> IO.puts - - {:noreply, state} - end - - def handle_cast({:show_compile_error, error}, state) do - Failure.show_compile_error(error) - |> IO.puts - - {:noreply, state} - end - - def handle_cast(:congratulate, state) do - Notifications.congratulate - |> IO.puts - - {:noreply, state} - end - def handle_cast(:clear_screen, state = %{clear_screen: true}) do IO.puts(ANSI.clear) IO.puts(ANSI.home) @@ -55,19 +27,23 @@ defmodule Display do end def invalid_koan(koan, modules) do - GenServer.cast(__MODULE__, {:invalid, koan, modules}) + Notifications.invalid_koan(koan, modules) + |> IO.puts end def show_failure(failure, module, name) do - GenServer.cast(__MODULE__, {:show_failure, failure, module, name}) + format(failure, module, name) + |> IO.puts end def show_compile_error(error) do - GenServer.cast(__MODULE__, {:show_compile_error, error}) + Failure.show_compile_error(error) + |> IO.puts end def congratulate do - GenServer.cast(__MODULE__, :congratulate) + Notifications.congratulate + |> IO.puts end def clear_screen do diff --git a/lib/elixir_koans.ex b/lib/elixir_koans.ex index 904967a..c535dd7 100644 --- a/lib/elixir_koans.ex +++ b/lib/elixir_koans.ex @@ -7,7 +7,9 @@ defmodule ElixirKoans do children = [ worker(Display, []), - worker(Tracker, []) + worker(Tracker, []), + worker(Runner, []), + worker(Watcher, []) ] opts = [strategy: :one_for_one, name: ElixirKoans.Supervisor] diff --git a/lib/meditate.ex b/lib/meditate.ex index ba529f5..8830602 100644 --- a/lib/meditate.ex +++ b/lib/meditate.ex @@ -7,8 +7,6 @@ defmodule Mix.Tasks.Meditate do def run(args) do Application.ensure_all_started(:elixir_koans) Code.compiler_options(ignore_module_conflict: true) - {:ok, watcher} = Watcher.start - Process.monitor(watcher) Options.start(args) @@ -22,17 +20,12 @@ defmodule Mix.Tasks.Meditate do |> Runner.modules_to_run Tracker.set_total(modules) + Tracker.notify_on_complete(self()) Runner.run(modules) - if Tracker.complete? do - Display.congratulate - exit(:normal) - end - - receive do - {:DOWN, _references, :process, ^watcher, _reason} -> nil - end + Tracker.wait_until_complete() + Display.congratulate() end defp ok?(koan) do diff --git a/lib/runner.ex b/lib/runner.ex index cfe7dfb..d082e6f 100644 --- a/lib/runner.ex +++ b/lib/runner.ex @@ -1,4 +1,5 @@ defmodule Runner do + use GenServer def koan?(koan) do Keyword.has_key?(koan.__info__(:exports), :all_koans) @@ -27,14 +28,38 @@ defmodule Runner do def modules_to_run, do: Options.initial_koan |> modules_to_run def modules_to_run(start_module), do: Enum.drop_while(modules(), &(&1 != start_module)) - def run(modules) do - Display.clear_screen() - - modules - |> Enum.take_while( &(run_module(&1) == :passed)) + def start_link do + GenServer.start_link(__MODULE__, [], name: __MODULE__) end - def run_module(module) do + def handle_cast({:run, modules}, _) do + flush() + send(self(), :run_modules) + {:noreply, modules} + end + + def handle_info(:run_modules, []) do + {:noreply, []} + end + + def handle_info(:run_modules, [module | rest]) do + Display.clear_screen() + + case run_module(module) do + :passed -> + send(self(), :run_modules) + {:noreply, rest} + + _ -> + {:noreply, []} + end + end + + def run(modules) do + GenServer.cast(__MODULE__, {:run, modules}) + end + + defp run_module(module) do module |> Execute.run_module(&track/3) |> display @@ -48,4 +73,12 @@ defmodule Runner do :failed end defp display(_), do: :passed + + defp flush do + receive do + _ -> flush() + after + 0 -> :ok + end + end end diff --git a/lib/tracker.ex b/lib/tracker.ex index f555b4e..f31b211 100644 --- a/lib/tracker.ex +++ b/lib/tracker.ex @@ -1,12 +1,19 @@ defmodule Tracker do alias __MODULE__ - defstruct total: 0, koans: MapSet.new(), visited_modules: MapSet.new() + defstruct total: 0, + koans: MapSet.new(), + visited_modules: MapSet.new(), + on_complete: :noop def start_link do Agent.start_link(fn -> %Tracker{} end, name: __MODULE__) end + def notify_on_complete(pid) do + Agent.update(__MODULE__, fn state -> %{state | on_complete: pid} end) + end + def set_total(modules) do total = modules |> Enum.flat_map(&(&1.all_koans)) @@ -17,6 +24,19 @@ defmodule Tracker do def completed(module, koan) do Agent.update(__MODULE__, &mark_koan_completed(&1, module, koan)) + if complete?() do + Agent.cast(__MODULE__, fn state -> + send(state.on_complete, {self(), :complete}) + state + end) + end + end + + def wait_until_complete() do + pid = Process.whereis(Tracker) + receive do + {^pid, :complete} -> :ok + end end defp mark_koan_completed(state, module, koan) do diff --git a/lib/watcher.ex b/lib/watcher.ex index bbfd390..e377c49 100644 --- a/lib/watcher.ex +++ b/lib/watcher.ex @@ -1,14 +1,13 @@ defmodule Watcher do use ExFSWatch, dirs: ["lib/koans"] + def start_link do + start() + end + def callback(file, events) do if Enum.member?(events, :modified) do file |> normalize |> reload - - if Tracker.complete? do - Display.congratulate - exit(:normal) - end end end