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
|
||||
"""
|
||||
#{intro(module, Tracker.visited)}
|
||||
Now meditate upon #{format_module(module)}
|
||||
#{progress_bar(Tracker.summarize)}
|
||||
----------------------------------------
|
||||
@@ -34,6 +35,19 @@ defmodule Display do
|
||||
"""
|
||||
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
|
||||
arrow = caluculate_progress(current, total) |> build_arrow
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
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, _) ->
|
||||
module
|
||||
|> run_koan(koan)
|
||||
|> hook(koan, callback)
|
||||
|> hook(module, koan, callback)
|
||||
|> continue?
|
||||
end)
|
||||
end
|
||||
|
||||
defp hook(result,koan, callback) do
|
||||
callback.(result, koan)
|
||||
defp hook(result, module, koan, callback) do
|
||||
callback.(result, module, koan)
|
||||
result
|
||||
end
|
||||
|
||||
|
||||
13
lib/koans.ex
13
lib/koans.ex
@@ -82,11 +82,22 @@ defmodule Koans do
|
||||
end
|
||||
|
||||
defmacro __before_compile__(env) do
|
||||
koans = Module.get_attribute(env.module, :koans) |> Enum.reverse
|
||||
koans = koans(env)
|
||||
quote do
|
||||
def all_koans do
|
||||
unquote(koans)
|
||||
end
|
||||
|
||||
def intro, do: @intro
|
||||
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
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
defmodule Equalities do
|
||||
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.
|
||||
koan "We shall contemplate truth by testing reality, via equality" do
|
||||
assert true == ___
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule Strings do
|
||||
use Koans
|
||||
|
||||
@intro "Strings"
|
||||
|
||||
koan "Strings are there to represent text" do
|
||||
assert "hello" == ___
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule Atoms do
|
||||
use Koans
|
||||
|
||||
@intro "Atoms"
|
||||
|
||||
koan "Atoms are sort of like strings" do
|
||||
adam = :human
|
||||
assert adam == ___
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule Tuples do
|
||||
use Koans
|
||||
|
||||
@intro "Tuples"
|
||||
|
||||
koan "Tuples can contain different things" do
|
||||
assert {:a, 1, "hi"} == ___
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule Lists do
|
||||
use Koans
|
||||
|
||||
@intro "Lists"
|
||||
|
||||
koan "We can see what is ahead" do
|
||||
assert List.first([1, 2, 3]) == ___
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule KeywordLists do
|
||||
use Koans
|
||||
|
||||
@intro "KeywordLists"
|
||||
|
||||
koan "Like maps, keyword lists are key-value pairs" do
|
||||
kw_list = [foo: "bar"]
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule Maps do
|
||||
use Koans
|
||||
|
||||
@intro "Maps"
|
||||
|
||||
@person %{
|
||||
first_name: "Jon",
|
||||
last_name: "Snow",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule Structs do
|
||||
use Koans
|
||||
|
||||
@intro "Structs"
|
||||
|
||||
defmodule Person do
|
||||
defstruct [:name, :age]
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule PatternMatching do
|
||||
use Koans
|
||||
|
||||
@intro "PatternMatching"
|
||||
|
||||
koan "One matches one" do
|
||||
assert match?(1, ___)
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule Functions do
|
||||
use Koans
|
||||
|
||||
@intro "Functions"
|
||||
|
||||
def greet(name) do
|
||||
"Hello, #{name}!"
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule Enums do
|
||||
use Koans
|
||||
|
||||
@intro "Enums"
|
||||
|
||||
koan "Knowing how many elements are in a list is important for book-keeping" do
|
||||
assert Enum.count([1, 2, 3]) == ___
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule Processes do
|
||||
use Koans
|
||||
|
||||
@intro "Processes"
|
||||
|
||||
koan "You are a process" do
|
||||
assert Process.alive?(self) == ___
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule Tasks do
|
||||
use Koans
|
||||
|
||||
@intro "Tasks"
|
||||
|
||||
koan "Tasks can be used for asynchronous computations with results" do
|
||||
task = Task.async(fn -> 3 * 3 end)
|
||||
do_other_stuff()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule Agents do
|
||||
use Koans
|
||||
|
||||
@intro "Agents"
|
||||
|
||||
koan "Agents maintain state, so you can ask them about it" do
|
||||
{:ok, pid} = Agent.start_link(fn -> "Hi there" end)
|
||||
assert Agent.get(pid, &(&1)) == ___
|
||||
|
||||
@@ -17,6 +17,7 @@ defmodule Runner do
|
||||
]
|
||||
|
||||
def koan?(koan), do: Enum.member?(@modules, koan)
|
||||
|
||||
def modules, do: @modules
|
||||
|
||||
def modules_to_run, do: Options.initial_koan |> modules_to_run
|
||||
@@ -31,16 +32,17 @@ defmodule Runner do
|
||||
|
||||
def run_module(module) do
|
||||
module
|
||||
|> Execute.run_module(&track/2)
|
||||
|> Execute.run_module(&track/3)
|
||||
|> display
|
||||
end
|
||||
|
||||
defp track(:passed, koan), do: Tracker.completed(koan)
|
||||
defp track(_, _), do: nil
|
||||
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
|
||||
|
||||
end
|
||||
|
||||
@@ -4,27 +4,38 @@ defmodule Tracker do
|
||||
|> Enum.flat_map(&(&1.all_koans))
|
||||
|> 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
|
||||
end
|
||||
|
||||
def get do
|
||||
Agent.get(__MODULE__, &(&1))
|
||||
end
|
||||
defp get(), do: Agent.get(__MODULE__, &(&1))
|
||||
|
||||
def completed(koan) do
|
||||
Agent.update(__MODULE__, fn({total, completed}) ->
|
||||
{total, MapSet.put(completed, koan)}
|
||||
def completed(module, koan) do
|
||||
Agent.update(__MODULE__, fn(%{koans: completed, visited_modules: modules} = all) ->
|
||||
%{ all | koans: MapSet.put(completed, koan),
|
||||
visited_modules: MapSet.put(modules, module)}
|
||||
end)
|
||||
end
|
||||
|
||||
def visited do
|
||||
summarize[:visited_modules]
|
||||
end
|
||||
|
||||
def complete? do
|
||||
{total, completed} = get
|
||||
total == Enum.count(completed)
|
||||
%{total: total, current: completed} = summarize
|
||||
total == completed
|
||||
end
|
||||
|
||||
def summarize, do: get |> summarize
|
||||
defp summarize({total, completed}) do
|
||||
%{total: total, current: MapSet.size(completed)}
|
||||
defp summarize(%{total: total,
|
||||
koans: completed,
|
||||
visited_modules: modules}) do
|
||||
%{
|
||||
total: total,
|
||||
current: MapSet.size(completed),
|
||||
visited_modules: MapSet.to_list(modules)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,6 +8,10 @@ defmodule ExecuteTest do
|
||||
test "stops at the first failing koan" do
|
||||
{:failed, %{file: file, line: line}, SampleKoan, _name} = Execute.run_module(SampleKoan)
|
||||
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
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule PassingKoan do
|
||||
use Koans
|
||||
|
||||
@intro "something"
|
||||
|
||||
koan "Hi there" do
|
||||
assert 1 == 1
|
||||
end
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
defmodule SampleKoan do
|
||||
use Koans
|
||||
|
||||
@intro """
|
||||
There is something
|
||||
"""
|
||||
|
||||
koan "Thinking more than once" do
|
||||
assert 3 == ___
|
||||
assert 4 == ___
|
||||
|
||||
@@ -5,19 +5,24 @@ defmodule TrackerTest do
|
||||
|
||||
test "can start" do
|
||||
Tracker.start(@sample_modules)
|
||||
assert Tracker.summarize == %{total: 2, current: 0}
|
||||
assert Tracker.summarize == %{total: 2, current: 0, visited_modules: []}
|
||||
end
|
||||
|
||||
test "can be notified of completed koans" do
|
||||
Tracker.start(@sample_modules)
|
||||
Tracker.completed(:"Hi there")
|
||||
assert Tracker.summarize == %{total: 2, current: 1}
|
||||
Tracker.completed(SampleKoan, :"Hi there")
|
||||
assert Tracker.summarize == %{total: 2, current: 1, visited_modules: [SampleKoan]}
|
||||
end
|
||||
|
||||
test "multiple comletions of the same koan count only once" do
|
||||
Tracker.start(@sample_modules)
|
||||
Tracker.completed(:"Hi there")
|
||||
Tracker.completed(:"Hi there")
|
||||
assert Tracker.summarize == %{total: 2, current: 1}
|
||||
Tracker.completed(SampleKoan, :"Hi there")
|
||||
Tracker.completed(SampleKoan, :"Hi there")
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user