Merge pull request #115 from elixirkoans/module-doc

Adds an @intro attribute to display when running a module
This commit is contained in:
Felipe Seré
2016-05-13 22:41:25 +02:00
23 changed files with 112 additions and 26 deletions

View File

@@ -26,6 +26,7 @@ defmodule Display do
def format(failure, module, name) do def format(failure, module, name) do
""" """
#{intro(module, Tracker.visited)}
Now meditate upon #{format_module(module)} Now meditate upon #{format_module(module)}
#{progress_bar(Tracker.summarize)} #{progress_bar(Tracker.summarize)}
---------------------------------------- ----------------------------------------
@@ -34,6 +35,19 @@ defmodule Display do
""" """
end end
defp intro(module, modules) do
if not module in modules do
show_intro(module.intro)
end
end
defp show_intro(""), do: nil
defp show_intro(message) do
message <> "\n"
|> Colours.green
|> IO.puts
end
def progress_bar(%{current: current, total: total}) do def progress_bar(%{current: current, total: total}) do
arrow = caluculate_progress(current, total) |> build_arrow arrow = caluculate_progress(current, total) |> build_arrow

View File

@@ -1,15 +1,15 @@
defmodule Execute do defmodule Execute do
def run_module(module, callback \\ fn(_result, _koan) -> nil end) do def run_module(module, callback \\ fn(_result, _module, _koan) -> nil end) do
Enum.reduce_while(module.all_koans, :passed, fn(koan, _) -> Enum.reduce_while(module.all_koans, :passed, fn(koan, _) ->
module module
|> run_koan(koan) |> run_koan(koan)
|> hook(koan, callback) |> hook(module, koan, callback)
|> continue? |> continue?
end) end)
end end
defp hook(result,koan, callback) do defp hook(result, module, koan, callback) do
callback.(result, koan) callback.(result, module, koan)
result result
end end

View File

@@ -82,11 +82,22 @@ defmodule Koans do
end end
defmacro __before_compile__(env) do defmacro __before_compile__(env) do
koans = Module.get_attribute(env.module, :koans) |> Enum.reverse koans = koans(env)
quote do quote do
def all_koans do def all_koans do
unquote(koans) unquote(koans)
end end
def intro, do: @intro
end end
end end
defp koans(env) do
env.module
|> Module.get_attribute(:koans)
|> Enum.reverse
end
defp default(nil, n), do: n
defp default(n, _), do: n
end end

View File

@@ -1,6 +1,13 @@
defmodule Equalities do defmodule Equalities do
use Koans use Koans
@intro """
Welcome to the Elixir koans.
Let these be your first humble steps towards learning a new language.
The path laid in front of you is one of many.
"""
# Replace ___ with the answer to make the koan pass. # Replace ___ with the answer to make the koan pass.
koan "We shall contemplate truth by testing reality, via equality" do koan "We shall contemplate truth by testing reality, via equality" do
assert true == ___ assert true == ___

View File

@@ -1,6 +1,8 @@
defmodule Strings do defmodule Strings do
use Koans use Koans
@intro "Strings"
koan "Strings are there to represent text" do koan "Strings are there to represent text" do
assert "hello" == ___ assert "hello" == ___
end end

View File

@@ -1,6 +1,8 @@
defmodule Atoms do defmodule Atoms do
use Koans use Koans
@intro "Atoms"
koan "Atoms are sort of like strings" do koan "Atoms are sort of like strings" do
adam = :human adam = :human
assert adam == ___ assert adam == ___

View File

@@ -1,6 +1,8 @@
defmodule Tuples do defmodule Tuples do
use Koans use Koans
@intro "Tuples"
koan "Tuples can contain different things" do koan "Tuples can contain different things" do
assert {:a, 1, "hi"} == ___ assert {:a, 1, "hi"} == ___
end end

View File

@@ -1,6 +1,8 @@
defmodule Lists do defmodule Lists do
use Koans use Koans
@intro "Lists"
koan "We can see what is ahead" do koan "We can see what is ahead" do
assert List.first([1, 2, 3]) == ___ assert List.first([1, 2, 3]) == ___
end end

