Merge pull request #115 from elixirkoans/module-doc
Adds an @intro attribute to display when running a module
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
13
lib/koans.ex
13
lib/koans.ex
@@ -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
|
||||||
|
|||||||
@@ -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 == ___
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 == ___
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)) == ___
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 == ___
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user