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
"""
#{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

View File

@@ -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

View File

@@ -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

View File

@@ -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 == ___

View File

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

View File

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

View File

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

View File

@@ -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

View File

@@ -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"]

View File

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

View File

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

View File

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

View File

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

View File

@@ -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

View File

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

View File

@@ -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()

View File

@@ -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)) == ___

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

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

View File

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

View File

@@ -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