View File

@@ -1,6 +1,8 @@
defmodule KeywordLists do defmodule KeywordLists do
use Koans use Koans
@intro "KeywordLists"
koan "Like maps, keyword lists are key-value pairs" do koan "Like maps, keyword lists are key-value pairs" do
kw_list = [foo: "bar"] kw_list = [foo: "bar"]

View File

@@ -1,6 +1,8 @@
defmodule Maps do defmodule Maps do
use Koans use Koans
@intro "Maps"
@person %{ @person %{
first_name: "Jon", first_name: "Jon",
last_name: "Snow", last_name: "Snow",

View File

@@ -1,6 +1,8 @@
defmodule Structs do defmodule Structs do
use Koans use Koans
@intro "Structs"
defmodule Person do defmodule Person do
defstruct [:name, :age] defstruct [:name, :age]
end end

View File

@@ -1,6 +1,8 @@
defmodule PatternMatching do defmodule PatternMatching do
use Koans use Koans
@intro "PatternMatching"
koan "One matches one" do koan "One matches one" do
assert match?(1, ___) assert match?(1, ___)
end end

View File

@@ -1,6 +1,8 @@
defmodule Functions do defmodule Functions do
use Koans use Koans
@intro "Functions"
def greet(name) do def greet(name) do
"Hello, #{name}!" "Hello, #{name}!"
end end

View File

@@ -1,6 +1,8 @@
defmodule Enums do defmodule Enums do
use Koans use Koans
@intro "Enums"
koan "Knowing how many elements are in a list is important for book-keeping" do koan "Knowing how many elements are in a list is important for book-keeping" do
assert Enum.count([1, 2, 3]) == ___ assert Enum.count([1, 2, 3]) == ___
end end

View File

@@ -1,6 +1,8 @@
defmodule Processes do defmodule Processes do
use Koans use Koans
@intro "Processes"
koan "You are a process" do koan "You are a process" do
assert Process.alive?(self) == ___ assert Process.alive?(self) == ___
end end

View File

@@ -1,6 +1,8 @@
defmodule Tasks do defmodule Tasks do
use Koans use Koans
@intro "Tasks"
koan "Tasks can be used for asynchronous computations with results" do koan "Tasks can be used for asynchronous computations with results" do
task = Task.async(fn -> 3 * 3 end) task = Task.async(fn -> 3 * 3 end)
do_other_stuff() do_other_stuff()

View File

@@ -1,6 +1,8 @@
defmodule Agents do defmodule Agents do
use Koans use Koans
@intro "Agents"
koan "Agents maintain state, so you can ask them about it" do koan "Agents maintain state, so you can ask them about it" do
{:ok, pid} = Agent.start_link(fn -> "Hi there" end) {:ok, pid} = Agent.start_link(fn -> "Hi there" end)
assert Agent.get(pid, &(&1)) == ___ assert Agent.get(pid, &(&1)) == ___

View File

@@ -17,6 +17,7 @@ defmodule Runner do
] ]
def koan?(koan), do: Enum.member?(@modules, koan) def koan?(koan), do: Enum.member?(@modules, koan)
def modules, do: @modules def modules, do: @modules
def modules_to_run, do: Options.initial_koan |> modules_to_run def modules_to_run, do: Options.initial_koan |> modules_to_run
@@ -31,16 +32,17 @@ defmodule Runner do
def run_module(module) do def run_module(module) do
module module
|> Execute.run_module(&track/2) |> Execute.run_module(&track/3)
|> display |> display
end end
defp track(:passed, koan), do: Tracker.completed(koan) defp track(:passed, module, koan), do: Tracker.completed(module, koan)
defp track(_, _), do: nil defp track(_, _, _), do: nil
defp display({:failed, error, module, name}) do defp display({:failed, error, module, name}) do
Display.show_failure(error, module, name) Display.show_failure(error, module, name)
:failed :failed
end end
defp display(_), do: :passed defp display(_), do: :passed
end end

