add .formatter.exs + format

This commit is contained in:
Martino Visintin
2018-05-22 21:46:54 +01:00
parent 08c90ce0d8
commit b07df7c780
52 changed files with 332 additions and 264 deletions

View File

@@ -1,6 +1,7 @@
defmodule Blanks do
def replace(ast, replacements) do
replacements = List.wrap(replacements)
ast
|> Macro.prewalk(replacements, &pre/2)
|> elem(0)
@@ -10,6 +11,7 @@ defmodule Blanks do
{args, replacements} = Macro.prewalk(args, replacements, &pre_pin/2)
{put_elem(node, 2, args), replacements}
end
defp pre({:___, _, _}, [first | remainder]), do: {first, remainder}
defp pre(node, acc), do: {node, acc}
@@ -21,6 +23,7 @@ defmodule Blanks do
^unquote(var)
end
end
defp pin(var), do: var
def count(ast) do
@@ -29,16 +32,18 @@ defmodule Blanks do
|> elem(1)
end
defp count({:___, _, _} = node, acc), do: {node, acc+1}
defp count({:___, _, _} = node, acc), do: {node, acc + 1}
defp count(node, acc), do: {node, acc}
def replace_line({:__block__, meta, lines}, replacement_fn) do
replaced_lines = Enum.map(lines, fn(line) ->
replace_line(line, replacement_fn)
end)
replaced_lines =
Enum.map(lines, fn line ->
replace_line(line, replacement_fn)
end)
{:__block__, meta, replaced_lines}
end
def replace_line(line, replacement_fn) do
if count(line) > 0 do
replacement_fn.(line)

View File

@@ -17,33 +17,34 @@ defmodule Display do
end
def handle_cast(:clear_screen, state = %{clear_screen: true}) do
IO.puts(ANSI.clear)
IO.puts(ANSI.home)
IO.puts(ANSI.clear())
IO.puts(ANSI.home())
{:noreply, state}
end
def handle_cast(:clear_screen, state) do
{:noreply, state}
end
def invalid_koan(koan, modules) do
Notifications.invalid_koan(koan, modules)
|> IO.puts
|> IO.puts()
end
def show_failure(failure, module, name) do
format(failure, module, name)
|> IO.puts
|> IO.puts()
end
def show_compile_error(error) do
Failure.show_compile_error(error)
|> IO.puts
|> IO.puts()
end
def congratulate do
Notifications.congratulate
|> IO.puts
Notifications.congratulate()
|> IO.puts()
end
def clear_screen do
@@ -52,9 +53,9 @@ defmodule Display do
defp format(failure, module, name) do
"""
#{Intro.intro(module, Tracker.visited)}
#{Intro.intro(module, Tracker.visited())}
Now meditate upon #{format_module(module)}
#{ProgressBar.progress_bar(Tracker.summarize)}
#{ProgressBar.progress_bar(Tracker.summarize())}
----------------------------------------
#{name}
#{Failure.format_failure(failure)}
@@ -62,6 +63,6 @@ defmodule Display do
end
defp format_module(module) do
Module.split(module) |> List.last
Module.split(module) |> List.last()
end
end

View File

@@ -5,7 +5,7 @@ defmodule Display.Paint do
def yellow(str), do: painter().yellow(str)
defp painter do
case Mix.env do
case Mix.env() do
:test -> Display.Uncoloured
_ -> Display.Colours
end
@@ -15,13 +15,13 @@ end
defmodule Display.Colours do
alias IO.ANSI
def red(str), do: colourize(ANSI.red, str)
def cyan(str), do: colourize(ANSI.cyan, str)
def green(str), do: colourize(ANSI.green, str)
def yellow(str), do: colourize(ANSI.yellow, str)
def red(str), do: colourize(ANSI.red(), str)
def cyan(str), do: colourize(ANSI.cyan(), str)
def green(str), do: colourize(ANSI.green(), str)
def yellow(str), do: colourize(ANSI.yellow(), str)
defp colourize(color, message) do
Enum.join([color, message, ANSI.reset], "")
Enum.join([color, message, ANSI.reset()], "")
end
end

View File

@@ -3,12 +3,17 @@ defmodule Display.Failure do
@no_value :ex_unit_no_meaningful_value
def format_failure(%{error: %ExUnit.AssertionError{expr: @no_value, message: message}, file: file, line: line}) do
def format_failure(%{
error: %ExUnit.AssertionError{expr: @no_value, message: message},
file: file,
line: line
}) do
"""
#{Paint.cyan("Assertion failed in #{file}:#{line}")}
#{Paint.red(message)}
"""
end
def format_failure(%{error: %ExUnit.AssertionError{expr: expr} = error, file: file, line: line}) do
"""
#{Paint.cyan("Assertion failed in #{file}:#{line}")}
@@ -16,6 +21,7 @@ defmodule Display.Failure do
"""
|> format_inequality(error)
end
def format_failure(%{error: error, file: file, line: line}) do
"""
#{Paint.cyan("Error in #{file}:#{line}")}
@@ -26,22 +32,24 @@ defmodule Display.Failure do
defp format_inequality(message, %{left: @no_value, right: @no_value}) do
message
end
defp format_inequality(message, %{left: @no_value, right: match_value}) do
"""
#{message}
value does not match: #{match_value |> inspect |> Paint.yellow}
value does not match: #{match_value |> inspect |> Paint.yellow()}
"""
end
defp format_inequality(message, %{left: left, right: right}) do
"""
#{message}
left: #{left |> inspect |> Paint.yellow}
right: #{right |> inspect |> Paint.yellow}
left: #{left |> inspect |> Paint.yellow()}
right: #{right |> inspect |> Paint.yellow()}
"""
end
defp format_error(error) do
trace = System.stacktrace |> Enum.take(2)
trace = System.stacktrace() |> Enum.take(2)
Paint.red(Exception.format(:error, error, trace))
end

