defmodule Runner do @moduledoc false use GenServer def koan?(koan) do case Code.ensure_loaded(koan) do {:module, _} -> Keyword.has_key?(koan.__info__(:functions), :all_koans) _ -> false end end def modules do {:ok, modules} = :application.get_key(:elixir_koans, :modules) modules |> Stream.map(&(&1.module_info |> get_in([:compile, :source]))) # Paths are charlists |> Stream.map(&to_string/1) |> Stream.zip(modules) |> Stream.filter(fn {_path, mod} -> koan?(mod) end) |> Stream.map(fn {path, mod} -> {path_to_number(path), mod} end) |> Enum.sort_by(fn {number, _mod} -> number end) |> Enum.map(fn {_number, mod} -> mod end) end @koan_path_pattern ~r/lib\/koans\/(\d+)_\w+.ex$/ def koan_path_pattern, do: @koan_path_pattern def path_to_number(path) do [_path, number] = Regex.run(@koan_path_pattern, path) String.to_integer(number) end def modules_to_run(start_module), do: Enum.drop_while(modules(), &(&1 != start_module)) def init(args) do {:ok, args} end def start_link do GenServer.start_link(__MODULE__, [], name: __MODULE__) end 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 end defp track(:passed, module, koan), do: Tracker.completed(module, koan) defp track(_, _, _), do: nil defp display({:failed, error, module, name}) do Display.show_failure(error, module, name) :failed end defp display(_), do: :passed defp flush do receive do _ -> flush() after 0 -> :ok end end end