View File

@@ -4,27 +4,38 @@ defmodule Tracker do
|> Enum.flat_map(&(&1.all_koans)) |> Enum.flat_map(&(&1.all_koans))
|> Enum.count |> Enum.count
Agent.start_link(fn -> {total, MapSet.new()} end, name: __MODULE__) Agent.start_link(fn -> %{total: total,
koans: MapSet.new(),
visited_modules: MapSet.new()} end, name: __MODULE__)
modules modules
end end
def get do defp get(), do: Agent.get(__MODULE__, &(&1))
Agent.get(__MODULE__, &(&1))
end
def completed(koan) do def completed(module, koan) do
Agent.update(__MODULE__, fn({total, completed}) -> Agent.update(__MODULE__, fn(%{koans: completed, visited_modules: modules} = all) ->
{total, MapSet.put(completed, koan)} %{ all | koans: MapSet.put(completed, koan),
visited_modules: MapSet.put(modules, module)}
end) end)
end end
def visited do
summarize[:visited_modules]
end
def complete? do def complete? do
{total, completed} = get %{total: total, current: completed} = summarize
total == Enum.count(completed) total == completed
end end
def summarize, do: get |> summarize def summarize, do: get |> summarize
defp summarize({total, completed}) do defp summarize(%{total: total,
%{total: total, current: MapSet.size(completed)} koans: completed,
visited_modules: modules}) do
%{
total: total,
current: MapSet.size(completed),
visited_modules: MapSet.to_list(modules)
}
end end
end end

View File

@@ -8,6 +8,10 @@ defmodule ExecuteTest do
test "stops at the first failing koan" do test "stops at the first failing koan" do
{:failed, %{file: file, line: line}, SampleKoan, _name} = Execute.run_module(SampleKoan) {:failed, %{file: file, line: line}, SampleKoan, _name} = Execute.run_module(SampleKoan)
assert file == 'test/support/sample_koan.ex' assert file == 'test/support/sample_koan.ex'
assert line == 4 assert line == 8
end
test "can access intro" do
assert SampleKoan.intro == "There is something\n"
end end
end end

View File

@@ -1,6 +1,8 @@
defmodule PassingKoan do defmodule PassingKoan do
use Koans use Koans
@intro "something"
koan "Hi there" do koan "Hi there" do
assert 1 == 1 assert 1 == 1
end end

View File

@@ -1,6 +1,10 @@
defmodule SampleKoan do defmodule SampleKoan do
use Koans use Koans
@intro """
There is something
"""
koan "Thinking more than once" do koan "Thinking more than once" do
assert 3 == ___ assert 3 == ___
assert 4 == ___ assert 4 == ___

View File

@@ -5,19 +5,24 @@ defmodule TrackerTest do
test "can start" do test "can start" do
Tracker.start(@sample_modules) Tracker.start(@sample_modules)
assert Tracker.summarize == %{total: 2, current: 0} assert Tracker.summarize == %{total: 2, current: 0, visited_modules: []}
end end
test "can be notified of completed koans" do test "can be notified of completed koans" do
Tracker.start(@sample_modules) Tracker.start(@sample_modules)
Tracker.completed(:"Hi there") Tracker.completed(SampleKoan, :"Hi there")
assert Tracker.summarize == %{total: 2, current: 1} assert Tracker.summarize == %{total: 2, current: 1, visited_modules: [SampleKoan]}
end end
test "multiple comletions of the same koan count only once" do test "multiple comletions of the same koan count only once" do
Tracker.start(@sample_modules) Tracker.start(@sample_modules)
Tracker.completed(:"Hi there") Tracker.completed(SampleKoan, :"Hi there")
Tracker.completed(:"Hi there") Tracker.completed(SampleKoan, :"Hi there")
assert Tracker.summarize == %{total: 2, current: 1} assert Tracker.summarize == %{total: 2, current: 1, visited_modules: [SampleKoan]}
end
test "knows when koans are not complete" do
Tracker.start(@sample_modules)
refute Tracker.complete?
end end
end end