View File

@@ -2,7 +2,7 @@ defmodule Display.Intro do
alias Display.Paint
def intro(module, modules) do
if not module in modules do
if not (module in modules) do
show_intro(module.intro)
else
""
@@ -10,7 +10,7 @@ defmodule Display.Intro do
end
def show_intro(message) do
message <> "\n"
|> Paint.green
(message <> "\n")
|> Paint.green()
end
end

View File

@@ -15,7 +15,7 @@ defmodule Display.Notifications do
|> Enum.map(&Atom.to_string/1)
|> Enum.map(&name/1)
|> Enum.join(", ")
|> Paint.red
|> Paint.red()
end
defp name("Elixir." <> module), do: module

View File

@@ -1,5 +1,4 @@
defmodule Display.ProgressBar do
@progress_bar_length 30
def progress_bar(%{current: current, total: total}) do
@@ -9,11 +8,12 @@ defmodule Display.ProgressBar do
end
defp calculate_progress(current, total) do
round( (current/total) * @progress_bar_length)
round(current / total * @progress_bar_length)
end
defp build_arrow(0), do: ""
defp build_arrow(length) do
String.duplicate("=", length-1) <> ">"
String.duplicate("=", length - 1) <> ">"
end
end

View File

@@ -1,6 +1,6 @@
defmodule Execute do
def run_module(module, callback \\ fn(_result, _module, _koan) -> nil end) do
Enum.reduce_while(module.all_koans, :passed, fn(koan, _) ->
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(module, koan, callback)
@@ -24,7 +24,7 @@ defmodule Execute do
def listen_for_result(module, name) do
receive do
:ok -> :passed
:ok -> :passed
%{error: _} = failure -> {:failed, failure, module, name}
_ -> listen_for_result(module, name)
end
@@ -32,23 +32,25 @@ defmodule Execute do
defp exec(module, name, args, parent) do
result = apply(module, name, args)
send parent, expand(result, module)
send(parent, expand(result, module))
Process.exit(self(), :kill)
end
defp expand(:ok, _), do: :ok
defp expand(error, module) do
{file, line} = System.stacktrace
|> Enum.drop_while(&!in_koan?(&1, module))
|> List.first
{file, line} =
System.stacktrace()
|> Enum.drop_while(&(!in_koan?(&1, module)))
|> List.first()
|> extract_file_and_line
%{error: error, file: file, line: line}
%{error: error, file: file, line: line}
end
defp in_koan?({module, _, _, _}, koan), do: module == koan
defp extract_file_and_line({_, _, _, [file: file, line: line]}) do
{file, line}
{file, line}
end
end

View File

@@ -29,8 +29,10 @@ defmodule Koans do
end
defmacro generate_test_method(_name, 0, _body), do: false
defmacro generate_test_method(name, 1, body) do
single_var = Blanks.replace(body, Macro.var(:answer, __MODULE__))
quote do
def unquote(name)(answer) do
try do
@@ -42,8 +44,11 @@ defmodule Koans do
end
end
end
defmacro generate_test_method(name, number_of_args, body) do
answer_vars = for id <- 1..number_of_args, do: Macro.var(String.to_atom("answer#{id}"), __MODULE__)
answer_vars =
for id <- 1..number_of_args, do: Macro.var(String.to_atom("answer#{id}"), __MODULE__)
multi_var = Blanks.replace(body, answer_vars)
quote do
@@ -60,12 +65,12 @@ defmodule Koans do
defp blank_line_replacement({:assert, _meta, [expr]}) do
code = Macro.escape(expr)
quote do: raise ExUnit.AssertionError, expr: unquote(code)
quote do: raise(ExUnit.AssertionError, expr: unquote(code))
end
defp blank_line_replacement(line) do
code = Macro.escape(line)
quote do: raise ExUnit.AssertionError, expr: unquote(code)
quote do: raise(ExUnit.AssertionError, expr: unquote(code))
end
defmacro __using__(_opts) do
@@ -83,6 +88,7 @@ defmodule Koans do
defmacro __before_compile__(env) do
koans = koans(env)
quote do
def all_koans do
unquote(koans)
@@ -95,6 +101,6 @@ defmodule Koans do
defp koans(env) do
env.module
|> Module.get_attribute(:koans)
|> Enum.reverse
|> Enum.reverse()
end
end

View File

@@ -1,7 +1,7 @@
defmodule Equalities do
use Koans
@intro """
@intro """
Welcome to the Elixir koans.
Let these be your first humble steps towards learning a new language.

View File

@@ -11,12 +11,12 @@ defmodule Atoms do
koan "It is surprising to find out that booleans are atoms" do
assert is_atom(true) == ___
assert is_boolean(false) == ___
assert :true == ___
assert :false == ___
assert true == ___
assert false == ___
end
koan "Like booleans, the nil value is also an atom" do
assert is_atom(nil) == ___
assert :nil == ___
assert nil == ___
end
end

View File

@@ -6,13 +6,11 @@ defmodule Maps do
@person %{
first_name: "Jon",
last_name: "Snow",
age: 27,
age: 27
}
koan "Maps represent structured data, like a person" do
assert @person == %{first_name: ___,
last_name: "Snow",
age: 27 }
assert @person == %{first_name: ___, last_name: "Snow", age: 27}
end
koan "Fetching a value returns a tuple with ok when it exists" do

View File

@@ -49,7 +49,7 @@ defmodule Structs do
koan "Use the update_in macro to modify a nested value" do
airline = %Airline{plane: %Plane{maker: :boeing, passengers: 200}}
assert update_in(airline.plane.passengers, fn(x) -> (x + 2) end) == ___
assert update_in(airline.plane.passengers, fn x -> x + 2 end) == ___
end
koan "Use the put_in macro with atoms to replace a nested value in a non-struct" do

View File

@@ -17,7 +17,7 @@ defmodule Sigils do
end
koan "The lowercase ~s sigil supports string interpolation" do
assert ~s[1 + 1 = #{1+1}] == ___
assert ~s[1 + 1 = #{1 + 1}] == ___
end
koan "The ~S sigil is similar to ~s but doesn't do interpolation" do
@@ -29,7 +29,7 @@ defmodule Sigils do
end
koan "The ~w sigil also allows interpolation" do
assert ~w(Hello 1#{1+1}3) == ___
assert ~w(Hello 1#{1 + 1}3) == ___
end
koan "The ~W sigil behaves to ~w as ~S behaves to ~s" do

View File

@@ -41,7 +41,7 @@ defmodule PatternMatching do
koan "Lists must match exactly" do
assert_raise ___, fn ->
[a, b] = [1,2,3]
[a, b] = [1, 2, 3]
end
end
@@ -71,9 +71,9 @@ defmodule PatternMatching do
koan "And they will only run the code that matches the argument" do
name = fn
("duck") -> "Donald"
("mouse") -> "Mickey"
(_other) -> "I need a name!"
"duck" -> "Donald"
"mouse" -> "Mickey"
_other -> "I need a name!"
end
assert name.("mouse") == ___
@@ -84,10 +84,11 @@ defmodule PatternMatching do
koan "Errors are shaped differently than successful results" do
dog = %{type: "dog"}
result = case Map.fetch(dog, :type) do
{:ok, value} -> value
:error -> "not present"
end
result =
case Map.fetch(dog, :type) do
{:ok, value} -> value
:error -> "not present"
end
assert result == ___
end
@@ -133,10 +134,11 @@ defmodule PatternMatching do
pinned_variable = 1
example = fn
(^pinned_variable) -> "The number One"
(2) -> "The number Two"
(number) -> "The number #{number}"
^pinned_variable -> "The number One"
2 -> "The number Two"
number -> "The number #{number}"
end
assert example.(1) == ___
assert example.(2) == ___
assert example.(3) == ___
@@ -144,17 +146,20 @@ defmodule PatternMatching do
koan "Pinning works anywhere one would match, including 'case'" do
pinned_variable = 1
result = case 1 do
^pinned_variable -> "same"
other -> "different #{other}"
end
result =
case 1 do
^pinned_variable -> "same"
other -> "different #{other}"
end
assert result == ___
end
koan "Trying to rebind a pinned variable will result in an error" do
a = 1
assert_raise MatchError, fn() ->
assert_raise MatchError, fn ->
^a = ___
end
end

View File

@@ -12,6 +12,7 @@ defmodule Functions do
end
def multiply(a, b), do: a * b
koan "Single line functions are cool, but mind the comma and the colon!" do
assert 6 == multiply(2, ___)
end
@@ -41,8 +42,8 @@ defmodule Functions do
assert sum_up(1) == ___
end
def bigger(a,b) when a > b, do: "#{a} is bigger than #{b}"
def bigger(a,b) when a <= b, do: "#{a} is not bigger than #{b}"
def bigger(a, b) when a > b, do: "#{a} is bigger than #{b}"
def bigger(a, b) when a <= b, do: "#{a} is not bigger than #{b}"
koan "Intricate guards are possible, but be mindful of the reader" do
assert bigger(10, 5) == ___
@@ -58,13 +59,13 @@ defmodule Functions do
end
koan "Little anonymous functions are common, and called with a dot" do
multiply = fn (a,b) -> a * b end
assert multiply.(2,3) == ___
multiply = fn a, b -> a * b end
assert multiply.(2, 3) == ___
end
koan "You can even go shorter, by using capture syntax `&()` and positional arguments" do
multiply = &(&1 * &2)
assert multiply.(2,3) == ___
assert multiply.(2, 3) == ___
end
koan "Prefix a string with & to build a simple anonymous greet function" do
@@ -77,7 +78,7 @@ defmodule Functions do
assert three_times.("foo") == ___
end
def times_five_and_then(number, fun), do: fun.(number*5)
def times_five_and_then(number, fun), do: fun.(number * 5)
def square(number), do: number * number
koan "You can pass functions around as arguments. Place an '&' before the name and state the arity" do
@@ -90,10 +91,11 @@ defmodule Functions do
end
koan "The result of a function can be piped into another function as its first argument" do
result = "full-name"
|> String.split("-")
|> Enum.map(&String.capitalize/1)
|> Enum.join(" ")
result =
"full-name"
|> String.split("-")
|> Enum.map(&String.capitalize/1)
|> Enum.join(" ")
assert result == ___
end

View File

@@ -12,12 +12,14 @@ defmodule Enums do
end
def less_than_five?(n), do: n < 5
koan "Elements can have a lot in common" do
assert Enum.all?([1, 2, 3], &less_than_five?/1) == ___
assert Enum.all?([4, 6, 8], &less_than_five?/1) == ___
end
def even?(n), do: rem(n, 2) == 0
koan "Sometimes you just want to know if there are any elements fulfilling a condition" do
assert Enum.any?([1, 2, 3], &even?/1) == ___
assert Enum.any?([1, 3, 5], &even?/1) == ___
@@ -30,6 +32,7 @@ defmodule Enums do
end
def multiply_by_ten(n), do: 10 * n
koan "Map converts each element of a list by running some function with it" do
assert Enum.map([1, 2, 3], &multiply_by_ten/1) == ___
end
@@ -67,6 +70,7 @@ defmodule Enums do
end
def divisible_by_five?(n), do: rem(n, 5) == 0
koan "...but you don't quite find it..." do
assert Enum.find([1, 2, 3], &divisible_by_five?/1) == ___
end
@@ -76,6 +80,6 @@ defmodule Enums do
end
koan "Collapse an entire list of elements down to a single one by repeating a function." do
assert Enum.reduce([1, 2, 3], 0, fn(element, accumulator) -> element + accumulator end) == ___
assert Enum.reduce([1, 2, 3], 0, fn element, accumulator -> element + accumulator end) == ___
end
end

View File

@@ -18,9 +18,11 @@ defmodule Processes do
end
koan "New processes are spawned functions" do
value = spawn(fn -> receive do
end
end)
value =
spawn(fn ->
receive do
end
end)
assert is_pid(value) == ___
end
@@ -39,7 +41,7 @@ defmodule Processes do
end
koan "Processes can send and receive messages" do
send self(), "hola!"
send(self(), "hola!")
receive do
msg -> assert msg == ___
@@ -58,8 +60,8 @@ defmodule Processes do
end
koan "Received messages are queued, first in first out" do
send self(), "hola!"
send self(), "como se llama?"
send(self(), "hola!")
send(self(), "como se llama?")
assert_receive ___
assert_receive ___
@@ -68,20 +70,20 @@ defmodule Processes do
koan "A common pattern is to include the sender in the message, so that it can reply" do
greeter = fn ->
receive do
{:hello, sender} -> send sender, :how_are_you?
{:hello, sender} -> send(sender, :how_are_you?)
end
end
pid = spawn(greeter)
send pid, {:hello, self()}
send(pid, {:hello, self()})
assert_receive ___
end
def yelling_echo_loop do
receive do
{caller, value} ->
send caller, String.upcase(value)
send(caller, String.upcase(value))
yelling_echo_loop()
end
end
@@ -89,18 +91,19 @@ defmodule Processes do
koan "Use tail recursion to receive multiple messages" do
pid = spawn_link(&yelling_echo_loop/0)
send pid, {self(), "o"}
send(pid, {self(), "o"})
assert_receive ___
send pid, {self(), "hai"}
send(pid, {self(), "hai"})
assert_receive ___
end
def state(value) do
receive do
{caller, :get} ->
send caller, value
send(caller, value)
state(value)
{caller, :set, new_value} ->
state(new_value)
end
@@ -108,38 +111,45 @@ defmodule Processes do
koan "Processes can be used to hold state" do
initial_state = "foo"
pid = spawn(fn ->
state(initial_state)
end)
send pid, {self(), :get}
pid =
spawn(fn ->
state(initial_state)
end)
send(pid, {self(), :get})
assert_receive ___
send pid, {self(), :set, "bar"}
send pid, {self(), :get}
send(pid, {self(), :set, "bar"})
send(pid, {self(), :get})
assert_receive ___
end
koan "Waiting for a message can get boring" do
parent = self()
spawn(fn -> receive do
after
5 -> send parent, {:waited_too_long, "I am impatient"}
end
end)
spawn(fn ->
receive do
after
5 -> send(parent, {:waited_too_long, "I am impatient"})
end
end)
assert_receive ___
end
koan "Trapping will allow you to react to someone terminating the process" do
parent = self()
pid = spawn(fn ->
Process.flag(:trap_exit, true)
send parent, :ready
receive do
{:EXIT, _pid, reason} -> send parent, {:exited, reason}
end
end)
pid =
spawn(fn ->
Process.flag(:trap_exit, true)
send(parent, :ready)
receive do
{:EXIT, _pid, reason} -> send(parent, {:exited, reason})
end
end)
receive do
:ready -> true

View File

@@ -10,23 +10,26 @@ defmodule Tasks do
end
koan "If you don't need a result, use start_link/1" do
{result, _pid} = Task.start_link(fn -> 1+1 end)
{result, _pid} = Task.start_link(fn -> 1 + 1 end)
assert result == ___
end
koan "Yield returns nil if the task isn't done yet" do
handle = Task.async(fn ->
:timer.sleep(100)
3 * 3
end)
handle =
Task.async(fn ->
:timer.sleep(100)
3 * 3
end)
assert Task.yield(handle, 10) == ___
end
koan "Tasks can be aborted with shutdown" do
handle = Task.async(fn ->
:timer.sleep(100)
3 * 3
end)
handle =
Task.async(fn ->
:timer.sleep(100)
3 * 3
end)
%Task{pid: pid} = handle
Task.shutdown(handle)
@@ -41,10 +44,11 @@ defmodule Tasks do
end
koan "You can yield to multiple tasks at once and extract the results" do
squares = [1, 2, 3, 4]
|> Enum.map(fn(number) -> Task.async(fn -> number * number end) end)
|> Task.yield_many(100)
|> Enum.map(fn({_task,{:ok, result}}) -> result end)
squares =
[1, 2, 3, 4]
|> Enum.map(fn number -> Task.async(fn -> number * number end) end)
|> Task.yield_many(100)
|> Enum.map(fn {_task, {:ok, result}} -> result end)
assert squares == ___
end

View File

@@ -5,36 +5,38 @@ defmodule Agents do
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)) == ___
assert Agent.get(pid, & &1) == ___
end
koan "Agents may also be named so that you don't have to keep the pid around" do
Agent.start_link(fn -> "Why hello" end, name: AgentSmith)
assert Agent.get(AgentSmith, &(&1)) == ___
assert Agent.get(AgentSmith, & &1) == ___
end
koan "Update to update the state" do
Agent.start_link(fn() -> "Hi there" end, name: __MODULE__)
Agent.start_link(fn -> "Hi there" end, name: __MODULE__)
Agent.update(__MODULE__, fn(old) ->
Agent.update(__MODULE__, fn old ->
String.upcase(old)
end)
assert Agent.get(__MODULE__, &(&1)) == ___
assert Agent.get(__MODULE__, & &1) == ___
end
koan "Use get_and_update when you need to read and change a value in one go" do
Agent.start_link(fn() -> ["Milk"] end, name: __MODULE__)
Agent.start_link(fn -> ["Milk"] end, name: __MODULE__)
old_list = Agent.get_and_update(__MODULE__, fn(old) ->
{old, ["Bread" | old]}
end)
old_list =
Agent.get_and_update(__MODULE__, fn old ->
{old, ["Bread" | old]}
end)
assert old_list == ___
assert Agent.get(__MODULE__, &(&1)) == ___
assert Agent.get(__MODULE__, & &1) == ___
end
koan "Somebody has to switch off the light at the end of the day" do
{:ok, pid} = Agent.start_link(fn() -> ["Milk"] end, name: __MODULE__)
{:ok, pid} = Agent.start_link(fn -> ["Milk"] end, name: __MODULE__)
Agent.stop(__MODULE__)

View File

@@ -46,7 +46,7 @@ defmodule GenServers do
# GenServer implementation
def handle_call(:get_password, _from, current_password) do
{:reply, current_password, current_password}
{:reply, current_password, current_password}
end
def handle_call(:get_manufacturer, _from, current_state) do
@@ -73,6 +73,7 @@ defmodule GenServers do
case password do
password when password === current_password ->
{:reply, {:ok, "Laptop unlocked!"}, current_password}
_ ->
{:reply, {:error, "Incorrect password!"}, current_password}
end
@@ -82,6 +83,7 @@ defmodule GenServers do
case old_password do
old_password when old_password == current_password ->
{:noreply, new_password}
_ ->
{:noreply, current_password}
end
@@ -147,7 +149,7 @@ defmodule GenServers do
{_, response} = Laptop.unlock("EL!73")
assert response == ___
{_, response} = Laptop.owner_name
{_, response} = Laptop.owner_name()
assert response == ___
end
end

View File

@@ -3,7 +3,7 @@ defmodule Protocols do
@intro "Want to follow the rules? Adhere to the protocol!"
defprotocol School, do: def enroll(person)
defprotocol(School, do: def(enroll(person)))
defimpl School, for: Any do
def enroll(_) do
@@ -16,9 +16,9 @@ defmodule Protocols do
defstruct name: ""
end
defmodule Musician, do: defstruct name: "", instrument: ""
defmodule Dancer, do: defstruct name: "", dance_style: ""
defmodule Baker, do: defstruct name: ""
defmodule(Musician, do: defstruct(name: "", instrument: ""))
defmodule(Dancer, do: defstruct(name: "", dance_style: ""))
defmodule(Baker, do: defstruct(name: ""))
defimpl School, for: Musician do
def enroll(musician) do

View File

@@ -9,10 +9,11 @@ defmodule Mix.Tasks.Meditate do
{parsed, _, _} = OptionParser.parse(args)
modules = parsed
|> initial_module
|> ok?
|> Runner.modules_to_run
modules =
parsed
|> initial_module
|> ok?
|> Runner.modules_to_run()
Tracker.set_total(modules)
Tracker.notify_on_complete(self())
@@ -26,7 +27,7 @@ defmodule Mix.Tasks.Meditate do
defp initial_module(parsed) do
name = Keyword.get(parsed, :koan, "Equalities")
String.to_atom("Elixir."<> name)
String.to_atom("Elixir." <> name)
end
defp set_clear_screen(parsed) do
@@ -39,7 +40,7 @@ defmodule Mix.Tasks.Meditate do
if Runner.koan?(koan) do
koan
else
Display.invalid_koan(koan, Runner.modules)
Display.invalid_koan(koan, Runner.modules())
exit(:normal)
end
end

View File

@@ -13,7 +13,8 @@ defmodule Runner do
modules
|> Stream.map(&(&1.module_info |> get_in([:compile, :source])))
|> Stream.map(&to_string/1) # Paths are charlists
# 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)
@@ -74,6 +75,7 @@ defmodule Runner do
Display.show_failure(error, module, name)
:failed
end
defp display(_), do: :passed
defp flush do

View File

@@ -2,9 +2,9 @@ defmodule Tracker do
alias __MODULE__
defstruct total: 0,
koans: MapSet.new(),
visited_modules: MapSet.new(),
on_complete: :noop
koans: MapSet.new(),
visited_modules: MapSet.new(),
on_complete: :noop
def start_link do
Agent.start_link(fn -> %Tracker{} end, name: __MODULE__)
@@ -15,15 +15,17 @@ defmodule Tracker do
end
def set_total(modules) do
total = modules
|> Enum.flat_map(&(&1.all_koans))
|> Enum.count
total =
modules
|> Enum.flat_map(& &1.all_koans)
|> Enum.count()
Agent.update(__MODULE__, fn _ -> %Tracker{total: total} end)
end
def completed(module, koan) do
Agent.update(__MODULE__, &mark_koan_completed(&1, module, koan))
if complete?() do
Agent.cast(__MODULE__, fn state ->
send(state.on_complete, {self(), :complete})
@@ -34,14 +36,18 @@ defmodule Tracker do
def wait_until_complete() do
pid = Process.whereis(Tracker)
receive do
{^pid, :complete} -> :ok
end
end
defp mark_koan_completed(state, module, koan) do
%{state | koans: MapSet.put(state.koans, koan),
visited_modules: MapSet.put(state.visited_modules, module)}
%{
state
| koans: MapSet.put(state.koans, koan),
visited_modules: MapSet.put(state.visited_modules, module)
}
end
def visited do
@@ -54,7 +60,7 @@ defmodule Tracker do
end
def summarize do
state = Agent.get(__MODULE__, &(&1))
state = Agent.get(__MODULE__, & &1)
%{
total: state.total,

View File

@@ -2,7 +2,7 @@ defmodule Watcher do
use GenServer
def start_link() do
GenServer.start_link(__MODULE__, [dirs: ["lib/koans"]])
GenServer.start_link(__MODULE__, dirs: ["lib/koans"])
end
def init(args) do
@@ -11,10 +11,11 @@ defmodule Watcher do
{:ok, %{watcher_pid: watcher_pid}}
end
def handle_info({:file_event, watcher_pid, {path, events}}, %{watcher_pid: watcher_pid}=state) do
def handle_info({:file_event, watcher_pid, {path, events}}, %{watcher_pid: watcher_pid} = state) do
if Enum.member?(events, :modified) do
path |> normalize |> reload
end
{:noreply, state}
end
@@ -22,11 +23,11 @@ defmodule Watcher do
if Path.extname(file) == ".ex" do
try do
file
|> Code.load_file
|> Enum.map(&(elem(&1, 0)))
|> Code.load_file()
|> Enum.map(&elem(&1, 0))
|> Enum.find(&Runner.koan?/1)
|> Runner.modules_to_run
|> Runner.run
|> Runner.modules_to_run()
|> Runner.run()
rescue
e -> Display.show_compile_error(e)
end